Cocos2d-x Lua运行流程

Cocos2d-lua 是 Cocos2d-x引擎添加了Lua绑定的版本。使用C++和Cocos2d-x引擎开发时,编写的代码是直接调用Cocos2d-x引擎的API,因为Cocos2d-x引擎也是使用C++编写的,所以无需进行语言转换。而使用Lua和Cocos2d-x编码时,编写的代码必须通过LuaEngine引擎执行,LuaEngine引擎封装了Cocos2d-x引擎的API,所以相当于使用Lua脚本在调用Cocos2d-x的API。

Cocos2d-lua
Cocos2d-lua

主入口文件 main.lua

main.lua启动文件是Cocos2d-lua项目默认主入口,项目启动时默认首先执行main.lua文件。

运行流程

步骤0:设置 编译后的客户端开始执行时,使用FileUtils加载Luac文件的提示是否显示。

cc.FileUtils:getInstance():setPopupNotify(true)

步骤1:将源代码src目录和资源res目录添加到文件搜索路径

cc.FileUtils:getInstance():addSearchPath("src/")
cc.FileUtils:getInstance():addSearchPath("res/")
  • 通过addSearchPath()方法设置源代码和资源的搜索路径

步骤2:加载配置文件读取配置

require "config"
  • config.lua配置文件用于定义初始化所使用的全局变量
  • require()方法表示引入一个文件,使用require()方法加载文件的过程会检查文件的lua语法,同时会完成被加载文件内部变量的初始化。

步骤3:执行初始化

加载cocos/init.lua文件,根据config.lua配置文件对cocos2d-lua框架进行初始化。

require "cocos.init"

步骤4:加载app目录下MyApp.lua文件,创建App对象并调用run()函数。

local function main()
  require("app.MyApp"):create():run()
end
  • 调用对应classCreate()方法创建对象,并执行run()方法。
  • run()函数用于配置第一个进入的场景,默认为MainScene
  • 通过载入的app.MyApp模块创建一个MyApp实例,并调用实例的run()方法,此时将启动执行MyApp脚本。

垃圾回收机制

local function main()
  -- 运行一个完整的垃圾回收周期
  collectgarbage("collect") 
  collectgarbage("setpause", 100) 
  collectgarbage("setstepmul", 5000)
end

collectgarbage("collect")

运行一个完整的垃圾回收周期

collectgarbage("count")

返回当前程序使用的内存总量,以KB为单位。

collectgarbage("restart")

若垃圾收集器停止则重新运行

collectgarbage("step")

进行一次垃圾回收迭代,第二个参数值越大表示一次迭代的时间越长,如果本次迭代时垃圾回收的最后一次迭代则函数返回true

collectgarbage("stop")

停止运行垃圾收集器

collectgarbage("setpause", 100)

设置垃圾收集暂停时间变量的值,值由第二个参数指出,该值被用于控制垃圾收集器被Lua自动内存管理再次运行之前需要的等待时长。当其小于100时意味着收集器在新周期开始前不再等待。其值越大垃圾收集器被运行的频率越低,越不主动。当其值200时,收集器在总使用内存数量达到上次垃圾收集时的2倍再开启新的收集周期。因此,根据程序不同的特征,可以通过修改该值使得程序达到最佳的性能。

collectgarbage("setstepmul", 5000)

设置垃圾收集器步长倍增器的值,步长倍增器stepmul用于控制垃圾收集器相对内存分配的速度。数值越大收集器工作越主动,同时也增加了垃圾收集每次迭代步长的大小。值小于100可能会导致垃圾器1个周期永远不能结束,建议不要这样设置。默认值为200,表示垃圾收集器运行的速率是内存分配的2倍。

错误捕获

local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
    print(msg)
end

xpcall(调用函数, 错误捕获函数)

Lua提供xpcall函数来捕获异常,当错误发生时,Lua会在栈释放前调用错误处理函数,因此可使用debug库收集错误信息。常用debug处理函数包括:

  • debug.debug 给出Lua的提示符,可手工查看错误发生时的情况。
  • debug.traceback 控制器解释器用来构建错误信息的函数

程序启动后,xpcall(表示以“保护模式”调用函数)会被执行,它会调用main()函数。如果发生错误,则会调用__G__TRACKBACK__()函数。

配置文件 config.lua

  • DEBUG
    设置输出调试信息选项,即通过控制不同的print()输出信息。
    0 表示关闭
    1 表示打印少量调试信息
    2 表示打印标准完整的调试信息
  • CC_USE_FRAMEWORK
    设置是否使用quick框架,其中包括MVC框架。
  • CC_SHOW_FPS
    设置是否显示FPS等显卡信息
  • CC_DISABLE_GLOBAL
    设置是否允许定义全局字段
  • CC_DESIGN_RESOLUTION
    设置不同分辨率

默认应用 MyApp.lua

应用文件:app/MyApp.lua

local  MyApp = class("MyApp", cc.load("mvc").AppBase)

function MyApp:onCreate()
  math.randomseed(os.time())
end

return MyApp
  • MyApp类继承自mvc中的cc.mvc.AppBase类,系统自动查找packages/mvc/AppBase.lua文件,AppBaseQuick框架自带的一个MVC的应用程序基础类,此类为应用程序提供一些逻辑控制上的功能,如进入场景、切换场景等。
  • class()方法本身是Quick框架定义的用于创建自定义lua类的函数,function class(classname, super)函数参数classname表示类名,super表示父类或创建对象实例的函数。

MVC

MVC是一种组合设计模式,被广泛应用在带有图形交互的用户界面程序(GUI)开发中。MVC的宗旨在于分离关注点(SoC),将界面表现和业务逻辑分离。SoC原则在现代软件工程中是一个非常重要的设计理念。对于实际问题,要划分不同的关注点,且关注点之间必须隔离开来,这样才能达到 更好的代码重用,以获得鲁棒性、可适配性、可维护性。

MVC

Cocos2d本身并不是基于MVC的理念设计的,在Cocos2d中CCSceneCCLayerCCSprite等类都是CCNode的子类,开发流程基本是这样的:

  1. 通过应用程序代理类来初始化第一个即主场景
  2. 场景中实例化一个或多个布景图层,并将其添加进去。
  3. 布景图层中实例化一个或多个精灵,并将其添加进去。
  4. 场景处理用户输入,例如Touch事件和加速计的改变。同时更新布景图层和精灵,让精灵运行一个或多个动作等。
  5. 场景中运行一个游戏循环(一般是1/60更新一次),然后布景图层和精灵在游戏循环中做更新和游戏逻辑。

对于手游而言,界面UI的布局与显示都是由View负责的,点击事件手势滑动等操作是由Controller来处理的,游戏中需要的数据是由Model负责的。

local MyApp = class("MyApp", cc.load("mvc").AppBase)

/src/packages/mvc/AppBase.lua 中定义了一个AppBase基类,作为所有Quick游戏的Lua入口,由player新建的工程,有个app/MyApp.lua文件就是继承自AppBase类。

AppBase功能类似于iOS工程中的 AppDelegate.m 实现的功能:

  • 程序前后台切换事件的接收和分发
  • framework 提供程序退出接口
  • 提供创建 sceneview 的接口,并约定其存放路径。

垃圾收集

local function main()
  collectgarbage("collect")
  collectgarbage("setpause", 100)
  collectgarbage("setstepmul", 5000)
end

本地存储

--[[
客户端本地存储
场景:游戏中需在本地存储数据,根据存储的数据类型调用接口
设置客户端本地数据库,本地的UserDefault.xml会存在相应数据。
--]]
-- 获取客户端本地存储数据
function GetClientUserData(key, dataType)
    local type = string.lower(dataType)
    if type == "bool" then
        return cc.UserDefault:getInstance():getBoolForKey(key)
    elseif type == "int" then
        return cc.UserDefault:getInstance():getIntegerForKey(key)
    elseif type == "float" then
        return cc.UserDefault:getInstance():getFloatForKey(key)
    elseif type=="double" then
        return cc.UserDefault:getInstance():getDoubleForKey(key)
    elseif type == "string" then
        return cc.UserDefault:getInstance():getStringForKey(key)
    end
end
-- 设置客户端本地存储数据
function SetClientUserData(key, value, dataType)
    local type = string.lower(dataType)
    if type == "bool" then
        return cc.UserDefault:getInstance():setBoolForKey(key, value)
    elseif type=="int" then
        return cc.UserDefault:getInstance():setIntegerForKey(key, value)
    elseif type=="float" then
        return cc.UserDefault:getInstance():setFloatForKey(key, value)
    elseif type=="double" then
        return cc.UserDefault:getInstance():setDoubleForKey(key, value)
end

声音系统

声音引擎:SimpleAudioEngine

音乐格式 iOS和Android均可采用MP3格式
AudioEngine.preload("bg.mp3") 预加载背景音乐
AudioEngine.playMusic("bg.mp3", true) 播放背景音乐,true为循环。
AudioEngine.stopMusic() 停止播放音乐
AudioEngine.pauseMusic() 暂停播放音乐
AudioEngine.resumeMusic() 恢复播放音乐
AudioEngine.isMusicPlaying() 判断音乐是否播放
AudioEngine.setMusicVolumn(0.5) 设置音乐音量
AudioEngine.getMusicVolumn() 获取音乐音量

音效格式 iOS可采用MP3或WAV,Android可采用OGG格式。
AudioEngine.preloadEffect("effect.mp3") 预加载音效
AudioEngine.playEffect("effect.mp3") 播放音效
AudioEngine.pauseAllEffect() 暂停所有音效的播放
AudioEngine.resumeAllEffect() 继续播放所有音效
AudioEngine.resumeEffect(id) 继续播放指定ID的音效
AudioEngine.stopEffect(id) 停止播放指定ID的音效
AudioEngine.stopAllEffect() 停止播放所有音效
AudioEngine.setEffectsMusicVolumn(0.5) 设置音效音量
AudioEngine.getEffectsVolumn() 获取音效音量

-- LUA使用方式
cc.SimpleAudioEngine:getInstance():playMusic("bg.mp3")

-- 重新设置为上次声音
local musicVolumn = cc.UserDefault:getInstance():getFloatForKey("MusicVolumn", 1)
cc.SimpleAudioEngine:getInstance():setMusicVolumn(musicVolumn)
cc.SimpleAudioEngine:getInstance():setEffectsMusicVolumn(musicVolumn)

杀虫游戏

玩法:初始化5个血量,随机生成爬向洞的昆虫,累积进入5个即结束游戏。

杀虫游戏
组织结构

布景层生命周期

生命周期顺序流程

  1. init() 初始化布景层调用,在类初始化时,仅会调用一次。
  2. onEnter() 进入布景层时调用,可能会发生多次。

Cocos2dx类似于ObjectC一样,有两种方式来申请内存创建对象,一种是静态函数create(),一种是在自身的init()中,在这两种函数中申请内存创建对象能让程序正常运行,但是当对象将要去展现或被添加到另一个容器时,它会调用onEnter()函数,所以最好不要在onEnter()中做内存方面的操作。

  1. onEnterTransitionDidFinish() 进入布景层且过渡动画结束时调用
  2. onExit() 退出布景层时调用
  3. onExitTransitionDidStart() 退出布景层且开始过渡动画时调用
  4. clearup() 布景层对象被清除时调用,整个布景层销毁时调用。

简单来说

  • 进入布景层时:init()->onEnter()->onEnterTransitionDidFinish()
  • 退出布景层时:onExit()->onExitTransitionDidStart()->init()

onEnteronExit在Lua中不会因为节点的addremove而直接被调用, 当子节点被父节点的addremove时,会发送enterexit的消息,所以需要初始化节点的时候,监听消息并在收到消息后调用onEnteronExit

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容