原文 http://www.zaojiahua.com/lua-callback-functions.html

最近做一个小项目,是用Lua写的,中间用到了很多的回调,基本Cocos中的那几种常用回调都用到了,为了不做一个无脑的码农,打算写篇博客,总结一下,刚刚学习Lua,望大家指教!

1、菜单按钮的回调。这二者的回调是这么实现的,新建一个菜单或者是按钮,为了点击菜单或者按钮以后实现程序的逻辑,我们需要为菜单和按钮来绑定一个回调函数,于是乎,我们有了以下的代码。

1 --定义菜单项的回调函数
2     local function item1_callback()
3         --切换场景
4         local gameScene = require("GameScene")
5         cc.Director:getInstance():replaceScene(gameScene:createScene())
6     end
7     local function item2_callback()
8         --切换场景
9         local aboutScene = require("AboutScene")
10         cc.Director:getInstance():pushScene(aboutScene:createScene())
11     end
12     local item1 = cc.MenuItemLabel:create(cc.Label:createWithTTF("开始游戏","fonts/menu.ttf",42))
13     item1:registerScriptTapHandler(item1_callback)
14     local item2 = cc.MenuItemLabel:create(cc.Label:createWithTTF("关于游戏","fonts/menu.ttf",42))
15     item2:registerScriptTapHandler(item2_callback)
16     --创建菜单
17     local menu = cc.Menu:create(item1,item2)
18     menu:alignItemsVerticallyWithPadding(size.height*0.15)
19  
20     --添加菜单到游戏中
21     layer:addChild(menu)

在这里我们使用了一个重要的回调注册函数registerScriptTapHandler,Tap就是按下的意思,所以调用这个函数的时候是对菜单和按钮进行绑定的,这个函数的调用者是菜单项,需要传入一个参数,这个参数就是绑定的函数。接下来我们看下绑定的函数,我绑定的函数并没有接受任何传递过来的参数,这个就是Lua,你可以选择接受或者不接受,但是参数就在那里。为了看看这货是什么鸟,我们写上俩个参数,打印一下,发现一个值是-1,一个值是UserData,有图为证。

1 --定义菜单项的回调函数
2     local function item1_callback(para1,para2)
3         print(para1,para2)
4         --切换场景
5         local gameScene = require("GameScene")
6         cc.Director:getInstance():replaceScene(gameScene:createScene())
7     end

打印的结果是-1和userdata,userdata我们可以理解,你绑定的谁那么就传递过来谁嘛,但是第一个传递过来的参数是-1肿么回事?原来第一个参数代表的是tag,如果你没有给你的菜单项设置tag,那么它就是-1,如果设置了tag,那么这个值就是tag的值,OK,这样的话就清楚了。以下的代码是添加button的代码,一共添加了三个button,大家可以参考一下。

1 --添加按钮
2 function GameScene:addButton(layer)
3     --创建button
4     local function scale9_normal()
5         return cc.Scale9Sprite:create("buttonBackground.png")
6     end
7     local function scale9_press()
8         return cc.Scale9Sprite:create("buttonHighlighted.png")
9     end
10     --文本信息
11     self.button1_label = cc.Label:createWithTTF("Ready","fonts/label.TTF",32)
12     self.button2_label = cc.Label:createWithTTF("Ready","fonts/label.TTF",32)
13     self.button3_label = cc.Label:createWithTTF("Ready","fonts/label.TTF",32)
14     --button1
15     local button1 = cc.ControlButton:create(self.button1_label,scale9_normal())
16     button1:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
17     button1:setTag(1)
18     button1:setPosition(self.size.width*0.2,self.size.height*0.2)
19     --button2
20     local button2 = cc.ControlButton:create(self.button2_label,scale9_normal())
21     button2:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
22     button2:setTag(2)
23     button2:setPosition(self.size.width*0.5,self.size.height*0.2)
24     --button3
25     local button3 = cc.ControlButton:create(self.button3_label,scale9_normal())
26     button3:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN)
27     button3:setTag(3)
28     button3:setPosition(self.size.width*0.8,self.size.height*0.2)
29  
30     --设置button的回调函数
31     local button_callback = function (ref,button)
32         --如果正确跟新分数
33         if self.correct == ref:getTag() then
34             self:correct_callback()
35         else
36             self:wrong_callback()
37         end
38     end
39  
40     --设置回调函数
41     button1:registerControlEventHandler(button_callback,cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
42     button2:registerControlEventHandler(button_callback,cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
43     button3:registerControlEventHandler(button_callback,cc.CONTROL_EVENTTYPE_TOUCH_UP_INSIDE)
44  
45     --添加到层中
46     layer:addChild(button1)
47     layer:addChild(button2)
48     layer:addChild(button3)
49 end

2、添加Android返回键的响应代码,Android返回键同样需要回调函数来做这件事情,在Cocos中使用的是事件监听和分发机制,所以我们需要先注册一下事件类型,然后绑定回调,代码如下。

1 --监听手机返回键
2     local key_listener = cc.EventListenerKeyboard:create()
3     --返回键回调
4     local function key_return()
5         --结束游戏
6         cc.Director:getInstance():endToLua()
7     end
8     --lua中得回调,分清谁绑定,监听谁,事件类型是什么
9     key_listener:registerScriptHandler(key_return,cc.Handler.EVENT_KEYBOARD_RELEASED)
10     local eventDispatch = layer:getEventDispatcher()
11     eventDispatch:addEventListenerWithSceneGraphPriority(key_listener,layer)

这次我们使用的注册回调函数是registerScriptHandler,没有Tap,显然就不是为菜单和按钮回调准备的,所以这个函数是用来注册一般的回调函数所使用的。其中注释有句话说的比较好,就是一定要明白谁绑定,监听谁,事件类型是什么,在这个例子看来,是事件监听器去绑定了一个回调函数key_return,然后事件的类型是放到了一个Handler表中的,我们的事件类型是keyBoard,可以到Handler中看看都有哪些事件类型,然后所有你熟悉的东西都会看到了,比如以下的代码所代表的事件类型。

1 local listener = cc.EventListenerTouchOneByOne:create()
2     listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN )
3     listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED )
4     listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED )
5     local eventDispatcher = layerFarm:getEventDispatcher()
6     eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layerFarm)

3、动作的回调,3.x以后在c++层的动作回调函数只剩下Callfunc和CallfuncN了,其他的都可以用c++11的std::bind解决,而在Lua中就剩下一个了,这个是由Lua的特性决定的,用法如下。

1 --ready和go的回调函数 函数可以有俩个参数,第一个代表接受动作的对象,就是谁执行了动作,第二个是传过来的参数,全部放到了表中
2     local  ready_action = function(actionSelf,tab)
3  
4     end
5     local go_action = function()
6  
7     end
8     cc.CallFunc:create(ready_action,{x=1})

CallFunc中有俩个参数,一个是绑定的回调函数,另一个是table表,这个表中可以存放一些命名参数,把你想要传递的东西统统通过这个表传递过去,然后在回调函数中,我们有俩个参数来接受,第一个当然是动作的执行者了,第二个就是传递过来的这张表,里边有所有我们要得参数,是不是很高大上啊!

4、看完了以上的这些东西,我们基本明白了怎么使用,所谓知其然更要知其所以然,这些回调函数是怎么分发到Lua层的代码中的,我们就需要看一个文件了。打开你引擎目录的cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp文件,我们需要找到这样一处代码。

1 int LuaEngine::sendEvent(ScriptEvent* evt)
2 {
3     if (NULL == evt)
4         return 0;
5  
6     switch (evt->type)
7     {
8         case kNodeEvent:
9             {
10                return handleNodeEvent(evt->data);
11             }
12             break;
13         case kMenuClickedEvent:
14             {
15                 return handleMenuClickedEvent(evt->data);
16             }
17             break;
18         case kCallFuncEvent:
19             {
20                 return handleCallFuncActionEvent(evt->data);
21             }
22             break;
23         case kScheduleEvent:
24             {
25                 return handleScheduler(evt->data);
26             }
27             break;
28         case kTouchEvent:
29             {
30                 return handleTouchEvent(evt->data);
31             }
32             break;
33         case kTouchesEvent:
34             {
35                 return handleTouchesEvent(evt->data);
36             }
37             break;
38         case kKeypadEvent:
39             {
40                 return handleKeypadEvent(evt->data);
41             }
42             break;
43         case kAccelerometerEvent:
44             {
45                 return handleAccelerometerEvent(evt->data);
46             }
47             break;
48         case kCommonEvent:
49             {
50                 return handleCommonEvent(evt->data);
51             }
52             break;
53         case kControlEvent:
54             {
55                 return handlerControlEvent(evt->data);
56             }
57             break;
58         default:
59             break;
60     }
61  
62     return 0;
63 }

sendEvent顾名思义,这段代码就是向Lua层来分发回调事件的代码,在这段代码中,我们看到了很多熟悉的身影,比如说node的分发,menu的分发,schedule的分发,touch的分发等等都在这里,但是却没有事件监听器的分发,没错,事件监听器的回调Lua并没有写在这个文件中,而是在其他的文件中处理的,大家可以自行研究。我们以node回调Lua层的代码为例,看看是怎么回调Lua层的函数的,关键的代码在这里。

1 int action = *((int*)(basicScriptData->value));
2     switch (action)
3     {
4         case kNodeOnEnter:
5             _stack->pushString("enter");
6             break;
7  
8         case kNodeOnExit:
9             _stack->pushString("exit");
10             break;
11  
12         case kNodeOnEnterTransitionDidFinish:
13             _stack->pushString("enterTransitionFinish");
14             break;
15  
16         case kNodeOnExitTransitionDidStart:
17             _stack->pushString("exitTransitionStart");
18             break;
19  
20         case kNodeOnCleanup:
21             _stack->pushString("cleanup");
22             break;
23  
24         default:
25             return 0;
26     }
27     int ret = _stack->executeFunctionByHandler(handler, 1);
28     _stack->clean();

根据action的类型,我们将不同的字符串压入了栈中,然后执行了executeFunctionByHandler函数,第一个参数handler就是回调的函数,第二个参数代表传递到回调函数中的参数个数。其他的回调Lua层的函数大同小异,大家自行研究吧。那么在Lua层,这个回调函数是怎么被注册的呢?我们新建工程由系统创建了一个GameScene.lua文件,这个文件中的这段代码,不知道大家是否留意过。

1 local function onNodeEvent(event)
2         if "exit" == event then
3             cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.schedulerID)
4         end
5     end
6     layerFarm:registerScriptHandler(onNodeEvent)

所以以后Lua中实现onEnter,onExit函数知道怎么做了吧。好吧,本篇博客就到这里,刚刚研究Lua,做一点小笔记,大神勿喷!

 

Lua相关回调总结【转】的更多相关文章

  1. 合宙模块LUA相关资料汇总

    1. 目录 1. 目录 [2. LUA二次开发](#2. LUA二次开发) 2.1 [新手教程](#2.1 新手教程) 2.2 [进阶教程](#2.2 进阶教程) 2.3 [LUA开发环境](#2.3 ...

  2. lua相关笔记

    --[[ xpcall( 调用函数, 错误捕获函数 ); lua提供了xpcall来捕获异常 xpcall接受两个参数:调用函数.错误处理函数. 当错误发生时,Lua会在栈释放以前调用错误处理函数,因 ...

  3. [cocos2dx] lua注册回调到c++

    思路 像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法. 这里也一样.核心思路是,当c代码执行到特定特定情形的时候,调用lua的方法 我这里使用的是用lua_stack直 ...

  4. linux安装lua相关编译报错

    1.报之类的错误 /usr/lib/libreadline.so: undefined reference to `PC' /usr/lib/libreadline.so: undefined ref ...

  5. 如何在cocos2dx lua的回调函数里面用self

    回调里的self是另一个不同的东西了,通常是触发回调的对象,或_G或nil ,视情况而定 我的 print(self) 输出 userdata function MyClass:sayFromCall ...

  6. lua 函数回调技巧

    技巧1: local a = {}; function b() print("Hello World") end a["sell"] = {callFunc = ...

  7. 优雅的使用sublime写lua~ sublime lua相关必装插件推荐~~

    缘起 lua脚本语言虽好,代码写得飞快,可是写错了调试起来却很困难,lua使用者经常容易犯得一个错误是--写错变量名了,if end 嵌套太多没匹配~,多打了一个逗号, 假设定义了一个变量 local ...

  8. 001 Lua相关链接

    Lua官网:http://www.lua.org/ Lua for windows地址:http://www.lua.org/download.html Lua教程:http://www.runoob ...

  9. lua相关的小知识

    lua的特性 1. 轻量级:一标准的C语言编写原发开放,编译后仅仅100K,占用内存小: 2. 扩展性:Lua提供了非常已于使用的扩展口和机制: 3. 支持面向过程编程和函数式编程 lua的数据类型 ...

随机推荐

  1. 洛谷 1328 生活大爆炸版石头剪刀布(NOIp2014提高组)

    [题解] 简单粗暴的模拟题. #include<cstdio> #include<algorithm> #include<cstring> #define LL l ...

  2. [luoguP2387] 魔法森林(LCT + 并查集)

    传送门 并查集真是一个判断连通的好东西! 连通性用并查集来搞. 把每一条边按照 a 为关键字从小到大排序. 那么直接枚举,动态维护 b 的最小生成树 用 a[i] + 1 ~ n 路径上最大的 b[i ...

  3. noip模拟赛 洗澡

    分析:首先肯定是要用线性筛把素数全部给筛出来的,然后可以维护一个前缀和数组记录1~i个素数的和,对于每一个询问可以从n到1+k枚举它的右端点,然后利用前缀和统计一个长度为K的区间和,看看是不是满足条件 ...

  4. 最小生成树 I - Agri-Net

    Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet c ...

  5. Project导入错误 36D27C48

    做后台系统导出Project时,部署到服务器提示:检索 COM 类工厂中 CLSID 为 {36D27C48-A1E8-11D3-BA55-00C04F72F325} 的组件失败,原因是出现以下错误: ...

  6. P3372 【模板】线段树 1 洛谷

    https://www.luogu.org/problem/show?pid=3372 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 ...

  7. 用API中的raf复制文件图片等及系统找不到指定的文件的解决办法

    该运行是在eclipse中进行的操作,小白的基础理解,如有不妥之处,请大佬们指正.QQ:1055802635 package raf; import java.io.IOException;impor ...

  8. Python基础--高速改造:字符串

    Python的字符串值得一说. 先看: >>>"Hello world!" 'Hello world!' 我们写是双引號,可是打印出来后是单引號. 差别何在? 答 ...

  9. Android studio 解决setText中文乱码问题

    我在用Android Studio编译器的时候,总会遇到非常多乱码的问题.第一个乱码问题是在Layout文件中面定义了EditText.在代码中须要将获取到的内容填充到EditText里面,这时候假设 ...

  10. Eclipse导入外部项目问题总结

     此次在项目开发过程中导入从oksvn下载的共享项目时出现几个项目在不同的IDE导入导出时的问题,为免忘记做例如以下笔记: 1 类路径问题 在Java开发中大多数的开发人员使用的IDE是MyEcl ...