[cocos2dx]cocos2dx-lua中的框架MVC

MVC简介

MVC,即Model View Controller。Model(模型),一般负责数据的处理View(视图),一般负责界面的显示;Controller(控制器),一般负责前端的逻辑处理。拿一款手机游戏来说,界面UI的显示、布局等就是View负责;点击了按钮,手势的滑动等操作由Controller来处理;游戏中需要的数据资源就交给Model。

其中cocos、Controller、Model、View这个不用多说,Event里面保存的全局消息类型Managers是用于管理游戏中的东东的,比如管理资源,管理各种场景切换,层的切换等等。Utilities提供一些工具类,比如字符串的处理等。大家也可以根据自己的需求来定制目录,比如定义一个NetCenter文件夹,专门用于处理网络的。本例子中没有用到数据操作和工具类,所以这两个文件夹为空。

流程实例

我们以游戏的运行流程为线索来展开说明。运行项目,进入到main.lua文件,来看看main函数:

local function main()
    collectgarbage(collect)
    -- avoid memory leak
    collectgarbage(setpause, 100)
    collectgarbage(setstepmul, 5000)

    -- initialize director
    local director = cc.Director:getInstance()

    --turn on display FPS
    director:setDisplayStats(true)

    --set FPS. the default value is 1.0/60 if you don't call this
    director:setAnimationInterval(1.0 / 60)

    cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(320, 480, 1)

    --create scene
    local scene = require(GameScene)
    local gameScene = scene:startGame()

end

我们最后调用了GameScene类中的startGame函数,来看看GameScene这个类:

require(Managers.SceneManager)
require(Managers.LayerManager)

local GameScene = class(GameScene)
local scene = nil

function GameScene:startGame()
    --初始化
    scene = cc.Scene:create()
    if cc.Director:getInstance():getRunningScene() then
        cc.Director:getInstance():replaceScene(scene)
    else
        cc.Director:getInstance():runWithScene(scene)
    end
    SceneManager:initLayer(scene)
    self:enterGame()
end

function GameScene:enterGame()
    LayerManager:getInstance():gotoLayerByType(LAYER_TYPE_MAIN)
end

return GameScene

在startGame函数中,我们创建了一个空场景,然后调用SceneManager场景管理器来初始化场景。最后调用enterGame函数正式进入游戏主界面,其中enterGame函数中又有一个LayerManager层管理器。我们来看看这两个管理器是如何工作的。先看看SceneManager:

--场景管理器
SceneManager = {}

--背景层
bgLayer = nil
--游戏层
gameLayer = nil
--弹窗层
panelLayer = nil

function SceneManager:initLayer(scene)
    bgLayer = cc.Layer:create()
    scene:addChild(bgLayer)

    gameLayer = cc.Layer:create()
    scene:addChild(gameLayer)

    panelLayer = cc.Layer:create()
    scene:addChild(panelLayer)
end

很简单,按顺序初始化了三个空Layer。再来看看LayerManager管理器:

--Layer管理器
LayerManager = {}

LAYER_TYPE_MAIN = LAYER_TYPE_MAIN

local curLayer = nil

function LayerManager:new(o)
    o = o or {}
    setmetatable(o,self)
    self.__index = self
    return o
end

function LayerManager:getInstance()
    if self.instance == nil then
        self.instance = self:new()
    end

    return self.instance
end

function LayerManager:gotoLayerByType(type)
    if curLayer ~= nil then
        curLayer:destroy()
    end

    if type == LAYER_TYPE_MAIN then
        local layer = require(Controller.MainLayerController):create()
        curLayer = layer
    end
end

看看gotoLayerByType这个函数,首先切换层的时候,看看当前层是否为空,不为空就删掉。然后根据传递过来的参数来判断要切换到哪个层。这里出现MVC中的Controller部分,看看是什么情况。这里调用了类MainLayerController中的create函数

function MainLayerC:create()
    local layer = MainLayerC:new()
    return layer
end

function MainLayerC:ctor()
    self:createUI()--创建界面
    self:addBtnEventListener()--添加按钮监听
end

function MainLayerC:createUI()
    local layer = require(View.MainLayerView)
    self.mainLayer = layer:createUI()
    gameLayer:addChild(self.mainLayer)
end

这里我们又发现了MVC中的View,在createUI函数中,我们调用了类MainLayerView的createUI函数,并将其添加到场景的游戏层中。我们来看看MainLayerView这个类。

local eventDispatcher = cc.Director:getInstance():getEventDispatcher()

local MainLayerV = class(MainLayerView,function()
    return cc.Layer:create()
end)

function MainLayerV:createUI()
    local mainLayer = MainLayerV:new()
    return mainLayer
end

function MainLayerV:ctor()
    self:initUI()
end

function MainLayerV:initUI()
    local winSize = cc.Director:getInstance():getWinSize()
    self.bg = cc.Sprite:create(ResManager.main_bg)
    self.bg:setPosition(winSize.width / 2,winSize.height / 2)
    self:addChild(self.bg)

    local function menuCallback(tag,menuItem)
        local event = cc.EventCustom:new(EVENT_CLICK_MENU_MAIN)
        event._usedata = tag
        eventDispatcher:dispatchEvent(event)
    end

    self.btnItem1 = cc.MenuItemImage:create(ResManager.main_btn1,ResManager.main_btn1,ResManager.main_btn1)
    self.btnItem1:setPosition(winSize.width / 2,winSize.height / 3)
    self.btnItem1:setTag(1)
    self.btnItem1:registerScriptTapHandler(menuCallback)

    self.btnItem2 = cc.MenuItemImage:create(ResManager.main_btn2,ResManager.main_btn2)
    self.btnItem2:setPosition(winSize.width / 2,winSize.height / 2)
    self.btnItem2:setTag(2)
    self.btnItem2:registerScriptTapHandler(menuCallback)

    self.btnItem3 = cc.MenuItemImage:create(ResManager.main_btn3,ResManager.main_btn3)
    self.btnItem3:setPosition(winSize.width / 2,winSize.height / 3 * 2)
    self.btnItem3:setTag(3)
    self.btnItem3:registerScriptTapHandler(menuCallback)

    --创建菜单
    self.menu = cc.Menu:create(self.btnItem1,self.btnItem2,self.btnItem3)
    self.menu:setPosition(0,0)
    self:addChild(self.menu)
end

return MainLayerV

可以看到,我们在主界面中添加了一张背景图和三个按钮。我们是通过资源管理器ResManager来管理游戏中的素材的,ResManager文件很简单:

--资源管理器
ResManager = {}

--主界面
ResManager.main_bg = bg_big.png
ResManager.main_btn1 = cell.png
ResManager.main_btn2 = cell2.png
ResManager.main_btn3 = cell3.png

这样做的好处是,如果图片改了名字或者换了路径等,只需要在这里改一次就可以了。

可以看到我们给三个按钮注册了响应函数menuCallback,在这个函数中,就是MVC中的V和C之间的“沟通”了。我们定义了一个自定义事件EVENT_CLICK_MENU_MAIN,并给这个事件添加了一个附带参数_usedata,这个参数保存的是三个按钮的tag。然后将这个事件发送给他的监听者。这里大家应该明白了,我们在对应的Controller中注册了EVENT_CLICK_MENU_MAIN的监听,但有这个事件发过来时,我们就响应。根据事件携带的参数_usedata,我们就知道了在View中,玩家点击了哪个按钮,这样做的好处是,保证了每个界面只有一个消息,我们只需要根据这个消息携带的附加参数来判断具体的事件,从而减少了消息个数,这样有助于游戏的效率。另外,我们在响应这个消息的时候,也会做一定的优化,来看看类MainLayerController的响应函数:

function MainLayerC:addBtnEventListener()
    --按钮事件处理
    local function eventBtnListener(event)
       local eventNum = event._usedata
       local switch = {
           [1] = function()
                print(Btn one)
           end,
           [2] = function()
                print(Btn two)
           end,
           [3] = function()
                print(Btn three)
           end
       }
       switch[eventNum]()
    end
    --注册事件处理
    self._eventBtnListener = cc.EventListenerCustom:create(EVENT_CLICK_MENU_MAIN,eventBtnListener)
    eventDispatcher:addEventListenerWithSceneGraphPriority(self._eventBtnListener,self.mainLayer)
end

可以看到实际情况,我们并不需要对传递过来的参数进行判断,而是定义了一个函数数组,直接根据下标来调用对应的消息响应。之后继续通过各种管理器来对游戏内容进行变化,方式和MainLayerController和MainLayerView差不多。

cocos2dx-lua框架流程

很多学习者甚至不知道enterScene(“MainScene”) 为什么里面可以是个字符串?

新建cocos2dx-Lua工程之后,你首先看到的main.lua启动到MyApp.lua。

require("app.MyApp").new():run()  

看MyApp.lua文件:

1. require(“app.MyApp”)

这里执行的MyApp.lua的代码是:

local MyApp = class("MyApp", cc.mvc.AppBase)  -- 继承cc.mvc.AppBase
return MyApp

这时候,你得到了MyApp这个类(lua关于类的实现网上很多)

2. require(“app.MyApp”).new()

MyApp.new()执行后,执行的代码是:

function MyApp:ctor()
    MyApp.super.ctor(self)
end

为什么new()了之后会执行MyApp:ctor()?请看function.lua下的function class(classname, super)方法:

function cls.new(...)
            local instance = cls.__create(...)
            -- copy fields from class to native object
            for k,v in pairs(cls) do instance[k] = v end
            instance.class = cls
            instance:ctor(...)
            return instance
end

可以看到,在class的实现方法里面,给每个创建的类声明了一个new()方法,方法里面调用了ctor()构造方法(ctor只是个名字,所以不是有些人认为的new了之后,当然会调用构造方法,lua没有类,只是我们模仿了类)

3. require(“app.MyApp”).new():run()

这时候调用了

function MyApp:run()
    CCFileUtils:sharedFileUtils():addSearchPath("res/")
    self:enterScene("MainScene")
end

所以进到了MainScene.lua。

对于MyApp.lua文件,如果我修改成下面的样子,是不是你就理解了上面所做的事情:

-- 声明类
MyApp = class("MyApp", cc.mvc.AppBase) 

--- 类构造方法
--
function MyApp:ctor()
    MyApp.super.ctor(self)
end

--- 对应cpp版的static create()方法
--
function MyApp:create()
    local myApp = MyApp.new()
    myApp:init()
end

--- 你自己的方法
-- @param self
--
local function launchMainScene(self)
    CCFileUtils:sharedFileUtils():addSearchPath("res/")
    self:enterScene("MainScene")
end

--- init 方法
--
function MyApp:init()
    -- add code here
    launchMainScene(self)
end

对应的main.lua将原来的require(“app.MyApp”).new():run()

修改为:

require("app.MyApp")
MyApp:create()

这样你是不是更容易理解了,哈哈。

4. MainScene.lua

enterScene(“MainScene”) 为什么可以切换场景?

我们看下MyApp的父类AppBase里面:

function AppBase:enterScene(sceneName, args, transitionType, time, more)
    local scenePackageName = self. packageRoot .. ".scenes." .. sceneName
    local sceneClass = require(scenePackageName)
    local scene = sceneClass.new(unpack(totable(args)))
    display.replaceScene(scene, transitionType, time, more)
end

这样你能理解了为什么连require文件都没有就能调用MainScene,当然你要留意下,它require时候的文件路径,scene默认写的app/scenes文件夹。好了,其他的应该按照上面的思路基本都能知道为什么了

Lua的MVC框架Sailor

Sailor 是一个 Lua 语言的 MVC 编程框架。支持跨平台,兼容 mod_lua 或者 mod_pLua, Nginx 的 ngx_lua, 或者任何支持 CGI 的 Web 服务器,如 Civetweb 或者 Mongoose, 前提是必须有 CGILua。使用 Sailor 开发应用的目录结构如下:

  • /conf - 存放配置文件
  • /controllers - 控制器
  • /layouts - 布局文件
  • /models - 模型
  • /pub - 静态文件
  • /runtime - 运行时生成的临时文件
  • /views - .lp 视图文件
时间: 2024-10-12 16:02:16

[cocos2dx]cocos2dx-lua中的框架MVC的相关文章

cocos2d-x在Lua中添加3d模型创建3D动画

--3d模型和3D动画的创建 require"Cocos2d" local Sprite3DScene=class("Sprite3DScene",function() return cc.Scene:create() end) --添加create函数 function Sprite3DScene:create() local scene=Sprite3DScene.new() scene:addChild(scene:init()) return scene e

cocos2dx之lua项目开发中MVC框架的简单应用

**************************************************************************** 时间:2015-03-31 作者:Sharing_Li 转载注明出处:http://blog.csdn.net/sharing_li/article/details/44658317 **************************************************************************** 最近的游

cocos2d-x lua 中使用protobuf并对http进行处理

本文介绍 cocos2d-x lua 中使用http 和 基于cocos2d-x 对lua http的封装(部分ok) 本博客链接 http://blog.csdn.net/vpingchangxin/article/details/24458051 protobuf  Google的一个非常好用的数据传输的封装 说实话Google的东西确实比較好用 所以我们前后端数据交换就用他了 只是Google没有对lua进行支持 还好社区有开源的大侠们贡献 找了全部关于lua protobuf 我仅仅找到

Cocos2d-x Lua中生命周期函数

场景(Scene)以及所有节点(Node)的生命周期事件如下:enter.进入场景时候触发.enterTransitionFinish.进入场景而且过渡动画结束时候触发.exit.退出场景时候触发 .exitTransitionDidStart.退出场景而且开始过渡动画时候触发.cleanup.场景对象被清除时候触发.提示  GameScene场景中的(Scene)继承于节点(Node),这些生命周期事件根本上是从Node继承而来.事实上所有Node对象(包括:场景.层.精灵等)都有这些事件,具

Cocos2d-x Lua中使用标签

游戏场景中的文字包括了静态文字和动态文字.静态文字如下图所示游戏场景中①号文字“COCOS2DX”,动态文字如图4-1所示游戏场景中的②号文字“Hello World”.静态文字一般是由美工使用Photoshop绘制在背景图片上,这种方式的优点是表现力很丰富,例如:①号文字“COCOS2DX”中的“COCOS”.“2D”和“X”设计的风格不同,而动态文字则不能,而且静态文字无法通过程序访问,无法动态修改内容.动态文字一般是需要通过程序访问,需要动态修改内容.Cocos2d-x Lua可以通过标签

Lua中调用 cocos2d-x 的滑动条/滚动条 ScrollView

 ScrollView 我想玩儿过手机的朋友对滑动条都不陌生吧,(旁边: 这不是废话么???? )   那好吧,废话不多说直接开始ScrollView吧 local m_BaseNode  -- 主场景 local CreateScroll    -- 房间分级滑动视图 local CreateStageNode   -- 创建节点 local m_ScrollView              -- 滑动层变量 local m_Inner     -- 内容器 local addScrol

Cocos2d-x 脚本语言Lua中的面向对象

Cocos2d-x 脚本语言Lua中的面向对象 面向对象不是针对某一门语言,而是一种思想.在面向过程的语言也能够使用面向对象的思想来进行编程. 在Lua中,并没有面向对象的概念存在,没有类的定义和子类的定义.但相同在Lua中能够利用面向对象的思想来实现面向对象的类继承. 一.复制表的方式面向对象 --Lua中的面向对象 --[[ 复制表方式面向对象 參数为一张表.通过遍历这张表取值,赋给一张空表,最后返回新建的表.来达到克隆表 ]] function clone(tab) local ins =

cocos2d-x lua中实现异步加载纹理

原文地址:  http://www.cnblogs.com/linchaolong/p/4033118.html 前言   问题:最近项目中需要做一个loading个界面,界面中间有一个角色人物走动的动画,在显示这个loading界面的时候加载资源,项目是用cocos2d-x lua实现的,界面做出来后发现在加载资源的时候界面会卡住. 原因: 因为使用的不是异步加载,而且cocos2d-x没有绑定异步加载资源的api到lua中,其实在lua中实现不了异步. 想通过在lua中启动一个线程去加载资源

Cocos2d-x Lua中多场景切换生命周期

在多个场景切换时候,场景的生命周期会更加复杂.这一节我们介绍一下场景切换生命周期.多个场景切换时候分为几种情况:情况1,使用pushScene函数从实现GameScene场景进入SettingScene场景.情况2,使用replaceScene函数实现从GameScene场景进入SettingScene场景.情况3,使用popScene函数从实现SettingScene场景回到GameScene场景.我们参考GameScene重写SettingScene的中几个生命周期函数,代码如下: [htm