[cocos2dx]cocos2dx-lua中的架構MVC,cocos2dxluamvc

來源:互聯網
上載者:User

[cocos2dx]cocos2dx-lua中的架構MVC,cocos2dxluamvc
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 oend function LayerManager:getInstance()    if self.instance == nil then        self.instance = self:new()    end         return self.instanceend 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    endend
看看gotoLayerByType這個函數,首先切換層的時候,看看當前層是否為空白,不為空白就刪掉。然後根據傳遞過來的參數來判斷要切換到哪個層。 這裡出現MVC中的Controller部分,看看是什麼情況。這裡調用了類MainLayerController中的create函數
function MainLayerC:create()    local layer = MainLayerC:new()    return layerend 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 mainLayerend 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.pngResManager.main_btn1 = cell.pngResManager.main_btn2 = cell2.pngResManager.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.AppBasereturn 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 instanceend
可以看到,在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 視圖檔案

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.