『可複用』這個詞相信大家都熟悉,通過『可複用』的元件,可以大大提高軟體開發效率。 值得注意的事,當我們設計一個可複用的物件導向元件時,需要保證其獨立性,也就是我們熟知的『高內聚,低耦合』原則。
元件化設計的思路
不管是開發客戶端應用程式還是開發伺服器端應用程式,『元件』這個詞我們並不陌生。不管是在iOS中的xib,還是在AngularJS的Component,或者後端開發的 User Control,可複用的元件是物件導向開發的基礎。所以在Unity 3D 框架設計時,元件化是核心的概念。那麼如何去設計SubView和SubViewModel,我總結出幾條原則:
- 當一個功能被不同的場合頻繁用到,建議將這個功能抽象成SubView(SubViewModel)
- SubView(SubViewModel)應該保持高內聚,低耦合原則
- SubViewModel不應該處理具體的業務邏輯,它很單純,可通過委託Delegate的方式交由外部處理
構建SubView和SubViewModel
假設現在有如下一個需求,需要繫結角色的資訊到頭像上,如下圖所示:
這是一個很常見的需求,建立一個MonoBehaviour,定義Public的變數並引用這些控制元件,最後再將這個MonoBehaviour附加到GameObject上,很快就能完成。當然,我不能說這樣的實施是錯誤的,畢竟我們只要保證執行正確就可以了。
看到左上角的勳章嗎,這個勳章會在不同的場景出現,我們優先把它考慮成一個SubView(BadgeView),也就是最外層的FaceBoxView裡巢狀了一個BadgeView。
public class FaceBoxView:UnityGuiView<FaceBoxViewModel>
{
public Text nameText;
public Text levelText;
public Image faceImage;
public BadgeView badgeView;
}複製程式碼
我們在分析一下BadgeView需要什麼資料?它需要武器的Icon和屬性顏色,所以我們抽象出一個Badge的DataModel:
public class Badge
{
public string Icon { get; set; }
public string ElementColor { get; set; }
}複製程式碼
所以對於FaceBoxViewModel而言,它為FaceBoxView服務。FaceBoxView需要什麼資料,它就提供什麼資料。顯然它需要提供Name,Level,Face以及Badge元件的DataModel:
public class FaceBoxViewModel:ViewModelBase
{
public readonly BindableProperty<string> Name=new BindableProperty<string>();
public readonly BindableProperty<int> Level=new BindableProperty<int>();
public readonly BindableProperty<string> Face=new BindableProperty<string>();
public readonly BindableProperty<Badge> Badge=new BindableProperty<Badge>();
public override void OnStartReveal()
{
base.OnStartReveal();
Initialization();
}
public void Initialization()
{
Name.Value = "比爾";
Level.Value = 9;
Face.Value = "Avatar204_Face";
Badge.Value = new Badge() {Icon = "Icon_WeaponRod", ElementColor = "1CB9FFFF"};
}
}複製程式碼
因為Badge是BindableProperty型別物件,特點是當Badge Value改變時,觸發的OnValueChanged事件就可以給BadgeViewModel傳遞資料,從而初始化BadgeView:
protected override void OnInitialize()
{
base.OnInitialize();
Binder.Add<string>("Name",OnNamePropertyVlaueChanged);
Binder.Add<int>("Level",OnLevelPropertyValueChanged);
Binder.Add<string>("Face",OnFacePropertyValueChanged);
Binder.Add<Badge>("Badge",OnBadgePropertyValueChanged);
}
private void OnBadgePropertyValueChanged(Badge oldValue, Badge newValue)
{
badgeView.BindingContext = new BadgeViewModel() ;
badgeView.BindingContext.Initialization(newValue);
}複製程式碼
我們可以看到,元件化的實施從程式碼量上是變得複雜了,元件的顆粒度越細,那麼巢狀的層次就越深,如果某個功能只出現一次,並且不會被複用,那麼我不推薦將它變為一個SubView(SubViewModel)
小結
本文為大家介紹怎樣將元件化模式思想引入到Unity 3D中,在我的uMVVM框架中,元件化是核心,就像使用者控制元件一樣,隨拿隨走,它們保持高度獨立,這樣的好處是不會產生緊耦合。還值得一提的是,其實Unity 3D本身的開發模式就是基於元件化開發的。只要建立一個MonoBehaviour元件然後附加到GameObject上就能正常執行。但需要注意的事,如果沒有好的約束,一個GameObject上就會附加好多個MonoBehaviour,GameObject的子GameObject也會附加很多個MonoBehaviour,久而久之,整個層級結構會變得異常複雜和難以維護。uMVVM的理念是隻需要一個View,View是唯一的入口,並且View可以是非常複雜,裡面維護了所有的SubView,所以換UI也好,換功能也罷,只要關注於對應的View即可。
原始碼託管在Github上,點選此瞭解
歡迎關注我的公眾號: