先简单说说MVC,即Model View Controller。Model(模型),一般负责数据的处理;View(视图),一般负责界面的显示;Controller(控制器),一般负责前端的逻辑处理。拿一款手机游戏来说,界面UI的显示、布局等就是View负责;点击了按钮,手势的滑动等操作由Controller来处理;游戏中需要的数据资源就交给Model。
接下来,看看在游戏开发中怎么用,这里用Lua(环境使用cocos code ide)给大家说说。
先来看看项目的目录结构:
其中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差不多。
到这里,MVC应用的简单介绍就结束啦,希望大家能够喜欢本文,能够对大家学习lua有所帮助。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- DzintraErliha-Dreamscape(2024)[24Bit-96kHz]FLAC
- 群星.1999-上华世纪国语总冠军4CD【上华】【WAV+CUE】
- 叶丽仪.1992-仙鹤神针电视剧原声音乐专辑【星传真】【WAV+CUE】
- 陈柏宇.2011-QUINQUENNIUM【SONY】【WAV+CUE】
- 《51首热门当红歌曲 十大顶级发烧女声 3CD》[WAV+CUE][1.6GB]
- 《赵传 深受摇滚迷青睐的台湾音乐人 约定》[WAV+CUE][320MB]
- 《庄心妍 实力唱将 发烧音乐精选集》[WAV+CUE][380MB]
- [音展纪念碟]2006年《TAA台湾第十六届国际HI-ENDHI-FI音响展》2cd[WAV]
- 中国音乐地图之听见西藏山歌牧歌酒歌2020[WAV分轨]
- 王宏伟2017-永远的红飘带[太平洋影音][WAV+CUE]
- 费翔.1997-NOW现在【第一中国】【WAV+CUE】
- 新格合唱团.1981-我唱你和2辑(滚石新格民歌系列)【新格】【WAV+CUE】
- 陈柏宇.2011-QUINQUENNIUM【SONY】【WAV+CUE】
- 雨果唱片-《金唛系列-蔷薇依韵琼瑶》WAV
- 陈百强《凝望(2012华纳LPCD45限量版)[DMI]》[WAV整轨]