1. 程式人生 > >ToLua熱更新之LuaFramework框架之PureMVC(7)

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類,可以使用“註冊/分發”機制。