怎么用cocos2d-x lua开发游戏
在游戏开发领域,游戏公司采用脚本语言如Lua、Python等进行开发是较为常见的做法。然而,许多开发者对这些脚本语言并不熟悉。本篇文章将向大家简单介绍脚本的用途以及在Cocos2d-x中的基础用法。
Lua与Python简介
对于Lua和Python的详细信息,不太熟悉的开发者可以自行查阅百度百科。这两种脚本语言在游戏开发中都较为常用,与Python相比,Lua更为轻量级。本文选择讲解Lua,主要有两个原因:其一,Cocos2d-x游戏引擎内嵌了Lua;其二,自“愤怒的小鸟”这款游戏火爆之后,国内游戏开发领域倾向于使用Lua进行开发。
脚本的用途
1. 作为游戏逻辑的核心
在手游开发中,脚本扮演着“大脑”的角色。游戏中所有与逻辑相关的代码通常都存放在脚本里,而客户端(前台)的代码则类似于“肢体”,也可以看作是“播放器”,其主要功能是向用户展示UI界面。此外,脚本还可用于处理地图数据等其他任务。
2. 便于游戏更新与维护
在手机网游中,脚本的作用尤为关键。以一款名为“Himi”的网游为例,如果该游戏未使用脚本,当1.0版本发布后,若发现客户端存在棘手的Bug需要修复,就必须等待更新客户端并重新提交发布才能解决问题。这样不仅会导致大量用户流失,而且每次游戏更新都会造成部分用户的流失。
但如果“Himi”使用脚本,解决此类问题就会变得非常简单。假设游戏中的逻辑代码都存放在脚本a.lua中,当a.lua的逻辑出现问题时,我们只需将修复后的a.lua脚本更新至服务器即可。一般来说,脚本都会定义版本号,例如有Bug的a.lua版本号为1.0,修复后的版本号可改为1.1。当用户每次启动游戏时,客户端会将本地脚本的版本号与服务器脚本的版本号进行对比。若服务器端脚本的版本号更新,客户端会自动下载并覆盖当前脚本,从而解决问题。此外,游戏中的活动更新、图片替换等操作也可以通过更新脚本即时完成,无需每次修改前端代码都重新发布新的游戏版本,避免了不必要的损失。
在Cocos2d-x中使用Lua脚本
新建项目与运行示例
我们可以通过新建一个Cocos2d-x Lua模板项目来开始。该模板默认包含一个示例,开发者可以直接运行项目查看效果。可能会有开发者疑惑,在项目的类文件中找不到相关代码。这是因为所有的逻辑代码都存放在Lua脚本中,项目启动后会直接解析名为hello.lua的脚本。
打开项目的Resources文件夹,我们可以找到hello.lua和hello2.lua两个脚本文件。Cocos2d-x Lua示例脚本相对简单,但对于不太熟悉的开发者来说可能仍有疑惑。以下是一份简单的示例脚本代码,开发者可以将其复制到hello.lua中查看效果:
require "hello2" -- 包含hello2这个脚本
-- 注释语句
-- 基本上调用的cocos2d-x函数和类的时候就是以cocos2d.*这样子来用
-- 注意2:function关键字定义函数,end结束函数
-- 打印
cocos2d.CCLuaLog("脚本hello开始运行... " .. myadd(3, 5))
-- 创建一个Scene
sceneForWorld = cocos2d.CCScene:node()
-- 创建一个Layer
layerForWorld = cocos2d.CCLayer:node()
sceneForWorld:addChild(layerForWorld)
-- 创建一个精灵
spriteForWorld = cocos2d.CCSprite:spriteWithFile("Icon.png")
layerForWorld:addChild(spriteForWorld)
-- 获取屏幕宽高
screenSize=cocos2d.CCDirector:sharedDirector():getWinSize()
-- 设置精灵坐标
spriteForWorld:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5))
-- 设置精灵缩放2倍
spriteForWorld:setScale(2)
-- 添加一个CCLabelTTF
myLableTTF = cocos2d.CCLabelTTF:labelWithString("Himi- Lua 基础","Helvetica-Bold",24)
myLableTTF:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5+100))
sceneForWorld:addChild(myLableTTF)
-- 添加一个CCLabelTTF
myLableTTF2 = cocos2d.CCLabelTTF:labelWithString("上面icon跟随用户触屏位置","Helvetica-Bold",24)
myLableTTF2:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5-100))
sceneForWorld:addChild(myLableTTF2)
-- 触摸事件
-- 开启触摸
layerForWorld:setIsTouchEnabled(true)
-- 注册触摸事件
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHBEGAN, "btnTouchBegin")
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHMOVED, "btnTouchMove")
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHENDED, "btnTouchEnd")
-- touch handlers
pointBegin = nil
function btnTouchBegin(e)
cocos2d.CCLuaLog("btnTouchBegin")
local v = e[1]
local pointMove = v:locationInView(v:view())
pointMove = cocos2d.CCDirector:sharedDirector():convertToGL(pointMove)
spriteForWorld:setPosition(cocos2d.CCPoint(pointMove.x,pointMove.y))
end
function btnTouchMove(e)
cocos2d.CCLuaLog("btnTouchMove")
local v = e[1]
local pointMove = v:locationInView(v:view())
pointMove = cocos2d.CCDirector:sharedDirector():convertToGL(pointMove)
spriteForWorld:setPosition(cocos2d.CCPoint(pointMove.x,pointMove.y))
end
function btnTouchEnd(e)
cocos2d.CCLuaLog("btnTouchEnd")
end
-- 触摸结束
-- 动态小狗
winSize = cocos2d.CCDirector:sharedDirector():getWinSize()
FrameWidth = 105
FrameHeight = 95
textureDog = cocos2d.CCTextureCache:sharedTextureCache():addImage("dog.png")
frame0 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, cocos2d.CCRectMake(0, 0, FrameWidth, FrameHeight))
frame1 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, cocos2d.CCRectMake(FrameWidth*1, 0, FrameWidth, FrameHeight))
spriteDog = cocos2d.CCSprite:spriteWithSpriteFrame(frame0)
spriteDog:setPosition(cocos2d.CCPoint(100, winSize.height/4*3))
layerForWorld:addChild(spriteDog)
animFrames = cocos2d.CCMutableArray_CCSpriteFrame__:new(2)
animFrames:addObject(frame0)
animFrames:addObject(frame1)
animation = cocos2d.CCAnimation:animationWithFrames(animFrames, 0.5)
animate = cocos2d.CCAnimate:actionWithAnimation(animation, false);
spriteDog:runAction(cocos2d.CCRepeatForever:actionWithAction(animate))
-- 自定义函数
function prForHimi()
cocos2d.CCLuaLog("reFresh function")
-- 取消选择器
--cocos2d.CCScheduler:sharedScheduler():unscheduleScriptFunc("prForHimi")
end
-- 使用选择器进行函数更新
--cocos2d.CCScheduler:sharedScheduler():scheduleScriptFunc("prForHimi", 1, false)
-- 循环语句
for i=0,4,1 do
for j=0,4,2 do
cocos2d.CCLuaLog("for loop",i)
end
end
-- 避免内存泄漏
collectgarbage( "setpause", 100)
collectgarbage( "setstepmul", 5000)
-- 播放背景音乐
--CocosDenshion.SimpleAudioEngine:sharedEngine():playBackgroundMusic("background.mp3", true)
-- 播放音效
--CocosDenshion.SimpleAudioEngine:sharedEngine():preloadEffect("effect1.wav")
-- run整个scene
cocos2d.CCDirector:sharedDirector():runWithScene(sceneForWorld)
cocos2d.CCLuaLog("脚本hello正常执行结束... " .. myadd(3, 5))
查找函数定义与参数
Lua脚本通常通过中间层(解析)与前端代码(Cocos2d-x封装的引擎类库)进行交互,因此很多方法名称可能会发生改变。对于不太熟悉的开发者来说,如何查找函数的定义和参数呢?
以添加CCLabelTTF为例,假如我们不确定其构造函数是否有修改,或者忘记了参数的具体内容,可以打开项目的libs文件夹,然后依次打开lua文件夹、cocos2dx_support文件夹,找到LuaCocos2d.cpp文件(注意该文件代码较多,打开可能较慢)。打开该文件后,我们可以看到很多方法的定义与实现。
若要查找CCLabelTTF的构造方法,可以搜索如下语句:
tolua_beginmodule(tolua_S,"CCLabelTTF");
在该类下方可以找到一大批类似的代码,这里是Lua与Cocos2d-x之间的转换函数定义。例如常用的CCLabelTTF构造函数:
tolua_function(tolua_S,"labelWithString",tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01);
该函数的第一个参数无需理会,第二个参数表示我们在使用Cocos2d-x时调用的函数名称,第三个参数是Lua与Cocos2d-x之间的转换函数实现代码。我们可以继续搜索第三个参数,或者按住Command键并点击第三个参数,找到其函数实现代码:
/* method: labelWithString of class cocos2d::CCLabelTTF */
#ifndef TOLUA_DISABLE_tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01
static int tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"cocos2d::CCLabelTTF",0,&tolua_err) ||
!tolua_isstring(tolua_S,2,0,&tolua_err) ||
!tolua_isstring(tolua_S,3,0,&tolua_err) ||
!tolua_isnumber(tolua_S,4,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,5,&tolua_err)
)
goto tolua_lerror;
else
{
const char* label = ((const char*) tolua_tostring(tolua_S,2,0));
const char* fontName = ((const char*) tolua_tostring(tolua_S,3,0));
float fontSize = ((float) tolua_tonumber(tolua_S,4,0));
{
cocos2d::CCLabelTTF* tolua_ret = (cocos2d::CCLabelTTF*) cocos2d::CCLabelTTF::labelWithString(label,fontName,fontSize);
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cocos2d::CCLabelTTF");
}
}
return 1;
tolua_lerror:
return tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
从这段代码中,我们可以清楚地看到函数的转换过程以及所需的参数。如果还不清楚,可以继续按住Command键并点击labelWithString,进入Cocos2d-x引擎代码CCLabelTTF.cpp中的该函数实现。
脚本与前端代码的交互过程
Lua脚本的解析过程可以简单概括为:解析Lua脚本中的一句代码 -> 通过解析层代码 -> 将其转换并转到前端代码进行使用。当然,这个过程也可能是可逆的,即前端代码 -> 通过解析层代码 -> 使用Lua脚本中的内容。不过,由于笔者对Cocos2d-x中的Lua还没有深入了解,不确定该过程是否完全可逆。
总结
对于开发者来说,熟悉脚本主要还是通过在实际项目中使用,逐步积累经验并熟练掌握。本文主要希望开发者能够记住脚本的概念和作用,为后续的学习和实践打下基础。