列举了一些 Lua 项目开发中的技术备忘录,基于 Cocos2d-x v3.16 和 Lua 5.1.5。
Lua 风格 API
借鉴了 quick-cocos2d-x 中的 Lua 风格 API,( quick-cocos2d-x 目前已被 Cocos2d-x 官方弃用)。
对常用的变量或者方法进行缩写封装。例如:
Lua 1 2 3 4 5 6 7 d = display function d.node () return cc.Node:create () end function d.sprite () return cc.Sprite:create () end
方法最后返回 self
,形成链式调用方法。例如:
Lua 1 2 3 4 5 6 7 8 9 10 function Node:pos (x, y) return self end function Node:addTo (parentNode, z, name) return self end local node = d.node():pos(100 , 100 ):size(100 , 100 ):scale(2 ):addTo(parentNode)
UI 框架
由于没有用到编辑器来进行 UI 编写,为了方便开发,使用了一套自用的 UI 框架。封装了常用的控件 Button
、ScrollList
、View
、Win
等。
避免混用 quick 与原生 Cocos2d-x 的 UI 和触摸机制。
热更新
每次发布的版本带有一个小版本号作为标志,从高版本到低版本比对生成差异代码和资源,作为更新包。
使用 AssetsManager
来下载更新包,解压到指定的 update
目录下。
确保 update
目录在 FileUtils
类的 SearchPath
中优先级比较高。添加 SearchPath
:
C++ 1 FileUtils::getInstance()->addSearchPath("update_path" , true );
热更新前需要删除缓存的 Lua 模块和资源。例如:
Lua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cc.Director:getInstance():getTextureCache():removeAllTextures() cc.SpriteFrameCache:getInstance():removeSpriteFrames() cc.FileUtils:getInstance():purgeCachedEntries() cc.Director:getInstance():purgeCachedData() for module, _ in pairs (package .loaded ) do if needRemove(module) then package .loaded [module] = nil end end
然后重新加载需要的模块文件。
注意:从应用商店下载或安装 apk 包后更新应用版本,如果安装包中带有新的代码或资源,需要删除 update
目录中下载的代码与资源,防止加载旧的代码。
原生平台交互 iOS
Lua luaoc.lua 1 function LuaObjcBridge.callStaticMethod (className, methodName, args) end
应用层 LuaObjcBridge
类,使用 Objective-C 中的反射机制调用对应类的类方法。
应用层方法必须为类方法,接受无参数或者一个 NSDictionary
类型的参数。例如:
Objective-C 1 2 3 @interface AppBridge : NSObject + (void ) openWebView:(NSDictionary *) dict; @end
Lua 1 2 3 LuaObjcBridge.callStaticMethod("className" , "methodName" , { callback = function () end , })
应用层获取回调函数为 functionId
,LuaBridge
执行函数。
Objective-C 1 2 3 4 int functionId = [[dict objectForKey:@"callback" ] intValue];cocos2d::LuaBridge::pushLuaFunctionById(functionId); cocos2d::LuaBridge::getStack()->executeFunction(0 ); cocos2d::LuaBridge::releaseLuaFunctionById(functionId);
Android
Lua luaj.lua 1 function LuaJavaBridge.callStaticMethod (className, methodName, args, sig) end
Java 1 2 3 public class AppBridge { public static void openWebView (String url) {} }
Lua 层参数中可传入回调函数,回调函数支持无参数或一个 string
类型参数。例如:
Lua 1 LuaJavaBridge.callStaticMethod("className" , "methodName" , function (paramString) end )
应用层获取回调函数为 functionId
,Cocos2dxLuaJavaBridge
执行函数。例如:
Java 1 2 3 int functionId;Cocos2dxLuaJavaBridge.callLuaFunctionWithString(functionId, "string" ); Cocos2dxLuaJavaBridge.releaseLuaFunction(functionId);
由于游戏在 GL 线程上运行,如果需要调用系统 UI 接口,需要在 UI 线程上运行,回调 Lua 时需要在 GL 线程上运行。例如:
Java 1 2 3 4 5 6 7 8 9 10 11 12 13 Context context; context.runOnUiThread(new Runnable() { @Override public void run () { context.runOnGLThread(new Runnable() { @Override public void run () { } }) } });
优化
Android:可将向应用层的参数统一封装为 JSON
格式。
网络请求 HTTP
CCHTTPRequest
类,Lua 层将 Response 封装为回调函数。
Socket
Protocol Buffers
配置文件
将 Excel 表转换为 Lua Table,项目中加载配置 Lua 文件。
使用 Python 编写工具转换,Excel 读取库 xlrd 。
数据持久化
方案一:使用了 quick 提供的 GameState 类,通过 io
模块读写文件。
方案二:使用 lsqlite3
模块,读写本地数据库。
国际化
使用 Application::getCurrentLanguage()
获取当前系统语言。
文本
将文本提取至语言包,代码中使用 key 来获取对应语言的文本。封装替换文本方法,例如:
Lua 1 2 3 4 5 function __text(key, ...) return langPack[key] end print (__text('hello_world' ))
不同语言包可在文件名后加上语言后缀以区分,例如:lang_en.lua
、lang_cn.lua
和 lang_fr.lua
等。
为便于编辑维护,可用配置文件来生成语言包文件。
资源
资源在路径上做区分,加入语言 code 作为后缀名。例如: res/example.png
、res_cn/example.png
和 res_fr/example.png
等。
插件化
将同一个插件内的文件放入某一文件夹下,例如:pluginA
。
同一插件文件之间的引用通过 import
相对路径来代替 require
绝对路径 。import
方法实现参考这里 。例如:import(".fileA")
,而不是 require("pluginA.fileA")
。
分辨率适配
都采用 NO_BORDER
策略,根据屏幕宽高比 frameSize
调整设计分辨率 designSize
大小。
全局变量 CC_DESIGN_RESOLUTION
存储设计分辨率和适配策略。autoscale
表示适配策略。
横屏游戏:如果 frameSize
宽高比大于 designSize
宽高比,autoscale
采用 FIXED_HEIGHT
。反之用 FIXED_WIDTH
。
竖屏游戏:如果 frameSize
宽高比小于 designSize
宽高比,autoscale
采用 FIXED_WIDTH
。反之用 FIXED_HEIGHT
。
Lua 1 2 3 4 5 CC_DESIGN_RESOLUTION = { width = 640 , height = 1136 , autoscale = "FIXED_HEIGHT" , }