lua版pureMVC框架使用分析

  一.简介:今天在网上找框架时,发现了一个使用lua实现的开源PureMVC框架,于是便分析了一下使用方法,以便在自己的项目中可以尝试使用。框架相关的博客地址:https://blog.csdn.net/u012740992/article/details/103674426;框架的源码地址:https://github.com/HengyuanLee/UnityLuaPuremvc;PureMVC开源框架官方网站:http://puremvc.org/;我之前的C#版PureMVC学习笔记:https://www.cnblogs.com/movin2333/p/14584491.html

  二.作者的框架提供的案例分析:

    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

    proxy负责代理相关的model数据,data就是这个数据模型,在其中提供获取数据的方法GetData和修改数据的相关方法。

  三.总结

    总的来说,这个lua版的puremvc和C#版本相比差别不大,基本的方法名称等都是一致的。使用lua实现和C#实现有一些使用上的不同:C#语言会预先编译,因此在使用过程中只需要用到相应的命令直接发送命令即可,但是lua是解释型语言,从上到下依顺序执行,因此在执行的过程中不注意就会出现发送了命令但是注册或者处理相关命令的脚本还没有执行过导致报错,因此lua相关脚本在使用前一定要看一看有没有脚本没有执行过就使用了,尤其是在Command脚本中需要注意引用其他Command或者Mediator、Proxy、Panel等类型的脚本;不过lua有优势的地方在于C#中的对象需要声明类型,而lua则不需要,这导致了C#在使用时需要经常转换类型,而lua则不需要。

posted @ 2021-04-21 16:54  movin2333  阅读(593)  评论(0编辑  收藏  举报