这次因为要在ios游戏里面接广告,所以就碰到了游戏层与OC层交互的问题,游戏是用lua写的,@H_502_4@相信很多朋友在使用cocos2d-x+lua开发游戏时都遇到过接入iOS原生SDK的问题,比如常见的接应用内支付SDK,广告SDK或是一些社交平台SDK等等。而我之前都没碰过这个,所以只能在度娘上找各种方法,爬了各种坑,最后终于让我成功的在lua里面调用了OC的方法,这篇文章主要是对我学习通过lua调用OC方法的一个总结,在这里分享给大家,希望对自己和大家的开发工作都有帮助。@H_502_4@@H_502_4@
因为我们游戏的逻辑主要是用Lua实现的,而SDK是用Objective-C实现,所以这里我们需要解决Lua与Objective-C的交互问题,即最终希望达到的目标是,在Lua层面“调用”Objective-C的代码(注意这里的调用是加引号的,间接的调用),而当Objective-C层面收到SDK的回调,再通知Lua。我们知道,Lua并没有简单的方法直接和Objective-C交流,但是Lua可以通过Lua Binding和C/C++交流,而我们又知道,C++和Objective-C可以混编,即C++可以直接调用(这里调用没引号,是真的直接调用)Objective-C的代码。想到这里,思路就很明显了,我们可以使用C++为Lua和Objective-C的交互充当桥梁,进而实现Lua到Objective-C的交互。@H_502_4@
@H_502_4@
根据上面的分析,我们可以用如下图表达我们的思路,我们这里将语言交互的过程分成了4个小部分:@H_502_4@
@H_502_4@@H_502_4@
首先就是解决lua和c++的交互:@H_502_4@@H_502_4@@H_502_4@
我们用x-code打开我们的游戏项目,在Classes文件夹下新建一个C++类,叫做@H_502_4@@H_502_4@MyCPPClass,我们的目的就是在lua层调用改C++类的方法@H_502_4@@H_502_4@
MyCPPClass.h头文件@H_502_4@
@H_502_4@@H_502_4@
#ifndef MyCPPClass_hpp@H_502_4@@H_502_4@
#define MyCPPClass_hpp@H_502_4@@H_502_4@
#include @H_502_4@<stdio.h>@H_502_4@@H_502_4@
#include @H_502_4@"cocos2d.h"@H_502_4@@H_502_4@
#include @H_502_4@"test_lua_bind.h"@H_502_4@@H_502_4@
USING_NS_CC@H_502_4@;@H_502_4@@H_502_4@
class@H_502_4@ MyCPPClass@H_502_4@@H_502_4@
public@H_502_4@:@H_502_4@@H_502_4@
@H_502_4@static@H_502_4@@H_502_4@int@H_502_4@ test_lua_bind(@H_502_4@lua_State@H_502_4@ *L);@H_502_4@@H_502_4@
#endif @H_502_4@/* MyCPPClass_hpp */@H_502_4@@H_502_4@
lua调用c++的方法的参数必须为(lua_State@H_502_4@*L),要使用lua_State,我们就必须包含头文件test_lua_bind(这是我们自己写的,不是系统自带的),所以我们在classes文件夹下创建test_lua_bind.h头文件@H_502_4@@H_502_4@@H_502_4@
test_lua_bind.h头文件
@H_502_4@@H_502_4@@H_502_4@
#include @H_502_4@<iostream>@H_502_4@@H_502_4@
extern@H_502_4@@H_502_4@"C"@H_502_4@ {@H_502_4@@H_502_4@
#include @H_502_4@"lua.h"@H_502_4@@H_502_4@
#include @H_502_4@"lualib.h"@H_502_4@@H_502_4@
因为lua调用c++方法,而c++方法内部与OC混编,所以MyCPPClass.cpp文件的后缀要改为.mm@H_502_4@@H_502_4@@H_502_4@@H_502_4@
我们先来创建一个OC类,方便MyCPPClass.mm文件中与OC的混编与交互@H_502_4@@H_502_4@@H_502_4@@H_502_4@@H_502_4@
我们在ios文件夹下创建一个OC类,命名为@H_502_4@@H_502_4@MyObject,我们的目的就是要调用@H_502_4@MyObject的方法,现在我们来创建@H_502_4@MyObject类@H_502_4@@H_502_4@
#import @H_502_4@<Foundation/Foundation.h>@H_502_4@@H_502_4@
@interface@H_502_4@ MyObject :@H_502_4@NSObject@H_502_4@@H_502_4@
//+ @H_502_4@表示类方法@H_502_4@ -@H_502_4@表示实例方法@H_502_4@@H_502_4@
+(@H_502_4@int@H_502_4@) method1:(@H_502_4@NSString@H_502_4@*)pszMsg title:(@H_502_4@NSString@H_502_4@*)pszTitle;@H_502_4@@H_502_4@
+(@H_502_4@NSString@H_502_4@*) method2;@H_502_4@@H_502_4@
MyObject.m@H_502_4@
@H_502_4@@H_502_4@
#import @H_502_4@"MyObject.h"@H_502_4@@H_502_4@
@implementation@H_502_4@ MyObject@H_502_4@@H_502_4@
+(@H_502_4@int@H_502_4@) method1:(@H_502_4@NSString@H_502_4@*)pszMsg title:(@H_502_4@NSString@H_502_4@*)pszTitle@H_502_4@@H_502_4@
@H_502_4@NSLog@H_502_4@(@H_502_4@@"%@ %@"@H_502_4@,pszMsg,pszTitle);@H_502_4@@H_502_4@
@H_502_4@return@H_502_4@@H_502_4@10@H_502_4@;@H_502_4@@H_502_4@
+(@H_502_4@NSString@H_502_4@*) method2@H_502_4@@H_502_4@
@H_502_4@return@H_502_4@@H_502_4@@"hello"@H_502_4@;@H_502_4@@H_502_4@
@H_502_4@
现在MyObject类就已经创建好了,接着来看MyCPPClass.mm文件是怎么做的@H_502_4@@H_502_4@
MyCPPClass.mm文件@H_502_4@@H_502_4@
#include @H_502_4@"MyCPPClass.hpp"@H_502_4@@H_502_4@
#include @H_502_4@"CCLuaEngine.h"@H_502_4@@H_502_4@
//@H_502_4@如果是@H_502_4@ios@H_502_4@平台,引入@H_502_4@MyObject.h@H_502_4@文件@H_502_4@@H_502_4@
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)@H_502_4@@H_502_4@
#include @H_502_4@"MyObject.h"@H_502_4@@H_502_4@
int@H_502_4@@H_502_4@MyCPPClass@H_502_4@::test_lua_bind(@H_502_4@lua_State@H_502_4@ *L)@H_502_4@@H_502_4@
@H_502_4@//@H_502_4@如果是@H_502_4@ios@H_502_4@平台@H_502_4@@H_502_4@
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)@H_502_4@@H_502_4@
@H_502_4@//iOS@H_502_4@代码@H_502_4@@H_502_4@调用@H_502_4@OC@H_502_4@的@H_502_4@MyObject@H_502_4@类的@H_502_4@method1@H_502_4@方法@H_502_4@@H_502_4@
@H_502_4@int@H_502_4@ result1=[@H_502_4@MyObject@H_502_4@@H_502_4@method1@H_502_4@:@H_502_4@@"cocos2dx@H_502_4@调用@H_502_4@oc@H_502_4@函数@H_502_4@"@H_502_4@@H_502_4@title@H_502_4@:@H_502_4@@"Himi"@H_502_4@];@H_502_4@@H_502_4@
@H_502_4@//@H_502_4@调用@H_502_4@OC@H_502_4@的@H_502_4@MyObject@H_502_4@类的@H_502_4@method2@H_502_4@方法@H_502_4@@H_502_4@在这个@H_502_4@MyCPPClass.mm@H_502_4@文件中,就实现了@H_502_4@c++@H_502_4@和@H_502_4@oc@H_502_4@的混编@H_502_4@@H_502_4@
@H_502_4@const@H_502_4@@H_502_4@char@H_502_4@* result2=[[@H_502_4@MyObject@H_502_4@@H_502_4@method2@H_502_4@] @H_502_4@UTF8String@H_502_4@];@H_502_4@@H_502_4@
@H_502_4@//Android@H_502_4@代码@H_502_4@@H_502_4@
@H_502_4@printf@H_502_4@(@H_502_4@"result %d\n"@H_502_4@,result1);@H_502_4@@H_502_4@
@H_502_4@printf@H_502_4@(@H_502_4@"result %s\n"@H_502_4@,result2);@H_502_4@@H_502_4@
@H_502_4@//@H_502_4@使用@H_502_4@lua_tonumber@H_502_4@、@H_502_4@lua_tostring@H_502_4@等函数,来取得传入的参数,比如@H_502_4@lua_tonumber(L,1)@H_502_4@就是得到传入的第一个参数,且类型为数字@H_502_4@@H_502_4@
@H_502_4@int@H_502_4@ number =@H_502_4@lua_tonumber@H_502_4@(L,@H_502_4@1@H_502_4@);@H_502_4@@H_502_4@
@H_502_4@printf@H_502_4@(@H_502_4@"haha1 %d\n"@H_502_4@,number);@H_502_4@@H_502_4@
number = number +@H_502_4@1@H_502_4@;@H_502_4@@H_502_4@
@H_502_4@printf@H_502_4@(@H_502_4@"haha2 %d\n"@H_502_4@,number);@H_502_4@@H_502_4@
@H_502_4@//@H_502_4@使用@H_502_4@lua_pushnumber@H_502_4@、@H_502_4@lua_pushstring@H_502_4@等函数,来将返回值压入@H_502_4@Lua@H_502_4@的环境中,因为@H_502_4@Lua@H_502_4@支持函数返回多个值,所以可以@H_502_4@push@H_502_4@多个返回值进@H_502_4@Lua@H_502_4@环@H_502_4@,@H_502_4@最终函数返回的数字表示有多少个返回值被压入了@H_502_4@Lua@H_502_4@环境@H_502_4@ number@H_502_4@为返回值@H_502_4@@H_502_4@
@H_502_4@lua_pushnumber@H_502_4@(L,number);@H_502_4@@H_502_4@
@H_502_4@return@H_502_4@@H_502_4@1@H_502_4@;@H_502_4@@H_502_4@
最后我们要在lua层调用test_lua_bind方法,我们就必须去@H_502_4@@H_502_4@@H_502_4@AppDelegate那里注册:@H_502_4@
AppDelegate@H_502_4@::applicationDidFinishLaunching方法如下:@H_502_4@
@H_502_4@
bool@H_502_4@ @H_502_4@AppDelegate@H_502_4@::applicationDidFinishLaunching()@H_502_4@
{@H_502_4@
@H_502_4@// set default FPS@H_502_4@
@H_502_4@Director@H_502_4@::@H_502_4@getInstance@H_502_4@()->@H_502_4@setAnimationInterval@H_502_4@(@H_502_4@1.0@H_502_4@ / @H_502_4@60.0f@H_502_4@);@H_502_4@
@H_502_4@
@H_502_4@// register lua module@H_502_4@
@H_502_4@auto@H_502_4@ engine = @H_502_4@LuaEngine@H_502_4@::@H_502_4@getInstance@H_502_4@();@H_502_4@
@H_502_4@
@H_502_4@ScriptEngineManager@H_502_4@::@H_502_4@getInstance@H_502_4@()->@H_502_4@setScriptEngine@H_502_4@(engine);@H_502_4@
@H_502_4@
@H_502_4@LuaStack@H_502_4@* stack = engine->@H_502_4@getLuaStack@H_502_4@();@H_502_4@
@H_502_4@
stack->@H_502_4@setXXTEAKeyAndSign@H_502_4@(@H_502_4@"2dxLua"@H_502_4@,@H_502_4@strlen@H_502_4@(@H_502_4@"2dxLua"@H_502_4@),@H_502_4@"XXTEA"@H_502_4@,@H_502_4@strlen@H_502_4@(@H_502_4@"XXTEA"@H_502_4@));@H_502_4@
@H_502_4@
@H_502_4@lua_State@H_502_4@* L = stack->@H_502_4@getLuaState@H_502_4@();@H_502_4@
@H_502_4@
@H_502_4@//@H_502_4@使用@H_502_4@lua_register@H_502_4@宏定义来将这个函数注册进@H_502_4@Lua@H_502_4@环境,@H_502_4@Lua@H_502_4@脚本里就可以用它了@H_502_4@
@H_502_4@lua_register@H_502_4@(L,@H_502_4@"gameOverCallBack"@H_502_4@,MyCPPClass::test_lua_bind@H_502_4@);@H_502_4@
@H_502_4@
@H_502_4@lua_module_register@H_502_4@(L);@H_502_4@
@H_502_4@
@H_502_4@register_all_packages@H_502_4@();@H_502_4@
@H_502_4@
@H_502_4@if@H_502_4@ (engine->@H_502_4@executeScriptFile@H_502_4@(@H_502_4@"src/main.lua"@H_502_4@))@H_502_4@
{@H_502_4@
@H_502_4@return@H_502_4@ @H_502_4@false@H_502_4@;@H_502_4@
}@H_502_4@
@H_502_4@
@H_502_4@return@H_502_4@ @H_502_4@true@H_502_4@;@H_502_4@
}@H_502_4@
@H_502_4@
static@H_502_4@ @H_502_4@int@H_502_4@ register_all_packages()@H_502_4@
{@H_502_4@
@H_502_4@@H_502_4@extern@H_502_4@ @H_502_4@void@H_502_4@ package_quick_register();@H_502_4@
@H_502_4@@H_502_4@package_quick_register@H_502_4@();@H_502_4@
@H_502_4@@H_502_4@return@H_502_4@ @H_502_4@0@H_502_4@; @H_502_4@//flag for packages manager@H_502_4@
}@H_502_4@
现在我们就可以来测试一下,在MyApp.lua中的构造函数ctor里面调用test_lua_bind方法@H_502_4@@H_502_4@@H_502_4@
@H_502_4@
@H_502_4@
@H_502_4@
如果c++调用OC的方法,获取到返回值并且要反馈给lua层,此时就需要由c++去调用lua的方法,在这里推荐一篇这方面的不错的文章@H_502_4@
【cocos2d-x + Lua(2) C++和lua数据通讯之间的互调】@H_502_4@
下面是我在学习的过程中找到的几篇比较不错的文章,分享给大家@H_502_4@
[原创]cocos2d-x + Lua接入iOS原生SDK的实现方案@H_502_4@
Cocos2d-x下Lua调用自定义C++类和函数的最佳实践