本次的游戏代码是使用lua完成,所以接入第三方的SDK也和c++层的方法稍有区别,首先来说明整体的思路。我们是在Android应用平台接入的微信分享,所以在这个平台下,也就是java层实现接入逻辑,留下给c++层调用的接口,c++层通过jni来调用java层实现业务逻辑的接口,而我们是在Lua中完成的游戏逻辑,所以我们还需要做的一个步骤就是,将c++的接口导出给lua层来使用,这个过程需要使用lua-binding。
前俩个过程我在博客用3.0实现飞机大战——接入微信分享有详细的说明,请大家参考这篇博客,我们把整个接入流程再走一遍。
1、将项目导入eclipse的工程中,添加微信的SDK,在微信的开发者中心申请接入的应用,获得APPID。
2、实现java层的接入逻辑,在AppActivity文件中实现了一些函数,应该是和lua有关的,不用管。在onCreate中调用注册代码,将用到的成员变量和接口函数写在appActivity文件中。以下是涉及到调用微信的代码,其中有一个Util类,在上面给的博客地址中有说明,注意包名写对了。
2 | private static final String APP_ID = "wx42ca6be8e52eebfb" ; |
3 | private static IWXAPI api; |
4 | private static AppActivity instance; |
6 | static String hostIPAdress= "0.0.0.0" ; |
8 | protected void onCreate(Bundle savedInstanceState) { |
10 | super .onCreate(savedInstanceState); |
15 | if (nativeIsLandScape()) { |
16 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); |
18 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); |
26 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
27 | if (!isNetworkConnected()) |
29 | AlertDialog.Builder builder= new AlertDialog.Builder( this ); |
30 | builder.setTitle( "Warning" ); |
31 | builder.setMessage( "Open Wifi for debuging..." ); |
32 | builder.setPositiveButton( "OK" , new DialogInterface.OnClickListener() { |
35 | public void onClick(DialogInterface dialog, int which) { |
36 | startActivity( new Intent(Settings.ACTION_WIFI_SETTINGS)); |
41 | builder.setCancelable( false ); |
45 | hostIPAdress = getHostIpAddress(); |
49 | private void regToWX(){ |
50 | api = WXAPIFactory.createWXAPI( this , APP_ID, true ); |
51 | api.registerApp(APP_ID); |
55 | public static void sendMsgToFriend(){ |
59 | WXTextObject textObject = new WXTextObject(); |
63 | WXMediaMessage msg = new WXMediaMessage(textObject); |
65 | msg.description = "分享给你的好友,让更多的人来玩!" ; |
68 | SendMessageToWX.Req req = new SendMessageToWX.Req(); |
70 | req.transaction = buildTransaction( "textObject" ); |
72 | req.scene = SendMessageToWX.Req.WXSceneSession; |
79 | Toast.makeText(instance, "启动微信失败!" , Toast.LENGTH_SHORT).show(); |
83 | public static void sendMsgToTimeLine(){ |
86 | if (api.getWXAppSupportAPI() >= 0x21020001 ) |
88 | WXWebpageObject webpage = new WXWebpageObject(); |
91 | WXMediaMessage msg = new WXMediaMessage(webpage); |
93 | msg.description = "分享到我的朋友圈,让更多的人来玩!" ; |
95 | Bitmap thumb = BitmapFactory.decodeResource(instance.getResources(),R.drawable.icon); |
96 | msg.thumbData = Util.bmpToByteArray(thumb, true ); |
98 | SendMessageToWX.Req req = new SendMessageToWX.Req(); |
99 | req.transaction = buildTransaction( "webpage" ); |
101 | req.scene = SendMessageToWX.Req.WXSceneTimeline; |
105 | Toast.makeText(instance, "微信版本过低" , Toast.LENGTH_SHORT).show(); |
110 | Toast.makeText(instance, "启动微信失败" , Toast.LENGTH_SHORT).show(); |
113 | private static String buildTransaction( final String type) { |
114 | return (type == null ) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis(); |
3、接下来实现c++层的逻辑,从这里开始才是本篇博客重点要说明的。首先需要打开c++层的代码,我是在Mac下写的代码,所以打开我的Xcode工程,工程位置如图所示,如果没有frameworks文件夹,可以采用如图的操作。
之前我们写的圆周运动类是在classes文件夹下,注册是在appDelegate中完成的,这种方法在我的博客绑定自定义类到Runtime(Lua-binding)中有详细的说明,本篇博客采用另一种更加简单的方法,但是思路是不变的,采用这种方法以后扩展比较容易,再换一种方式来做可以更好的理解绑定的机制。
在frameworks/cocos2d-x/cocos目录下新建custom文件夹,顾名思义就是自定义的一些类,以后我们可以将自己定义的类全部放到这个文件夹中,在这个文件夹中新建类WeixinShare,然后在工程中导入这个类,导入路径如下。
以下是俩个静态函数的实现。
1 | #ifndef __ColorBlind__WeixinShare__ |
2 | #define __ColorBlind__WeixinShare__ |
4 | #if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) |
6 | #include "platform/android/jni/JniHelper.h" |
14 | class CC_DLL WeixinShare |
17 | static void sendToFriend(); |
18 | static void sendToTimeLine(); |
21 | #endif /* defined(__DontCrash__WeixinShare__) */ |
1 | #include "WeixinShare.h" |
3 | #if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) |
5 | #include "platform/android/jni/JniHelper.h" |
10 | void WeixinShare::sendToFriend() |
12 | #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 |
15 | bool isHave = JniHelper::getStaticMethodInfo(minfo, "org/cocos2dx/lua/AppActivity" , "sendMsgToFriend" , "()V" ); |
18 | log ( "jni:sendMsgToFriend is null" ); |
21 | minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID); |
26 | void WeixinShare::sendToTimeLine() |
28 | #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 |
31 | bool isHave = JniHelper::getStaticMethodInfo(minfo, "org/cocos2dx/lua/AppActivity" , "sendMsgToTimeLine" , "()V" ); |
34 | log ( "jni:sendMsgToTimeLine is null" ); |
37 | minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID); |
这样的话c++层的代码也完成了,大家可以编译一下工程,看看是否有错误。如果没有错误我们就导出给lua使用。
4、进入tools/tolua目录,复制一份cocos2dx.ini配置文件,改名为cocos2dx_custom.ini文件,然后修改这个文件,主要注意的地方如图所示。
这样我们自己导出类的配置文件就OK啦,然后打开genbinding.py文件,添加一行新的配置文件内容。
接着在控制台下执行脚本genbinding.py。
这样在auto目录下我们就成功的得到了桥接函数lua_cocos2dx_custom_auto,现在我们将这个桥接函数导入到我们的工程中,之所以要导入是为了让整个项目在编译的时候编译这些文件。在lua_cocos2dx_custom_auto.cpp中,对WeixinShare.h的包含要加入目录名。现在编译整个工程,如果编译成功代表导出的就没有问题,我们才能进行下一步。
现在只差最后一步注册了,之前的函数注册是在appDelegate中完成的,这次的注册和cocos2d-x的注册函数放到一起完成,路径在cocos/scripting/lua-bindings/manual/CCLuaStack.cpp文件中,需要先包含头文件,所谓的注册就是调用桥接函数的头文件中所声明的那个函数。
函数注册完毕以后我们需要重新编译一下c++层的代码,你可以在Xcode中编译,也可以在IDE中build一下runtime,俩者的道理是一样的,都是重新编译c++层代码。不过需要注意的是,如果是在Xcode工程中编译的代码,在IDE中可能需要选择一下使用最新编译出来的runtime,如图所示。整个lua-binding的过程就是这样,这么做的方便是以后如果你写很多的自定义类那么只需要在custom目录下新建文件,在init中新增自己的类名,然后运行genbinding.py脚本就会生成绑定的接口了,不再需要注册自己的多个导出函数了,刚才的这些步骤刚开始做是比较麻烦,不过为以后再导出自定义的类省下不少功夫。
5、下面我们就使用导出的函数来完成程序的功能,先对lua层的代码稍作修改,添加调用分享的菜单项。
2 | function MainGameScene:gameOver(layer) |
5 | self.score:setString( "0" ) |
13 | function MainGameScene:addButton(layer) |
14 | local share = cc.MenuItemImage:create( "share.png" , "" , "" ) |
16 | local more = cc.MenuItemImage:create( "more.png" , "" , "" ) |
18 | local retry = cc.MenuItemImage:create( "retry.png" , "" , "" ) |
20 | local menu = cc.Menu:create(share,retry,more) |
21 | menu:alignItemsHorizontallyWithPadding(self.size.width*0.1) |
22 | menu:setPosition(cc.p(self.size.width/2,self.size.height/2)) |
23 | layer:addChild(menu,3) |
26 | local retry_callback = function () |
28 | SoundDeal:playEffect(EffectType.Start) |
29 | retry:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3), |
30 | cc.CallFunc:create( function ()menu:removeFromParentAndCleanup( true ) end ))) |
31 | self:runRedCar(self.car_red) |
32 | self:runYelloCar(self.car_yello) |
36 | local share_callback = function () |
37 | share:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),cc.ScaleTo:create(0.05,0.8))) |
38 | if cc.Application:getInstance():getTargetPlatform() == cc.PLATFORM_OS_ANDROID then |
39 | cc.WeixinShare:sendToFriend() |
42 | local more_callback = function () |
43 | more:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),cc.ScaleTo:create(0.05,0.8))) |
45 | retry:registerScriptTapHandler(retry_callback) |
46 | share:registerScriptTapHandler(share_callback) |
47 | more:registerScriptTapHandler(more_callback) |
通过cc.WeixinShare:sendToFriend()调用c++层导出来的接口,注意要判断一下平台。最后需要做的就是打包apk,来测试我们写的程序是否正确。在IDE中导出包的过程中,你可能会遇到类没有实现的错误,因为我们添加了自定义的类却没有告诉编译器啊,在之前我们把自定义的类添加到Xcode工程中就是告诉编译器要编译我们新建的类,现在的平台是Android,我们要怎么告诉编译器编译我们的类呢,那就是修改.mk文件。我们需要修改俩处文件,一个是将WeixinShare.cpp添加到.mk文件中,另一个是将导出的桥接函数lua_cocos2dx_custom_auto添加到.mk文件中,这俩个mk文件的位置如图所示。
通过这几个步骤我们就完成了微信的分享功能,这个过程刚开始比较复杂,大家在完成了某一个阶段以后最好测试一下是否正确,出现错误仔细排查一下,只要认真一些一定可以导出成功的,最后微信分享的截图如下。