一.簡介:今天在網上找架構時,發現了一個使用lua實作的開源PureMVC架構,于是便分析了一下使用方法,以便在自己的項目中可以嘗試使用。
二.作者的架構提供的案例分析:
1.Main函數
require 'PureMVC/PureMVC'
require 'StartupMCmd'
require 'AppFacade'
require 'Network'
--遊戲lua入口函數
function Main()
print('start Main ...')
AppFacade:GetInstance():Startup()
end
Main函數是遊戲項目的入口函數,其中首先執行了一些必要的lua腳本,包括MVC架構、啟動遊戲Command腳本、管理Facade腳本、網絡NetWord腳本。在C#端的代碼中,是直接使用xlua的luaenv執行了require 'Main' Main()語句,也就是執行了Main腳本中的Main函數,啟動了遊戲。
2.Facade函數
AppFacade = class('AppFacade',PureMVC.Facade)
AppFacade.APP_STARTUP = 'APP_STARTUP'
local module = AppFacade
--ctor等同于class的構造方法,在new()時會被調用。
function module:ctor()
self.super:ctor()
--注冊啟動指令StartupMCmd,這是個宏指令。
--注冊為一個函數原因是實作指令的無狀态性,需要時才被執行個體化出來,省記憶體。
self:RegisterCommand(AppFacade.APP_STARTUP, function() return StartupMCmd.new() end)
end
--發出啟動遊戲指令。
function module:Startup()
self:SendNotification(AppFacade.APP_STARTUP)
end
在Main函數中啟動了Startup函數,這個函數是Facade中定義的函數。架構定義了一個class方法用于建立類對象(在lua中對象本質上是一張表),第一個參數為對象名,第二個參數為對象繼承的類(lua中本質上是元表),這裡這個對象是繼承自Facade類的;第二行代碼在對象裡添加了一個字元串變量,用于作為名稱和實際的指令綁定。在ctor構造方法中使用RegisterCommand方法進行指令注冊。這裡最後提供的Startup方法就是Main腳本中調用的方法,負責發出啟動指令的消息。
3.MacroCommand指令
require 'Login/LoginInitCmd'
require 'PlayerInfo/PlayerInfoInitCmd'
StartupMCmd = class('StartupMCmd', PureMVC.MacroCommand)
--這個類以MCmd字尾結尾是一個MacroCommand,
--内部添加了多個子Cmd,按添加順序執行,
--宏Cmd一次性使用,執行完之後子Cmd就會被清空。
function StartupMCmd:InitializeMacroCommand()
--添加2個UI系統子產品的初始化Cmd
self:AddSubCommand(function() return LoginInitCmd.new() end)
self:AddSubCommand(function() return PlayerInfoInitCmd.new() end)
end
Macro複合指令和C#版本的puremvc使用是基本一樣的,在其中的InitializeMacroCommand方法中使用AddSubCommand方法添加子指令。值得注意的是在定義指令前,需要執行相應的指令腳本。
4.SimpleCommand指令
require 'Login/LoginPanel'
require 'Login/LoginMediator'
require 'Login/LoginClickCmd'
LoginInitCmd = class('LoginInitCmd', PureMVC.SimpleCommand)
--Login功能子產品初始化cmd,
--這裡是有view 的Mediator執行個體化,model的Proxy參考PlayerInfo系統子產品
function LoginInitCmd:Execute(notification)
local panel = LoginPanel.new(LoginPanel.NAME)
local mediator = LoginMediator.new(LoginPanel.NAME, panel)
AppFacade:GetInstance():RegisterMediator(mediator)
AppFacade:GetInstance():RegisterCommand(LoginClickCmd.CMD, function () return LoginClickCmd.new() end)
end
Simple指令繼承自SimpleCommand類,在其中的Execute函數中定義指令執行的具體代碼。還是需要注意的是使用代碼時涉及到的相關panel、mediator和command等腳本都需要先執行再使用,執行個體化出來的panel、mediator、proxy等都可以注冊到facade中。
5.Panel腳本
--以Panel為字尾的類名對應Unity 裡制作的UI預設
--如這裡對應LoginPanel.prefab
--panel類等同于一個view,panel裡隻提供擷取控件的接口,供mediator通路,panel内部不做複雜操作。
LoginPanel = class('LoginPanel')
LoginPanel.NAME = 'LoginPanel'
local module = LoginPanel
module.gameObject = nil
module.transform = nil
module.inputUsername = nil
module.inputPassword = nil
module.btnLogin = nil
function module:ctor(panelName)
self.panelName = panelName
self:Load(panelName)
end
--加載unity 裡的LoginPanel.prefab預設,
--并且擷取元件
function module:Load(panelName)
self.gameObject = CS.ResourcesLoader.Instance:LoadPanel(panelName)
self.transform = self.gameObject.transform
self.gameObject:SetActive(false)
self.inputUsername = self.transform:Find('InputUsername'):GetComponent(typeof(CS.UnityEngine.UI.InputField))
self.inputPassword = self.transform:Find('InputPassword'):GetComponent(typeof(CS.UnityEngine.UI.InputField))
self.btnLogin = self.transform:Find('BtnLogin'):GetComponent(typeof(CS.UnityEngine.UI.Button))
end
function module:Show()
self.gameObject:SetActive(true)
end
function module:Hide()
self.gameObject:SetActive(false)
end
panel就是是MVC中的view,使用mediator去代理它。在panel中持有對應遊戲面闆的相關資訊,如gameObject、transform、各種控件等,在ctor函數中為相關資訊指派,提供函數用于擷取控件,提供顯示隐藏panel的函數等。
6.mediator中介
--用于對LoginPanel的各種操作,事件處理,資料填充等。
--PureMVC.Mediator同時繼承了Notifier類,是以能夠SendNotification
--PureMVC.Mediator實作了觀察者功能,提供ListNotificationInterests()方法來填寫感興趣的Cmd,并在HandleNotification()回調。
--這說明了Mediator其實也是一個Cmd,以觀察者模式內建了Cmd的功能。
LoginMediator = class('LoginMediator', PureMVC.Mediator)
local module = LoginMediator
function module:ctor(mediatorName, view)
--覆寫了父類的構造方法,顯示調用父類構造方法,
--和其它語言有點不同,lua文法特性和class的定義決定了這樣寫
module.super.ctor(self, mediatorName, view)
self:init()
end
function module:init()
self.View:Show()
self.View.btnLogin.onClick:AddListener(
function()
self:onLoginClick()
end
)
end
--點選登入事件
function module:onLoginClick()
if self.View.inputUsername == nil then
print(debug.traceback())
print(self.View.__cname)
end
local username = self.View.inputUsername.text
local password = self.View.inputPassword.text
local body = {
username = username,
password = password
}
self:SendNotification(LoginClickCmd.CMD, body)
end
--指定方法ListNotificationInterests()中填寫感興趣的Cmd
function module:ListNotificationInterests()
return {
Network.CMD_LOGIN_SUCCESS
}
end
--感興趣的Cmd 在HandleNotification(notification)中收到回調
function module:HandleNotification(notification)
if notification.Name == Network.CMD_LOGIN_SUCCESS then
self.View:Hide()
end
end
在Mediator中介中提供相應panel的各種處理方法,以消息事件的形式提供給外部調用。mediator中介都繼承自Mediator類,ctor構造方法也和C#版本的puremvc一緻,在其中進行控件的方法注冊等操作,view是持有的具體panel,在ListNotificationInterests中傳回mediator關注的事件名稱,在HandleNotification中進行處理。
7.proxy代理
--Proxy繼承了Notifer接口,可以發送指令SendNotification(),
--但不監聽任何指令。
PlayerInfoProxy = class('PlayerInfoProxy', PureMVC.Proxy)
PlayerInfoProxy.CMD_REFRESH = 'PlayerInfoProxy'
local module = PlayerInfoProxy
function module:GetData()
return self.Data
end
--提供修改資料的方法,隻應該提供給伺服器資料來時修改用。
function module:Refresh(data)
self.Data = data
self:SendNotification(PlayerInfoProxy.CMD_REFRESH)
end