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