学习Cocos2d-x Lua:Lua回调函数小结
在这个系列中,我们主要学习Cocos2d-x Lua,总结Lua开发过程中涉及的知识点,以及如何在开发过程中使用Cocos Code IDE。本文将对回调函数进行小结。
起因是最近用Lua做了一个小项目,项目中用到了很多回调,基本涵盖了Cocos中几种常用的回调类型。下面将针对这些用到的回调函数进行总结。
1、菜单按钮的回调
为了在点击菜单或按钮后实现程序逻辑,我们需要为菜单和按钮绑定回调函数。以下是实现代码:
-- 定义菜单项的回调函数
local function item1_callback()
-- 切换场景
local gameScene = require("GameScene")
cc.Director:getInstance():replaceScene(gameScene:createScene())
end
local function item2_callback()
-- 切换场景
local aboutScene = require("AboutScene")
cc.Director:getInstance():pushScene(aboutScene:createScene())
end
local item1 = cc.MenuItemLabel:create(cc.Label:createWithTTF("开始游戏", "fonts/menu.ttf", 42))
item1:registerScriptTapHandler(item1_callback)
local item2 = cc.MenuItemLabel:create(cc.Label:createWithTTF("关于游戏", "fonts/menu.ttf", 42))
item2:registerScriptTapHandler(item2_callback)
-- 创建菜单
local menu = cc.Menu:create(item1, item2)
menu:alignItemsVerticallyWithPadding(size.height * 0.15)
-- 添加菜单到游戏中
layer:addChild(menu)
这里使用了重要的回调注册函数 registerScriptTapHandler,“Tap” 表示按下,该函数用于为菜单和按钮绑定回调。其调用者是菜单项,需要传入一个参数,即绑定的函数。
在Lua中,绑定的函数可以选择接受或不接受传递的参数。为了探究传递的参数,我们为绑定的函数添加两个参数并打印:
-- 定义菜单项的回调函数
local function item1_callback(para1, para2)
print(para1, para2)
-- 切换场景
local gameScene = require("GameScene")
cc.Director:getInstance():replaceScene(gameScene:createScene())
end
打印结果为 -1 和 userdata。userdata 可以理解为绑定的对象,而第一个参数 -1 代表菜单项的 tag。如果没有为菜单项设置 tag,则其值为 -1;若设置了 tag,该值就是 tag 的值。
以下是添加三个按钮的代码,供大家参考:
-- 添加按钮
function GameScene:addButton(layer)
-- 创建button
local function scale9_normal()
return cc.Scale9Sprite:create("buttonBackground.png")
end
local function scale9_press()
return cc.Scale9Sprite:create("buttonHighlighted.png")
end
-- 文本信息
self.button1_label = cc.Label:createWithTTF("Ready", "fonts/label.TTF", 32)
self.button2_label = cc.Label:createWithTTF("Ready", "fonts/label.TTF", 32)
self.button3_label = cc.Label:createWithTTF("Ready", "fonts/label.TTF", 32)
-- button1
local button1 = cc.ControlButton:create(self.button1_label, scale9_normal())
button1:setBackgroundSpriteForState(scale9_press(), cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
button1:setTag(1)
button1:setPosition(self.size.width * 0.2, self.size.height * 0.2)
-- button2
local button2 = cc.ControlButton:create(self.button2_label, scale9_normal())
button2:setBackgroundSpriteForState(scale9_press(), cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
button2:setTag(2)
button2:setPosition(self.size.width * 0.5, self.size.height * 0.2)
-- button3
local button3 = cc.ControlButton:create(self.button3_label, scale9_normal())
button3:setBackgroundSpriteForState(scale9_press(), cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
button3:setTag(3)
button3:setPosition(self.size.width * 0.8, self.size.height * 0.2)
-- 设置button的回调函数
local button_callback = function(ref, button)
-- 如果正确跟新分数
if self.correct == ref:getTag() then
self:correct_callback()
else
self:wrong_callback()
end
end
-- 设置回调函数
button1:registerControlEventHandler(button_callback, cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
button2:registerControlEventHandler(button_callback, cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
button3:registerControlEventHandler(button_callback, cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
-- 添加到层中
layer:addChild(button1)
layer:addChild(button2)
layer:addChild(button3)
end
2、添加Android返回键的响应代码
Android返回键的响应需要使用回调函数,在Cocos中采用事件监听和分发机制。我们需要先注册事件类型,然后绑定回调,代码如下:
-- 监听手机返回键
local key_listener = cc.EventListenerKeyboard:create()
-- 返回键回调
local function key_return()
-- 结束游戏
cc.Director:getInstance():endToLua()
end
-- lua中得回调,分清谁绑定,监听谁,事件类型是什么
key_listener:registerScriptHandler(key_return, cc.Handler.EVENT_KEYBOARD_RELEASED)
local eventDispatch = layer:getEventDispatcher()
eventDispatch:addEventListenerWithSceneGraphPriority(key_listener, layer)
这里使用的注册回调函数是 registerScriptHandler,它不是为菜单和按钮回调准备的,而是用于注册一般的回调函数。在使用时,一定要明确谁绑定、监听谁以及事件类型是什么。在这个例子中,事件监听器绑定了回调函数 key_return,事件类型存放在 Handler 表中,事件类型为 keyBoard。我们可以查看 Handler 表,了解所有的事件类型。例如以下代码代表的事件类型:
local listener = cc.EventListenerTouchOneByOne:create()
listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED)
listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED)
local eventDispatcher = layerFarm:getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layerFarm)
3、动作的回调
在Cocos2d-x 3.x 以后,C++层的动作回调函数只剩下 Callfunc 和 CallfuncN,其他的可以用 C++11 的 std::bind 解决。而在Lua中,只剩下一个动作回调函数 CallFunc,其用法如下:
-- ready和go的回调函数 函数可以有俩个参数,第一个代表接受动作的对象,就是谁执行了动作,第二个是传过来的参数,全部放到了表中
local ready_action = function(actionSelf, tab)
end
local go_action = function()
end
cc.CallFunc:create(ready_action, {x = 1})
CallFunc 有两个参数,一个是绑定的回调函数,另一个是 table 表。该表可以存放命名参数,我们可以将想要传递的参数都放在这个表中。在回调函数中,有两个参数来接收,第一个是动作的执行者,第二个是传递过来的表,表中包含所有需要的参数。
4、小结
通过以上内容,我们基本了解了回调函数的使用方法。为了深入理解这些回调函数是如何分发到Lua层的代码中的,我们需要查看引擎目录下的 cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp 文件。以下是相关代码:
int LuaEngine::sendEvent(ScriptEvent* evt)
{
if (NULL == evt)
return 0;
switch (evt->type)
{
case kNodeEvent:
{
return handleNodeEvent(evt->data);
}
break;
case kMenuClickedEvent:
{
return handleMenuClickedEvent(evt->data);
}
break;
case kCallFuncEvent:
{
return handleCallFuncActionEvent(evt->data);
}
break;
case kScheduleEvent:
{
return handleScheduler(evt->data);
}
break;
case kTouchEvent:
{
return handleTouchEvent(evt->data);
}
break;
case kTouchesEvent:
{
return handleTouchesEvent(evt->data);
}
break;
case kKeypadEvent:
{
return handleKeypadEvent(evt->data);
}
break;
case kAccelerometerEvent:
{
return handleAccelerometerEvent(evt->data);
}
break;
case kCommonEvent:
{
return handleCommonEvent(evt->data);
}
break;
case kControlEvent:
{
return handlerControlEvent(evt->data);
}
break;
default:
break;
}
return 0;
}
sendEvent 函数用于向Lua层分发回调事件。在这段代码中,我们可以看到很多熟悉的分发类型,如 node、menu、schedule、touch 等,但没有事件监听器的分发。事件监听器的回调在其他文件中处理,大家可以自行研究。
以 node 回调Lua层的代码为例,看看如何回调Lua层的函数,关键代码如下:
int action = *((int*)(basicScriptData->value));
switch (action)
{
case kNodeOnEnter:
_stack->pushString("enter");
break;
case kNodeOnExit:
_stack->pushString("exit");
break;
case kNodeOnEnterTransitionDidFinish:
_stack->pushString("enterTransitionFinish");
break;
case kNodeOnExitTransitionDidStart:
_stack->pushString("exitTransitionStart");
break;
case kNodeOnCleanup:
_stack->pushString("cleanup");
break;
default:
return 0;
}
int ret = _stack->executeFunctionByHandler(handler, 1);
_stack->clean();
根据 action 的类型,将不同的字符串压入栈中,然后执行 executeFunctionByHandler 函数。第一个参数 handler 是回调的函数,第二个参数代表传递到回调函数中的参数个数。其他回调Lua层的函数原理类似,大家可以自行研究。
在Lua层,回调函数的注册示例如下。新建工程时,系统创建的 GameScene.lua 文件中有这样的代码:
local function onNodeEvent(event)
if "exit" == event then
cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.schedulerID)
end
end
layerFarm:registerScriptHandler(onNodeEvent)
通过以上内容,我们应该知道如何在Lua中实现 onEnter、onExit 等函数了。本文到此结束。