先簡(jiǎn)單說(shuō)說(shuō)MVC,即Model View Controller。Model(模型),一般負(fù)責(zé)數(shù)據(jù)的處理;View(視圖),一般負(fù)責(zé)界面的顯示;Controller(控制器),一般負(fù)責(zé)前端的邏輯處理。拿一款手機(jī)游戲來(lái)說(shuō),界面UI的顯示、布局等就是View負(fù)責(zé);點(diǎn)擊了按鈕,手勢(shì)的滑動(dòng)等操作由Controller來(lái)處理;游戲中需要的數(shù)據(jù)資源就交給Model。
接下來(lái),看看在游戲開(kāi)發(fā)中怎么用,這里用Lua(環(huán)境使用cocos code ide)給大家說(shuō)說(shuō)。
先來(lái)看看項(xiàng)目的目錄結(jié)構(gòu):
其中cocos、Controller、Model、View這個(gè)不用多說(shuō),Event里面保存的全局消息類型,Managers是用于管理游戲中的東東的,比如管理資源,管理各種場(chǎng)景切換,層的切換等等。Utilities提供一些工具類,比如字符串的處理等。大家也可以根據(jù)自己的需求來(lái)定制目錄,比如定義一個(gè)NetCenter文件夾,專門用于處理網(wǎng)絡(luò)的。本例子中沒(méi)有用到數(shù)據(jù)操作和工具類,所以這兩個(gè)文件夾為空。
我們以游戲的運(yùn)行流程為線索來(lái)展開(kāi)說(shuō)明。
運(yùn)行項(xiàng)目,進(jìn)入到main.lua文件,來(lái)看看main函數(shù):
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
我們最后調(diào)用了GameScene類中的startGame函數(shù),來(lái)看看GameScene這個(gè)類:
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函數(shù)中,我們創(chuàng)建了一個(gè)空?qǐng)鼍埃缓笳{(diào)用SceneManager場(chǎng)景管理器來(lái)初始化場(chǎng)景。最后調(diào)用enterGame函數(shù)正式進(jìn)入游戲主界面,其中enterGame函數(shù)中又有一個(gè)LayerManager層管理器。我們來(lái)看看這兩個(gè)管理器是如何工作的。先看看SceneManager:
--場(chǎng)景管理器
SceneManager = {}
--背景層
bgLayer = nil
--游戲?qū)?
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
很簡(jiǎn)單,按順序初始化了三個(gè)空Layer。再來(lái)看看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這個(gè)函數(shù),首先切換層的時(shí)候,看看當(dāng)前層是否為空,不為空就刪掉。然后根據(jù)傳遞過(guò)來(lái)的參數(shù)來(lái)判斷要切換到哪個(gè)層。這里出現(xiàn)MVC中的Controller部分,看看是什么情況。這里調(diào)用了類MainLayerController中的create函數(shù):
function MainLayerC:create()
local layer = MainLayerC:new()
return layer
end
function MainLayerC:ctor()
self:createUI()--創(chuàng)建界面
self:addBtnEventListener()--添加按鈕監(jiān)聽(tīng)
end
function MainLayerC:createUI()
local layer = require("View.MainLayerView")
self.mainLayer = layer:createUI()
gameLayer:addChild(self.mainLayer)
end
這里我們又發(fā)現(xiàn)了MVC中的View,在createUI函數(shù)中,我們調(diào)用了類MainLayerView的createUI函數(shù),并將其添加到場(chǎng)景的游戲?qū)又?。我們?lái)看看MainLayerView這個(gè)類。
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)
--創(chuàng)建菜單
self.menu = cc.Menu:create(self.btnItem1,self.btnItem2,self.btnItem3)
self.menu:setPosition(0,0)
self:addChild(self.menu)
end
return MainLayerV
可以看到,我們?cè)谥鹘缑嬷刑砑恿艘粡埍尘皥D和三個(gè)按鈕。我們是通過(guò)資源管理器ResManager來(lái)管理游戲中的素材的,ResManager文件很簡(jiǎn)單:
--資源管理器
ResManager = {}
--主界面
ResManager.main_bg = "bg_big.png"
ResManager.main_btn1 = "cell.png"
ResManager.main_btn2 = "cell2.png"
ResManager.main_btn3 = "cell3.png"
這樣做的好處是,如果圖片改了名字或者換了路徑等,只需要在這里改一次就可以了。
可以看到我們給三個(gè)按鈕注冊(cè)了響應(yīng)函數(shù)menuCallback,在這個(gè)函數(shù)中,就是MVC中的V和C之間的“溝通”了。我們定義了一個(gè)自定義事件EVENT_CLICK_MENU_MAIN,并給這個(gè)事件添加了一個(gè)附帶參數(shù)_usedata,這個(gè)參數(shù)保存的是三個(gè)按鈕的tag。然后將這個(gè)事件發(fā)送給他的監(jiān)聽(tīng)者。這里大家應(yīng)該明白了,我們?cè)趯?duì)應(yīng)的Controller中注冊(cè)了EVENT_CLICK_MENU_MAIN的監(jiān)聽(tīng),但有這個(gè)事件發(fā)過(guò)來(lái)時(shí),我們就響應(yīng)。根據(jù)事件攜帶的參數(shù)_usedata,我們就知道了在View中,玩家點(diǎn)擊了哪個(gè)按鈕,這樣做的好處是,保證了每個(gè)界面只有一個(gè)消息,我們只需要根據(jù)這個(gè)消息攜帶的附加參數(shù)來(lái)判斷具體的事件,從而減少了消息個(gè)數(shù),這樣有助于游戲的效率。另外,我們?cè)陧憫?yīng)這個(gè)消息的時(shí)候,也會(huì)做一定的優(yōu)化,來(lái)看看類MainLayerController的響應(yīng)函數(shù):
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
--注冊(cè)事件處理
self._eventBtnListener = cc.EventListenerCustom:create(EVENT_CLICK_MENU_MAIN,eventBtnListener)
eventDispatcher:addEventListenerWithSceneGraphPriority(self._eventBtnListener,self.mainLayer)
end
可以看到實(shí)際情況,我們并不需要對(duì)傳遞過(guò)來(lái)的參數(shù)進(jìn)行判斷,而是定義了一個(gè)函數(shù)數(shù)組,直接根據(jù)下標(biāo)來(lái)調(diào)用對(duì)應(yīng)的消息響應(yīng)。之后繼續(xù)通過(guò)各種管理器來(lái)對(duì)游戲內(nèi)容進(jìn)行變化,方式和MainLayerController和MainLayerView差不多。
到這里,MVC應(yīng)用的簡(jiǎn)單介紹就結(jié)束啦,希望大家能夠喜歡本文,能夠?qū)Υ蠹覍W(xué)習(xí)lua有所幫助。
您可能感興趣的文章:- Lua游戲開(kāi)發(fā)教程之時(shí)區(qū)問(wèn)題詳解
- Lua在各個(gè)操作系統(tǒng)中的開(kāi)發(fā)環(huán)境配置教程
- 安裝Nginx+Lua開(kāi)發(fā)環(huán)境
- Centos7 安裝Nginx整合Lua的示例代碼
- Nginx安裝lua-nginx-module模塊的方法步驟
- cocos2dx+lua實(shí)現(xiàn)橡皮擦功能
- Lua中三種循環(huán)語(yǔ)句的使用講解
- Lua中的變量與賦值方法
- Lua協(xié)同程序coroutine的簡(jiǎn)介及優(yōu)缺點(diǎn)
- Luvit像Node.js一樣寫Lua應(yīng)用