MVP Pattern
MVC模式(Model-View-Controller
):MVC模式在開發UI中是一種非常受歡迎的設計模式。
- MVC主要想法為:將程式劃分為模型(
Model
)、視圖(View
)和控制器(Controller
)Model
:主要負責管理資料邏輯。Model
拿到資料後將資料交給View
。- 在此可以直接與資料庫、API 或其他資料來源交互。
- 在此不執行遊戲邏輯或是執行運算。
View
:負責顯示資料。- 它從
Model
中獲取資料,並將這些資料呈現給使用者。View
僅關注如何顯示資料。
- 它從
Controller
:充當Model
和View
之間的溝通的橋樑。- 它接收使用者的輸入,呼叫
Model
來處理這些輸入,然後選擇適當的View
來顯示處理結果。 - 可以把它想像為大腦,它處理遊戲資料並在執行期間計算資料是如何改變的。
- 它接收使用者的輸入,呼叫
MVC
模式符合單一職責原則,每個部分都只負責自己的部分。- 在Unity中,
UI Toolkit
或是Unity UI
很自然地負責擔任View
- 缺點
MVC
模式中三者相互依賴,一但更新了其中一個,另外兩個也須跟著修改。Controller
的程式碼會隨著功能的添加越來越臃腫。
graph TD A[ User ] -->| Interacts with | B[ View ] B -->| Sends user input to | C[ Controller ] C -->| Updates | D[ Model ] D -->| Notifies changes to | B[ View ] B -->| Displays data from | D[ Model ]
MVP模式(Model-View-Presenter
):MVP是MVC的一種變體
- MVP將Controller改爲
Presenter
,並改變了通信方向。當Model
拿到資料之後,不直接給View
更新,而是交給Presenter
,之後Presenter
再把資料交給View
,View
再更新畫面。View
:只負責收到使用者回饋,之後呼叫Presenter
拿取資料,並在接收到資料時,更新畫面。Model
:被動的接收到Presenter
命令,拿取資料,並回傳給Presenter
。Presenter
:Model
和View
之間的的橋樑,與View
和Model
溝通。
- 從三者相互依賴變成都只依賴
Presenter
- M <=> P <=> V 之間雙向通信但
View
與Model
不通信
- M <=> P <=> V 之間雙向通信但
Presenter
的程式碼會隨著功能的添加越來越臃腫。
graph TD A[ User ] -->| Interacts with | B[ View ] B -->| Sends user input to | C[ Presenter ] C -->| Updates | D[ Model ] D -->| Notifies changes to | C[ Presenter ] C -->| Updates | B[ View ] B -->| Displays data from | C[ Presenter ]
使用MVP模式製作生命條UI:Health類
- 在此Health類的身份是
Model
,保存真正的生命資料 - 含有一個
HealthChanged
event,每當有更改生命值的動作時,都會呼叫這個event。 - Health類只負責增加,減少生命值,符合符合單一職責原則
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public class Health : MonoBehaviour
{
public event Action HealthChanged;
private const int minHealth = 0;
private const int maxHealth = 100;
private int currentHealth;
public int CurrentHealth
{
get => currentHealth;
set => currentHealth = value;
}
public int MinHealth => minHealth;
public int MaxHealth => maxHealth;
public void Increment(int amount)
{
currentHealth += amount;
currentHealth = Mathf.Clamp(currentHealth, minHealth, maxHealth);
UpdateHealth();
}
public void Decrement(int amount)
{
currentHealth -= amount;
currentHealth = Mathf.Clamp(currentHealth, minHealth, maxHealth);
UpdateHealth();
}
public void Restore()
{
currentHealth = maxHealth;
UpdateHealth();
}
private void UpdateHealth()
{
HealthChanged?.Invoke();
}
}
HealthPresenter類
- HealthPresenter類含有
Health
類的依賴,用來操控Health
- 其他物件不會直接操控
Health
類,而是透過HealthPresenter類暴露的Damage
與Heal
以及Reset
來操控1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55public class HealthPresenter : MonoBehaviour
{
// 在MVP中的Model
[private Health health; ]
// 在MVP中的View
[private Slider healthSlider; ]
private void Start()
{
if (health != null)
{
health.HealthChanged += OnHealthChanged;
}
UpdateView();
}
private void OnDestroy()
{
if (health != null)
{
health.HealthChanged -= OnHealthChanged;
}
}
public void Damage(int amount)
{
health?.Decrement(amount);
}
public void Heal(int amount)
{
health?.Increment(amount);
}
public void Reset()
{
health?.Restore();
}
public void UpdateView()
{
if (health == null) return;
if (healthSlider != null && health.MaxHealth != 0)
{
healthSlider.value = (float)health.CurrentHealth / health.MaxHealth;
}
}
private void OnHealthChanged()
{
UpdateView();
}
}
ClickDamage類
:為使用HealthPresenter
的類
- 不直接操控 Model (
Health
)與 View (healthSlider
)而是透過HealthPresenter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26[ ]
public class ClickDamage : MonoBehaviour
{
private Collider collider;
private HealthPresenter healthPresenter;
[private LayerMask layerToClick; ]
[private int damageValue = 10; ]
private void Start()
{
collider = GetComponent<Collider>();
healthPresenter = GetComponent<HealthPresenter>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, Mathf.Infinity, layerToClick))
{
healthPresenter?.Damage(damageValue);
}
}
}
}
Reference: https://github.com/Unity-Technologies/game-programming-patterns-demo/tree/main/Assets/12%20MVP
https://github.com/push-pop/Unity-MVVM/blob/main/Docs/Architechture.md