做完lua版棋牌游戏,下面把我遇到的一些问题记录下。
一、确定版本
关于版本问题真的要好好计划下,目前苹果官方下达通牒,所有app必须支持IPv6。而lua热更新机制是去请求网站,低版本可能不支持IPv6,因此开发的时候务必要选择支持IPv6的cocos版本。这个问题不多说,自己可以模拟下IPv6网段,去试试便知。开发版本是cocos2d-x 3.2(支持不支持IPv6目前还没有测试,因此最好开发时选择最新版本)
打开lua当前项目文件夹/frameworks/cocos2d-x/cocos/cocos2d.cpp文件
const char* cocos2dVersion()
{
return "cocos2d-x 3.2";
}
这段代码就表示版本号
二、常用cocos方法
1)cc.FileUtils:getInstance():setSearchPaths(searchPaths) -- 添加搜索路径
e.g.local fileUtils = cc.FileUtils:getInstance()
local searchPaths = {}
local resPrefix = "res/"
table.insert(searchPaths, 1, resPrefix .. "图片名")
...
table.insert(searchPaths, 1, resPrefix .. "abc...d/图片名")
fileUtils:setSearchPaths(searchPaths)
2)cc.Director:getInstance():getTextureCache():addImageAsync(magePath, callback)
e.g.cc.Director:getInstance():getTextureCache():addImageAsync("图片名",handler(self, self.AsyncLoaded))
cc.Director:getInstance():getTextureCache():addImageAsync("图片名",function () self:AsyncLoaded() end)
注意: handler(obj, method)方法在version cocos2d-x 3.2 是没有的(这个方法是quick-3.3里的方法),下面把他添加到当前版本里,打开extern.lua在最后添加如下代码
实际上,除了 C++ 回调 Lua 函数之外,在其他所有需要回调的地方都可以使用handler(obj, method)方法
function handler(obj, method)
return function(...)
return method(obj, ...)
end
end
很明显handler(obj, method)方法,只不过是把function封装了下,跟function () self:AsyncLoaded() end调用时一样的。
3)cc.Director:getInstance():getVisibleSize() -- 手机可视尺寸
4)cc.UserDefault:getInstance():getBoolForKey("字段名", false)
cc.UserDefault:getInstance():setBoolForKey("字段名", true)
e.g.local m_bUserInit = cc.UserDefault:getInstance():getBoolForKey("USER_INIT", false)
if not m_bUserInit then
cc.UserDefault:getInstance():setBoolForKey("USER_INIT", true)
cc.UserDefault:getInstance():setBoolForKey("BackMusic", true)
cc.UserDefault:getInstance():setBoolForKey("GameMusic", true)
end
5)cc.Director:getInstance():getScheduler():scheduleScriptFunc(handler(self, self.Login), 0.1, false)
注意:这个方面有2个坑:参数一:刷新函数
参数二:每次刷新的时间间隔
参数三:是否只执行一次。false为无限次。
1、scheduleScriptFunc第3个参数,只有设置false才执行,true不执行(不是只执行一次)
2、这个定时器除非手动关闭,就算整个场景销毁,也无法关闭(比如在某个场景开启定时器,切换到另个场景,这个场景里的定时器还是运行的)。
没关系,我们可以用动作来完成所需功能:
local delay = cc.DelayTime:create(delay)
local sequence = cc.Sequence:create(delay, cc.CallFunc:create(callback)
local action = cc.RepeatForever:create(sequence)
node:runAction(action)
-- delay = 1 那么就表示:每1S执行一次callback回调函数
-- local action = cc.RepeatForever:create(sequence)出掉node:runAction(sequence) 就表示只执行一次
后来才发现原来版本里是有这个函数的:extern.lua里的schedule(node, callback, delay)可以自己试试(我没试)
6)动作:
1、cc.BezierTo:create(time, bezier)方法
e.g.local bezier = {cc.p(x1,y1), cc.p(x2,y2), cc.p(x3,y3),}
local pSeqBack = cc.Sequence:create(cc.BezierTo:create(time, bezier))
node:runAction(pSeqBack)
注意:同样BezierTo(obj, method)方法在version cocos2d-x 3.2是没有的。。。quick-3.3有!
添加BezierTo有3步:
1)打开lua当前项目文件夹/cocos2d-x/cocos/scripting/lua-bindings/manual/lua_cocos2dx_manual.cpp
搜索BezierBy,ctrl+c,ctrl+v到BezierBy下面(有兴趣可以看看这2个方法的区别)
int tolua_cocos2d_BezierTo_create(lua_State* tolua_S)
{
if (NULL == tolua_S)
return 0;
int argc = 0;
bool ok = true;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(tolua_S,1,"cc.BezierTo",0,&tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(tolua_S) - 1;
if (argc == 2)
{
double t = 0.0;
ok &= luaval_to_number(tolua_S, 2, &t);
if (!ok)
return 0;
int num = 0;
cocos2d::Vec2 *arr = NULL;
ok &= luaval_to_array_of_vec2(tolua_S, 3, &arr, &num);
if (!ok)
return 0;
if (num < 3)
{
CC_SAFE_DELETE_ARRAY(arr);
return 0;
}
ccBezierConfig config;
config.controlPoint_1 = arr[0];
config.controlPoint_2 = arr[1];
config.endPosition = arr[2];
CC_SAFE_DELETE_ARRAY(arr);
BezierTo* tolua_ret = BezierTo::create(t, config);
if (NULL != tolua_ret)
{
int nID = (tolua_ret) ? (int)tolua_ret->_ID : -1;
int* pLuaID = (tolua_ret) ? &tolua_ret->_luaID : NULL;
toluafix_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret,"cc.BezierTo");
return 1;
}
}
CCLOG("'create' has wrong number of arguments: %d, was expecting %d\n", argc, 2);
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'create'.",&tolua_err);
return 0;
#endif
}
2)搜索BezierBy,ctrl+c,ctrl+v到BezierBy下面(有兴趣可以看看这2个方法的区别)
static void extendBezierTo(lua_State* tolua_S)
{
lua_pushstring(tolua_S,"cc.BezierTo");
lua_rawget(tolua_S,LUA_REGISTRYINDEX);
if (lua_istable(tolua_S,-1))
{
lua_pushstring(tolua_S,"create");
lua_pushcfunction(tolua_S,tolua_cocos2d_BezierTo_create);
lua_rawset(tolua_S,-3);
}
lua_pop(tolua_S, 1);
}
3)打开lua当前项目文件夹/frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.cpp
搜索BezierBy,ctrl+c,ctrl+v到BezierBy下面(有兴趣可以看看这2个方法的区别)
static int lua_cocos2dx_BezierTo_finalize(lua_State* tolua_S)
{
printf("luabindings: finalizing LUA object (BezierTo)");
return 0;
}
int lua_register_cocos2dx_BezierTo(lua_State* tolua_S)
{
tolua_usertype(tolua_S,"cc.BezierTo");
tolua_cclass(tolua_S,"BezierTo","cc.BezierTo","cc.BezierBy",nullptr);
tolua_beginmodule(tolua_S,"BezierTo");
tolua_endmodule(tolua_S);
std::string typeName = typeid(cocos2d::BezierTo).name();
g_luaType[typeName] = "cc.BezierTo";
g_typeCast["BezierTo"] = "cc.BezierTo";
return 1;
}
7)动画:
1、直接加载.png
e.g.local animation =cc.Animation:create()
for i=0,5 do
local szName =string.format("图片%d.png",i)
animation:addSpriteFrameWithFile(szName)
end
2、加载.plist里的.png
e.g.local animation =cc.Animation:create()
for i=0,13 do
local szName =string.format("图片%d.png",i)
local spriteFrame = cc.SpriteFrameCache:getInstance():getSpriteFrame(szName)
animation:addSpriteFrame(spriteFrame)
end
animation:setDelayPerUnit(time)
animation:setRestoreOriginalFrame(true)
local action =cc.Animate:create(animation)
node:runAction(cc.RepeatForever:create(action))
8)TableView使用方法和注意点
e.g.local tableView = cc.TableView:create(cc.size(width, height))
tableView:setDirection(cc.SCROLLVIEW_DIRECTION_HORIZONTAL)
tableView:setPosition(0, 0)
tableView:setDelegate()
self:addChild(tableView)
--registerScriptHandler functions must be before the reloadData funtion
tableView:registerScriptHandler(handler(self, self.numberOfCellsInTableView),cc.NUMBER_OF_CELLS_IN_TABLEVIEW)
tableView:registerScriptHandler(handler(self, self.scrollViewDidScroll),cc.SCROLLVIEW_SCRIPT_SCROLL)
tableView:registerScriptHandler(handler(self, self.scrollViewDidZoom),cc.SCROLLVIEW_SCRIPT_ZOOM)
tableView:registerScriptHandler(handler(self, self.tableCellTouched),cc.TABLECELL_TOUCHED)
tableView:registerScriptHandler(handler(self, self.cellSizeForTable),cc.TABLECELL_SIZE_FOR_INDEX)
tableView:registerScriptHandler(handler(self, self.tableCellAtIndex),cc.TABLECELL_SIZE_AT_INDEX)
tableView:reloadData()
function ScrollView:scrollViewDidScroll(view)
print("scrollViewDidScroll")
end
function ScrollView:scrollViewDidZoom(view)
print("scrollViewDidZoom")
end
function ScrollView:tableCellTouched(table,cell)
print("cell touched at index: " .. cell:getIdx())
end
function ScrollView:cellSizeForTable(table,idx)
--error cocos2dx
return self.m_cellSize.width, self.m_cellSize.width
end
function ScrollView:tableCellAtIndex(table, idx)
local cell = table:dequeueCell()
if not cell then
cell = cc.TableViewCell:new()
item = cc.Sprite:create("图片名")
item:setPosition(self.m_cellSize.width/2, self.m_cellSize.height/2)
item:setTag(60)
cell:addChild(item)
else
item = cell:getChildByTag(60)
end
return cell
end
function ScrollView:numberOfCellsInTableView(table)
return ScrollView:GetCount()
end
注意:
1、setDelegate()方法必须要加上,不然回调不起作用。
2、打开cocos2dx源码/tests/lua-tests/src/ExtensionTest/ExtensionTest.lua
搜索TableView,这个文件就是官方例子下面有段话:
-- registerScriptHandler functions must be before the reloadData funtion
注册方法必须要在reloadData之前,不然回调不起作用。
3、cellSizeForTable函数的坑
发现没有: 返回的宽高居然是一样的,而且取宽高最小值,这里宽小。
function ScrollView:cellSizeForTable(table,idx)
return self.m_cellSize.width, self.m_cellSize.width
end
9)粒子、其他动作、场景跳转等就不说了,官方例子。
三、常用lua方法
1)随机函数
1、随机种子
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))2、随机值
math.random([n [, m]]) 有三种用法: 无参调用, 产生 (0,1) 之间的浮点随机数; 只有参数 n, 产生 1-n 之间的整数; 有两个参数 n, m, 产生 n-m 之间的随机整数2)table.sort的一个坑
e.g.table.sort(self.m_Data,
function(a,b)
return a.m_cbData<b.m_cbData
end)
注意:当self.m_Data表里有2个相同的m_cbData字段值时,如果多次调用sort时,这2个值的位置会互换。
什么意思呢?假如表self.m_Data = { m_pSprite, m_cbData } 2个字段
local pData = {}
pData.m_pSprite = cc.Sprite:create(a)
pData.m_cbData = 1
table.insert(self.m_Data, pData)
pData.m_pSprite = cc.Sprite:create(b)
pData.m_cbData = 2
table.insert(self.m_Data, pData)
pData.m_pSprite = cc.Sprite:create(c)
pData.m_cbData = 2
table.insert(self.m_Data, pData)
当调用时:
table.sort(self.m_Data,
function(a,b)
return a.m_cbData<b.m_cbData
end)
假如是:a b c
下次在调用时,可能就是:a c b
所以必须要有个字段2值不相同,才能排序,例如再增加个字段
local pData = {}
pData.m_pSprite = cc.Sprite:create(a)
pData.m_cbData = 1
pData.m_iIndex = self.m_iIndex
table.insert(self.m_Data, pData)
self.m_iIndex = self.m_iIndex + 1
pData.m_pSprite = cc.Sprite:create(b)
pData.m_cbData = 2
pData.m_iIndex = self.m_iIndex
table.insert(self.m_Data, pData)
self.m_iIndex = self.m_iIndex + 1
pData.m_pSprite = cc.Sprite:create(c)
pData.m_cbData = 2
pData.m_iIndex = self.m_iIndex
table.insert(self.m_Data, pData)
self.m_iIndex = self.m_iIndex + 1
-- 这样排序就不会有问题了,精灵就不会互换了。
table.sort(self.m_Data,
function(a,b)
if a.m_cbData==b.m_cbData then
return a.m_iIndex < b.m_iIndex
else
return a.m_cbData<b.m_cbData
end
end)
3)函数参数传递问题
function hanshu(Data, iCount) -- Data为表 iCount为数值
<span style="white-space:pre"> </span>Data = {}
for i = #Data+1, #Data + 5 do
Data[i] = i
iCount = iCount + 1
end
end
----------------------
local Data = {1,2,3,4,5,6,7,8,9,10}
local iCount = 10
hanshu(Data, iCount)
for i = 1, #Data do
print(Data[i])
end
print(iCount)
这个结果是多少呢?->1,2,3,4,5 10
1、为什么不是1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 15
因为:Data = {} 这一句的意思是把原来的表释放,创建一个新表所以:#Data的大小为0 值为1,2,3,4,5
2、为什么iCount不是15 或者 5 呢?
因为:iCount传递给函数的是值的拷贝,而不是引用修改:
function hanshu(Data, iCount) -- Data为表 iCount为数值
for i = #Data+1, #Data + 5 do
Data[i] = i
iCount = iCount + 1
end
return iCount
end
4)os.time时间戳问题
os.time使用范围1970-1-1到2038-1-1(unix系统中范围)
local tmMember = os.time({
year = MyUserData:GetMemberOverDate().wYear,
month = MyUserData:GetMemberOverDate().wMonth,
day = MyUserData:GetMemberOverDate().wDay,
hour = MyUserData:GetMemberOverDate().wHour,
min = MyUserData:GetMemberOverDate().wMinute,
sec = MyUserData:GetMemberOverDate().wSecond})
year = MyUserData:GetMemberOverDate().wYear,
month = MyUserData:GetMemberOverDate().wMonth,
day = MyUserData:GetMemberOverDate().wDay,
hour = MyUserData:GetMemberOverDate().wHour,
min = MyUserData:GetMemberOverDate().wMinute,
sec = MyUserData:GetMemberOverDate().wSecond})
如果使用的日期不在这个范围内,在win32上是正常的,移植到Android手机上就会出错
四)2个类的写法
1)普通的继承类
e.g.local A = class("A", function() return Layer:create() end)
--init()
function A:init()
print("A:init")
local m_visibleSize = cc.Director:getInstance():getVisibleSize()
self.m_pSprite = cc.Sprite:create(a)
self:addChild(self.m_pSprite, 0)
end
--function SetTexture
function A:SetTexture(strPngName)
print("A:SetTexture")
self.m_pSprite:setTexture(strPngName)
end
--ctor()
function A:ctor()
print("A:ctor")
self:ignoreAnchorPointForPosition(false)
self:init()
end
--create( ... )
function A_createLayer( ... )
print("A_createLayer")
local layer = A.new()
return layer
end
------------------------------------
local m_ALayer = A_createLayer()
self:addChild(m_ALayer, 0)
m_ALayer:SetTexture(b)
说明:
1、local A = class("A", function() return Layer:create() end) 类A继承Layer可以使用Layer里所有公有方法
2、init()相当于cocos2d-x init函数
3、SetTexture 类的普通成员函数,创建A类的对象m_ALayer可以调用
4、ctor 类A的构造函数
2)单例模式
e.g.A = class("A", nil)
function A:new(o)
print("A:new")
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end
function A:GetInstance()
if self.instance == nil then
self.instance = self:new()
self:Reset()
end
return self.instance
end
function A:ReleaseInstance()
print("A:ReleaseInstance")
if self.instance then
<span style="white-space:pre"> </span>self.instance = nil
end
end
function A:GetA()
return self
end
function A:Reset()
-- clear()
self.dwUserID = 0
self.dwGameID = 0
self.dwGroupID = 0
self.szNickName = ""
self.szGroupName = ""
self.szUnderWrite = ""
self.wFaceID = 0
self.dwCustomID = 0
end
-- 成员方法
...
function A:SetUserID(dwUserID)
self.dwUserID = dwUserID
end
function A:GetUserID()
return self.dwUserID
end
-------------------------------
local m_AInstance = A:GetInstance():GetA()
m_AInstance.dwUserID = 1101
m_AInstance.dwGameID = 1120
m_AInstance.szNickName = "test001"
....
m_AInstance.dwCustomID = 100
A:GetInstance():SetUserID(1102)
print(A:GetInstance():GetUserID())