[Cocos2dx] framework MVC in cocos2dx-lua
MVC introduction MVC, that is, Model View Controller.Model, generally responsible for data processing;View (View), generally responsible for display of the interface; Controller (Controller ),Generally responsible for front-end logic processing. For a mobile game, the display and layout of the UI are the responsibility of the View. When a button is clicked, operations such as gesture sliding are handled by the Controller; the data resources required in the game are handed over to the Model.
Here, cocos, Controller, Model, and View are needless to say,Global message types saved in the Event,Managers is used to manage the game's stuff, such as managing resources, managing various scenario switching, layer SwitchingAnd so on.Utilities provides some tool classes, such as string processing.. You can also customize directories as needed. For example, you can define a NetCenter folder for processing networks. In this example, data operations and tool classes are not used, so the two folders are empty. Process instances are described based on the running process of the game. Run the project and go to the main. lua file to see the main function:
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
Finally, we call the startGame function in the GameScene class to see the class GameScene:
Require (Managers. sceneManager) require (Managers. layerManager) local GameScene = class (GameScene) local scene = nil function GameScene: startGame () -- initialize 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 (bytes) end return GameScene
In the startGame function, we create an empty scenario and call the SceneManager scenario manager to initialize the scenario. Finally, call the enterGame function to enter the main game interface. The enterGame function contains a LayerManager layer manager. Let's see how these two managers work. First look at SceneManager:
-- Scenario manager SceneManager ={}-- background layer bgLayer = nil -- game layer gameLayer = nil -- pop-up window layer 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
It is easy to initialize three empty layers in order. Let's take a look at the LayerManager:
-- Layer manager LayerManager ={} LAYER_TYPE_MAIN = LAYER_TYPE_MAIN local curLayer = nil function LayerManager: new (o) o = o or {} setretriable (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
Look at the gotoLayerByType function. When switching the layer, check whether the current layer is empty. If it is not empty, delete it. Then, you can determine the layer to switch to based on the passed parameters.
Here the Controller section in MVC appears to see what the situation is. The create function in the MainLayerController class is called here.:
Function MainLayerC: create () local layer = MainLayerC: new () return layerend function MainLayerC: ctor () self: createUI () -- create interface self: addBtnEventListener () -- Add button to listen to end function MainLayerC: createUI () local layer = require (View. mainLayerView) self. mainLayer = layer: createUI () gameLayer: addChild (self. mainLayer) end
Here, we find the View in MVC. In the createUI function, we call the createUI function like MainLayerView and add it to the game layer of the scenario.. Let's take a look at the MainLayerView class.
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) -- create menu self. menu = cc. menu: create (self. btnItem1, self. btnItem2, self. btnItem3) self. menu: setPosition (0, 0) self: addChild (self. menu) end return MainLayerV
We can see that a background image and three buttons are added to the main interface. We use resource manager ResManager to manage materials in the game. The ResManager file is simple:
-- Resource Manager ResManager ={} -- Main Interface ResManager. main_bg = bg_big.pngResManager.main_btn1 = cell.png ResManager. main_btn2 = cell2.pngResManager. main_btn3 = cell3.png
The advantage of this is that if the image name or path is changed, you only need to change it once.
We can see that we have registered the three buttons.
The response function menuCallback. In this function, it is the "communication" between V and C in MVC.. We have defined a Custom Event EVENT_CLICK_MENU_MAIN, and added an additional parameter _ usedata for this event. This parameter stores the tags of the three buttons. Then, send the event to the listener. Here we should understand that we have registered the EVENT_CLICK_MENU_MAIN listener in the corresponding Controller, but when this event is sent, we will respond. Based on the _ usedata parameter carried by the event, we know which button the player clicks in the View. The advantage of this is that,
This ensures that each interface has only one message. We only need to determine the specific event based on the additional parameters carried by the message, thus reducing the number of messages, which helps the efficiency of the game.. In addition, we will make some optimizations when responding to this message. Let's take a look at the response functions of the MainLayerController class:
Function MainLayerC: addBtnEventListener () -- button event processing 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 -- register event processing self. _ eventBtnListener = cc. eventListenerCustom: create (EVENT_CLICK_MENU_MAIN, eventBtnListener) eventDispatcher: addEventListenerWithSceneGraphPriority (self. _ eventBtnListener, self. mainLayer) end
We can see that we do not need to judge the passed parameters, but define a function array to directly call the corresponding message response according to the subscript. Then, the game content will be changed through various managers, similar to that of MainLayerController and MainLayerView. Cocos2dx-lua Frame Process
Many learners even don't know Why enterScene ("MainScene") can be a string?
After creating a new cocos2dx-Lua project, you first see main. lua started to MyApp. lua.
require("app.MyApp").new():run()
View the MyApp. lua file:
1.Require ("app. MyApp ")
The MyApp. lua code executed here is:
Local MyApp = class ("MyApp", cc. mvc. AppBase) -- inherits cc. mvc. AppBasereturn MyApp
At this time, you get the MyApp class (lua has a lot of online class implementations)
2.Require ("app. MyApp"). new ()
After MyApp. new () is executed, the Code executed is:
function MyApp:ctor() MyApp.super.ctor(self)end
Why does new () execute MyApp: ctor ()? See the function class (classname, super) method under function. lua:
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
We can see that in the implementation method of the class, a new () method is declared for each created class. The method calls the ctor () constructor method (the ctor is just a name, so after some people think of new, they will certainly call the constructor. lua does not have a class, but we just mimic the class)
3.Require ("app. MyApp"). new (): run ()
At this time
function MyApp:run() CCFileUtils:sharedFileUtils():addSearchPath("res/") self:enterScene("MainScene")end
So we entered MainScene. lua.
For the MyApp. lua file, if I change it to the following format, do you understand the above:
-- Declaration class MyApp = class ("MyApp", cc. mvc. appBase) --- class constructor -- function MyApp: ctor () MyApp. super. ctor (self) end --- corresponds to the static create () method of cpp -- function MyApp: create () local myApp = MyApp. new () myApp: init () end --- your method -- @ param self -- local function launchMainScene (self) CCFileUtils: sharedFileUtils (): addSearchPath ("res/") self: enterScene ("MainScene") end --- init Method -- function MyApp: init () -- add code here launchMainScene (self) end
The corresponding main. lua converts the original require ("app. MyApp"). new (): run ()
To:
require("app.MyApp")MyApp:create()
Is it easier for you to understand, haha.
4.MainScene. lua
EnterScene ("MainScene") Why can I switch the scenario?
Let's take a look at the AppBase of the parent class of MyApp:
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
In this way, you can understand why MainScene can be called even if no require file exists. Of course, you should pay attention to the file path of require, which is the default app/scenes folder written by scene. All right, the rest should be able to see why based on the above ideas.
Lua's MVC Framework Sailor
Sailor is a MVC programming framework of Lua language.. Supports cross-platform, mod_lua, mod_pLua, Nginx ngx_lua, or any Web server that supports CGI, such as Civetweb or Mongoose, provided that CGILua is available. The directory structure for developing applications using Sailor is as follows:
/Conf-stores the configuration file/controllers-Controller/layouts-layout file/models-model/pub-static file/runtime-temporary file generated during runtime/views-. lp View File