lua开发中实现MVC框架的简单应用

先简单说说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有所帮助。

(0)

相关推荐

  • lua开发中实现MVC框架的简单应用

    先简单说说MVC,即Model View Controller.Model(模型),一般负责数据的处理:View(视图),一般负责界面的显示:Controller(控制器),一般负责前端的逻辑处理.拿一款手机游戏来说,界面UI的显示.布局等就是View负责:点击了按钮,手势的滑动等操作由Controller来处理:游戏中需要的数据资源就交给Model. 接下来,看看在游戏开发中怎么用,这里用Lua(环境使用cocos code ide)给大家说说. 先来看看项目的目录结构: 其中cocos.Co

  • Android开发中的MVC设计模式浅析

    Android开发中的MVC设计模式的理解 1. Android系统中分层的理解: (1).在Android的软件开发工作中,应用程序的开发人员主要是应用Android Application Framework层封装好的Api进行快速开发. (2).在Android框架的四个层次中,下层为上层服务,上层需要下层的支持,上层需要调用下层的服务. (3).这种分层的方式带来极大的稳定性.灵活性和可扩展性,使得不同层的开发人员可以按照规范专心特定层的开发. (4). Android的官方建议应用程序

  • iOS开发中使用CoreLocation框架处理地理编码的方法

    一.简介 1.在移动互联网时代,移动app能解决用户的很多生活琐事,比如 (1)导航:去任意陌生的地方 (2)周边:找餐馆.找酒店.找银行.找电影院 2.在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这2大功能,必须基于2个框架进行开发 (1)Map Kit :用于地图展示 (2)Core Location :用于地理定位 3.两个热门专业术语 (1)LBS :Location Based Service(基于定位的服务) (2)SoLoMo :Social Local Mobi

  • iOS App开发中Core Data框架基本的数据管理功能小结

    一.何为CoreData CoreData是一个专门用来管理数据的框架,其在性能与书写方便上都有很大的优势,在数据库管理方面,apple强烈推荐开发者使用CoreData框架,在apple的官方文档中称,使用CoreData框架可以减少开发者50%--70%的代码量,这虽然有些夸张,但由此可见,CoreData的确十分强大. 二.设计数据模型 在iOS开发中,时常使用SQL数据库对大量的表结构数据进行处理,但是SQL有一个十分明显的缺陷,对于常规数据模型的表,其处理起来是没问题的,例如一个班级表

  • iOS App开发中Masonry布局框架的基本用法解析

    Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了并具有高可读性,而且同时支持 iOS 和 Max OS X.Masonry是一个用代码写iOS或OS界面的库,可以代替Auto layout.Masonry的github地址:https://github.com/SnapKit/Masonry Masonry使用讲解: mas_makeConstraints 是给view添加约束,约束有几种,分别是边距,宽,高,左上右下距离,基准线.添加过约束后

  • Android开发中类加载器DexClassLoader的简单使用讲解

    简介 "类装载器"(ClassLoader),顾名思义,就是用来动态装载class文件的.标准的Java SDK中有个ClassLoader类,借助此类可以装载需要的class文件,前提是ClassLoader类初始化必须制定class文件的路径. import关键字引用的类文件和ClassLoader动态加载类的区别: import引用类的两个特点: 1.必须存在于本地,当程序运行该类时,内部类装载器会自动装载该类. 2.编译时必须在现场,否则编译过程会因找不到引用文件而不能正常编译

  • .net中的DI框架AutoFac简单介绍

    AutoFac是.net程序下一个非常灵活易用,且功能强大的DI框架,本文这里简单的介绍一下使用方法. 安装: Install-Package Autofac 简单的示例: static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<ConsoleLogger>().As<ILogger>(); var container = builder.Build()

  • 深入解读Android开发中Activity的生命周期

    什么是Activity        首先,Activity是Android系统中的四大组件之一,可以用于显示View.Activity是一个与用记交互的系统模块,几乎所有的Activity都是和用户进行交互的,但是如果这样就能说Activity主要是用来显示View就不太正确了. 在深入了解Activity之前,我们先要知道一下MVC设计模式,在JAVAEE 中MVC设计模式已经很经典了,而且分的也比较清晰了,但是在Android中,好多人对MVC在Android开发中的应用不是很清楚,下面我

  • Android开发中的单例模式应用详解

    本文实例讲述了Android开发中的单例模式应用.分享给大家供大家参考,具体如下: 单例模式是应用最广的设计模式之一,在应用这种模式的时候,单例对象的类必须保证只有一个实例存在.许多时候,整个系统只需要拥有一个全局对象,这样有利于协调系统的整体行为.如一个应用中,应该只有ImageLoader实例,这个ImageLoader实例中又包含网络请求.缓存系统.线程池等,很耗资源,因此没有理由让他构造多个实例.这种不能自由构造对象的情况就是使用单例模式的场景.在Android系统中存在很多这种场景,比

  • C#开发中的垃圾回收机制简析

    GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是GC的第一次出现.Lisp的程序员认为内存管理太重要了,所以不能由程序员自己来管理.但后来的日子里Lisp却没有成气候,采用内存手动管理的语言占据了上风,以C为代表.出于同样的理由,不同的人却又不同的看法,C程序员认为内存管理太重要了,所以不能由系统来管理,并且讥笑Lisp程序慢如乌龟的运行速度.的

随机推荐