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,保存真正的生命資料 - 含有一個
HealthChangedevent,每當有更改生命值的動作時,都會呼叫這個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)而是透過HealthPresenter1
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