ToLua熱更新之LuaFramework框架之PureMVC(7)
LuaFramework使用了PureMVC框架。百度百科上說:“PureMVC是在基於模型、檢視和控制器MVC模式建立的一個輕量級的應用框架”。PureMVC框架可以做到較好的解耦,減少遊戲程式碼的相互呼叫。然而LuaFramework整合PureMVC屬於“殺雞用牛刀”,實質上只用到了事件分發(也可能是我理解得不夠透徹)。如果單純寫一套事件分發系統,可能不到100行程式碼就能完成。
1、解耦的好處
如果沒有很好的解耦設計,遊戲功能越多,程式碼就越亂,最後沒人敢改動。舉個例子,假如遊戲中揹包(item)和成就(Achieve)兩項功能,各用一個類實現。當玩家獲得100個經驗豆(一種道具)時,會獲得“擁有100個經驗豆”的成就;當成就點數達到300時,會獲得道具獎勵。一種常見的實現方法是呼叫對方的public函式,程式碼如下所示。然而如果一款遊戲有幾百上千個類,之間又相互呼叫,如果某些功能需要大改(例如刪掉成就功能),那其他的類也得改動。
Class Item
{
public AddItem()
{
if(經驗豆 > 100)
achieve.AddAchieve(“擁有100個經驗豆”)
}
}
Class Achieve
{
public AddAchieve()
{
成就點數 + 10
if(成就點數 > 300)
item.AddItem(寶石)
}
}
如果使用事件分發,各個類之間的聯絡就減弱了。如下所示的程式碼中揹包類(Item)監聽了訊息“新增道具”,成就類(Achieve)監聽了訊息“新增成就”。如果達成成就需要新增獎勵,只需派發“新增道具”這條訊息,由揹包類去執行。這樣類與類之間不存在相互呼叫,就算大改功能甚至刪掉功能,其他類都受到的影響比較小。
Class Item { Start() { 監聽(“新增道具”,AddItem) } private AddItem() { if(經驗豆 > 100) 分發(“新增成就”,“擁有100個經驗豆”) } } Class Achieve { Start() { 監聽(“新增成就”,AddAchieve) } private AddAchieve() { 成就點數 + 10 If(成就點數 > 300) 分發(“新增道具”, 寶石) } }
2、MVC的使用方法
LuaFramework中的Framwork目錄存放著PureMVC框架的程式碼,個人認為在LuaFramework中屬於過度設計(畢竟從其他地方拷過來的)。它的原理並不複雜,用一個列表把監聽資訊儲存起來,在派發訊息時,查詢對應的監聽表,找到需要回調的物件。
PureMVC框架便是實現了“註冊/分發”模式(釋出/訂閱、觀察者模式),可以呼叫RegisterCommand註冊訊息(命令),呼叫SendMessageCommand方法分發訊息。RegisterCommand方法可以把某個繼承ControllerCommand 的類註冊到指定的訊息下,在事件分發時呼叫該類的Execute方法。
例如新建一個名為TestCommand的類,讓它繼承ControllerCommand,然後編寫Execute方法處理具體事務。
using UnityEngine;
using System.Collections;
public class TestCommand : ControllerCommand
{
public override void Execute(IMessage message)
{
Debug.Log("name=" + message.Name);
Debug.Log("type=" + message.Type);
}
}
接著,編寫另一個類來處理訊息。這個類先呼叫AppFacade.Instance.RegisterCommand()將TestCommand類註冊到“TestMessage”訊息下。然後使用SendMessageCommand()派發“TestMessage”訊息。框架將會建立一個TestCommand例項,並呼叫它的Execute方法。
public class Main : MonoBehaviour
{
void Start()
{
AppFacade.Instance.RegisterCommand ("TestMessage",
typeof(TestCommand));
AppFacade.Instance.SendMessageCommand ("TestMessage");
}
}
執行結果如下所示,可以看到分發訊息後,TestCommand的Execute方法被呼叫。
Execute方法的引數message包含了Name,Body,Type三個成員(如下圖所示)。其中Name是命令名,Body是一個任意型別的引數。
如下程式碼所示,在SendMessageCommand中可以給訊息的Body傳值,相應的Execute方法便可以獲取它。
void Start()
{
AppFacade.Instance.RegisterCommand ("TestMessage",
typeof(TestCommand));
AppFacade.Instance.SendMessageCommand ("TestMessage", "這是字串");
}
執行結果如下圖所示。
總而言之,LuaFramework中所謂的pureMVC只是一套“註冊/分發”機制,完全可以用c#的事件來實現。另《Unity3D網路遊戲實戰》中的客戶端網路模組部分也使用的“註冊/分發”機制,有興趣的讀者可以看看。
3、MVC與Unity3D元件的結合
pureMVC與Unity3D元件之間有一些封裝,只要讓元件繼承View類(View類繼承MonoBehavior),即使用pureMVC框架的RegisterMessage和SendMessageComman方法實現“註冊/分發”機制。
例如,新建一個繼承自View的TestManage元件,在Start 方法中它註冊了“msg1”、“msg2”、“msg3”三個訊息的監聽。在Update方法中,當按下空格鍵時,分發訊息“msg1”。
當接收到訊息後,指定物件(這裡指定this)的OnMessage方法會被呼叫,引數message裡面包含了命令名、Body等資訊。程式碼如下所示。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TestManage : View
{
// Use this for initialization
void Start ()
{
List<string> regList = new List<string>();
regList.Add("msg1");
regList.Add("msg2");
regList.Add("msg3");
RegisterMessage(this,regList);
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyUp (KeyCode.Space))
{
facade.SendMessageCommand("msg1", null);
}
}
public override void OnMessage(IMessage message)
{
Debug.Log ("OnMessage " + message.Name);
}
}
此外LuaFramework的各個Manager(如GameManager,LuaManager,SoundManager等)也都繼承自View類,可以使用“註冊/分發”機制。