原文 http://www.zaojiahua.com/lua-callback-functions.html
最近做一个小项目,是用Lua写的,中间用到了很多的回调,基本Cocos中的那几种常用回调都用到了,为了不做一个无脑的码农,打算写篇博客,总结一下,刚刚学习Lua,望大家指教!
1、菜单按钮的回调。这二者的回调是这么实现的,新建一个菜单或者是按钮,为了点击菜单或者按钮以后实现程序的逻辑,我们需要为菜单和按钮来绑定一个回调函数,于是乎,我们有了以下的代码。
2 |
local function item1_callback() |
4 |
local gameScene = require ( "GameScene" ) |
5 |
cc.Director:getInstance():replaceScene(gameScene:createScene()) |
7 |
local function item2_callback() |
9 |
local aboutScene = require ( "AboutScene" ) |
10 |
cc.Director:getInstance():pushScene(aboutScene:createScene()) |
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) |
17 |
local menu = cc.Menu:create(item1,item2) |
18 |
menu:alignItemsVerticallyWithPadding(size.height*0.15) |
在这里我们使用了一个重要的回调注册函数registerScriptTapHandler,Tap就是按下的意思,所以调用这个函数的时候是对菜单和按钮进行绑定的,这个函数的调用者是菜单项,需要传入一个参数,这个参数就是绑定的函数。接下来我们看下绑定的函数,我绑定的函数并没有接受任何传递过来的参数,这个就是Lua,你可以选择接受或者不接受,但是参数就在那里。为了看看这货是什么鸟,我们写上俩个参数,打印一下,发现一个值是-1,一个值是UserData,有图为证。
2 |
local function item1_callback(para1,para2) |
5 |
local gameScene = require ( "GameScene" ) |
6 |
cc.Director:getInstance():replaceScene(gameScene:createScene()) |
打印的结果是-1和userdata,userdata我们可以理解,你绑定的谁那么就传递过来谁嘛,但是第一个传递过来的参数是-1肿么回事?原来第一个参数代表的是tag,如果你没有给你的菜单项设置tag,那么它就是-1,如果设置了tag,那么这个值就是tag的值,OK,这样的话就清楚了。以下的代码是添加button的代码,一共添加了三个button,大家可以参考一下。
2 |
function GameScene:addButton(layer) |
4 |
local function scale9_normal() |
5 |
return cc.Scale9Sprite:create( "buttonBackground.png" ) |
7 |
local function scale9_press() |
8 |
return cc.Scale9Sprite:create( "buttonHighlighted.png" ) |
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) |
15 |
local button1 = cc.ControlButton:create(self.button1_label,scale9_normal()) |
16 |
button1:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN) |
18 |
button1:setPosition(self.size.width*0.2,self.size.height*0.2) |
20 |
local button2 = cc.ControlButton:create(self.button2_label,scale9_normal()) |
21 |
button2:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN) |
23 |
button2:setPosition(self.size.width*0.5,self.size.height*0.2) |
25 |
local button3 = cc.ControlButton:create(self.button3_label,scale9_normal()) |
26 |
button3:setBackgroundSpriteForState(scale9_press(),cc.CONTROL_EVENTTYPE_TOUCH_DOWN) |
28 |
button3:setPosition(self.size.width*0.8,self.size.height*0.2) |
31 |
local button_callback = function (ref,button) |
33 |
if self.correct == ref:getTag() then |
34 |
self:correct_callback() |
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) |
46 |
layer:addChild(button1) |
47 |
layer:addChild(button2) |
48 |
layer:addChild(button3) |
2、添加Android返回键的响应代码,Android返回键同样需要回调函数来做这件事情,在Cocos中使用的是事件监听和分发机制,所以我们需要先注册一下事件类型,然后绑定回调,代码如下。
2 |
local key_listener = cc.EventListenerKeyboard:create() |
4 |
local function key_return() |
6 |
cc.Director:getInstance():endToLua() |
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) |
5 |
local go_action = function () |
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) |
10 |
return handleNodeEvent(evt->data); |
13 |
case kMenuClickedEvent: |
15 |
return handleMenuClickedEvent(evt->data); |
20 |
return handleCallFuncActionEvent(evt->data); |
25 |
return handleScheduler(evt->data); |
30 |
return handleTouchEvent(evt->data); |
35 |
return handleTouchesEvent(evt->data); |
40 |
return handleKeypadEvent(evt->data); |
43 |
case kAccelerometerEvent: |
45 |
return handleAccelerometerEvent(evt->data); |
50 |
return handleCommonEvent(evt->data); |
55 |
return handlerControlEvent(evt->data); |
sendEvent顾名思义,这段代码就是向Lua层来分发回调事件的代码,在这段代码中,我们看到了很多熟悉的身影,比如说node的分发,menu的分发,schedule的分发,touch的分发等等都在这里,但是却没有事件监听器的分发,没错,事件监听器的回调Lua并没有写在这个文件中,而是在其他的文件中处理的,大家可以自行研究。我们以node回调Lua层的代码为例,看看是怎么回调Lua层的函数的,关键的代码在这里。
1 |
int action = *(( int *)(basicScriptData->value)); |
5 |
_stack->pushString( "enter" ); |
9 |
_stack->pushString( "exit" ); |
12 |
case kNodeOnEnterTransitionDidFinish: |
13 |
_stack->pushString( "enterTransitionFinish" ); |
16 |
case kNodeOnExitTransitionDidStart: |
17 |
_stack->pushString( "exitTransitionStart" ); |
21 |
_stack->pushString( "cleanup" ); |
27 |
int ret = _stack->executeFunctionByHandler(handler, 1); |
根据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) |
6 |
layerFarm:registerScriptHandler(onNodeEvent) |
所以以后Lua中实现onEnter,onExit函数知道怎么做了吧。好吧,本篇博客就到这里,刚刚研究Lua,做一点小笔记,大神勿喷!
- 合宙模块LUA相关资料汇总
1. 目录 1. 目录 [2. LUA二次开发](#2. LUA二次开发) 2.1 [新手教程](#2.1 新手教程) 2.2 [进阶教程](#2.2 进阶教程) 2.3 [LUA开发环境](#2.3 ...
- lua相关笔记
--[[ xpcall( 调用函数, 错误捕获函数 ); lua提供了xpcall来捕获异常 xpcall接受两个参数:调用函数.错误处理函数. 当错误发生时,Lua会在栈释放以前调用错误处理函数,因 ...
- [cocos2dx] lua注册回调到c++
思路 像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法. 这里也一样.核心思路是,当c代码执行到特定特定情形的时候,调用lua的方法 我这里使用的是用lua_stack直 ...
- linux安装lua相关编译报错
1.报之类的错误 /usr/lib/libreadline.so: undefined reference to `PC' /usr/lib/libreadline.so: undefined ref ...
- 如何在cocos2dx lua的回调函数里面用self
回调里的self是另一个不同的东西了,通常是触发回调的对象,或_G或nil ,视情况而定 我的 print(self) 输出 userdata function MyClass:sayFromCall ...
- lua 函数回调技巧
技巧1: local a = {}; function b() print("Hello World") end a["sell"] = {callFunc = ...
- 优雅的使用sublime写lua~ sublime lua相关必装插件推荐~~
缘起 lua脚本语言虽好,代码写得飞快,可是写错了调试起来却很困难,lua使用者经常容易犯得一个错误是--写错变量名了,if end 嵌套太多没匹配~,多打了一个逗号, 假设定义了一个变量 local ...
- 001 Lua相关链接
Lua官网:http://www.lua.org/ Lua for windows地址:http://www.lua.org/download.html Lua教程:http://www.runoob ...
- lua相关的小知识
lua的特性 1. 轻量级:一标准的C语言编写原发开放,编译后仅仅100K,占用内存小: 2. 扩展性:Lua提供了非常已于使用的扩展口和机制: 3. 支持面向过程编程和函数式编程 lua的数据类型 ...
随机推荐
- 3.3.5 boolean类型
boolean(布尔)类型有两个值:false 和 true ,用来判定逻辑条件.与Python不同的是,Java中的boolean值与整型值之间进行互相转换. Python中Tu ...
- 【Codeforces 924C】Riverside Curio
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 设第i天总共的线数为t[i] 水平线上线数为m[i]是固定的 水平线下的线数设为d[i] 则d[i]+m[i]+1=t[i] 也就是说问题可以 ...
- 51. spring boot属性文件之多环境配置【从零开始学Spring Boot】
原本这个章节是要介绍<log4j多环境不同日志级别的控制的>但是没有这篇文章做基础的话,学习起来还是有点难度的,所以我们先一起了解下spring boot属性文件之多环境配置,当然文章中也 ...
- RabbitMQ-基本概念(一)
整体架构模型 Producer 消息生产者,生产者创建消息然后发布到RabbitM中,消息一般包含2个部分 消息体(payload)和标签 消息体就是带有业务逻辑结构的数据,消息标签用来表述这条消息, ...
- github & Front-end JavaScript frameworks
github & Front-end JavaScript frameworks https://github.com/collections/front-end-javascript-fra ...
- noip模拟赛 铺瓷砖
[问题描述]有一面很长很长的墙. 你需要在这面墙上贴上两行瓷砖. 你的手头有两种不同尺寸的瓷砖, 你希望用这两种瓷砖各贴一行.瓷砖的长可以用分数表示,贴在第一行的每块瓷砖长度为A/B贴在第二行的每块瓷 ...
- 如何用js往html页面拼接一个div包括div的各种常用属性
$("#div").append("<table><tr align='center'>" +"<td >&quo ...
- codevs——1979 第K个数
1979 第K个数 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 给定一个长度为N(0<n<=10 ...
- 晶振虚焊导致TI 28335 DSP 烧写FLASH后,连接仿真器时正常工作,拔掉仿真器却不能启动运行
遇到个诡异的问题,28335的DSP,之前程序调试一切正常,但是烧写FLASH后,拔掉仿真器却始终部工作. 解决思路: 1) 检查配置文件貌似没什么问题,复制到其他工程,在开发板上拔掉仿真器启动正常. ...
- Oracle Multitenant Environment (三) Plan for a cdb
Below tables contains contant you need to consider while planning for a CDB. Action Considerations f ...