本次的游戏代码是使用lua完成,所以接入第三方的SDK也和c++层的方法稍有区别,首先来说明整体的思路。我们是在Android应用平台接入的微信分享,所以在这个平台下,也就是java层实现接入逻辑,留下给c++层调用的接口,c++层通过jni来调用java层实现业务逻辑的接口,而我们是在Lua中完成的游戏逻辑,所以我们还需要做的一个步骤就是,将c++的接口导出给lua层来使用,这个过程需要使用lua-binding。

Lua小游戏别撞车——接入微信分享

前俩个过程我在博客用3.0实现飞机大战——接入微信分享有详细的说明,请大家参考这篇博客,我们把整个接入流程再走一遍。

1、将项目导入eclipse的工程中,添加微信的SDK,在微信的开发者中心申请接入的应用,获得APPID。

2、实现java层的接入逻辑,在AppActivity文件中实现了一些函数,应该是和lua有关的,不用管。在onCreate中调用注册代码,将用到的成员变量和接口函数写在appActivity文件中。以下是涉及到调用微信的代码,其中有一个Util类,在上面给的博客地址中有说明,注意包名写对了。

//微信SDK
	private static final String APP_ID = "wx42ca6be8e52eebfb";
	private static IWXAPI api;
	private static AppActivity instance;

	static String hostIPAdress="0.0.0.0";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);

		//注册到微信
		regToWX();

		if(nativeIsLandScape()) {
			setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
		} else {
			setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
		}

		//2.Set the format of window

		// Check the wifi is opened when the native is debug.
		if(nativeIsDebug())
		{
			getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
			if(!isNetworkConnected())
			{
				AlertDialog.Builder builder=new AlertDialog.Builder(this);
				builder.setTitle("Warning");
				builder.setMessage("Open Wifi for debuging...");
				builder.setPositiveButton("OK",new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
						finish();
						System.exit(0);
					}
				});
				builder.setCancelable(false);
				builder.show();
			}
		}
		hostIPAdress = getHostIpAddress();
	}

	//初始化微信
	private void regToWX(){
	    api = WXAPIFactory.createWXAPI(this, APP_ID, true);
	    api.registerApp(APP_ID);
	}

	//留下给c++层调用的接口
	public static void sendMsgToFriend(){
	    if(api.openWXApp())
	    {
	    		//初始化一个对象WebpageObject
	        WXTextObject textObject = new WXTextObject();
	        textObject.text = "小塔博客用lua写的小游戏别撞车,博客地址http://www.zaojiahua.com/tag/3-0%E9%A3%9E%E6%9C%BA%E5%A4%A7%E6%88%98!";

	        //媒体对象中
	        WXMediaMessage msg = new WXMediaMessage(textObject);
	        msg.title = "别撞车";
	        msg.description = "分享给你的好友,让更多的人来玩!";

	        //建立请求对象
	        SendMessageToWX.Req req = new SendMessageToWX.Req();
	        //transaction是用来表示一个请求的唯一标示符
	        req.transaction = buildTransaction("textObject");
	        req.message = msg;
	        req.scene = SendMessageToWX.Req.WXSceneSession;

	        //使用通信类发送
	        api.sendReq(req);
	    }
	    else
	    {
	         Toast.makeText(instance, "启动微信失败!", Toast.LENGTH_SHORT).show();
	    }
	}

	public static void sendMsgToTimeLine(){
	    if(api.openWXApp())
	    {
	        if(api.getWXAppSupportAPI() >= 0x21020001)
	        {
	            WXWebpageObject webpage = new WXWebpageObject();
	            webpage.webpageUrl = "http://m.baidu.com/app?action==content&docid=6561264&f=s1001";

	            WXMediaMessage msg = new WXMediaMessage(webpage);
	            msg.title = "别撞车";
	            msg.description = "分享到我的朋友圈,让更多的人来玩!";

	            Bitmap thumb = BitmapFactory.decodeResource(instance.getResources(),R.drawable.icon);
	            msg.thumbData = Util.bmpToByteArray(thumb, true);

	            SendMessageToWX.Req req = new SendMessageToWX.Req();
	            req.transaction = buildTransaction("webpage");
	            req.message = msg;
	            req.scene = SendMessageToWX.Req.WXSceneTimeline;
	            api.sendReq(req);
	        }
	        else{
	            Toast.makeText(instance, "微信版本过低", Toast.LENGTH_SHORT).show();
	        }
	    }
	    else
	    {
	         Toast.makeText(instance, "启动微信失败", Toast.LENGTH_SHORT).show();
	    }
	}
	private static String buildTransaction(final String type) {
	    return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
	}

Lua小游戏别撞车——接入微信分享

3、接下来实现c++层的逻辑,从这里开始才是本篇博客重点要说明的。首先需要打开c++层的代码,我是在Mac下写的代码,所以打开我的Xcode工程,工程位置如图所示,如果没有frameworks文件夹,可以采用如图的操作。

Lua小游戏别撞车——接入微信分享 Lua小游戏别撞车——接入微信分享

之前我们写的圆周运动类是在classes文件夹下,注册是在appDelegate中完成的,这种方法在我的博客绑定自定义类到Runtime(Lua-binding)中有详细的说明,本篇博客采用另一种更加简单的方法,但是思路是不变的,采用这种方法以后扩展比较容易,再换一种方式来做可以更好的理解绑定的机制。

在frameworks/cocos2d-x/cocos目录下新建custom文件夹,顾名思义就是自定义的一些类,以后我们可以将自己定义的类全部放到这个文件夹中,在这个文件夹中新建类WeixinShare,然后在工程中导入这个类,导入路径如下。

Lua小游戏别撞车——接入微信分享 Lua小游戏别撞车——接入微信分享

以下是俩个静态函数的实现。

#ifndef __ColorBlind__WeixinShare__
#define __ColorBlind__WeixinShare__

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

#include "platform/android/jni/JniHelper.h"
#include <jni.h>

#endif

#include "Cocos2d.h"
USING_NS_CC;

class CC_DLL WeixinShare
{
public:
    static void sendToFriend();
    static void sendToTimeLine();
};

#endif /* defined(__DontCrash__WeixinShare__) */
#include "WeixinShare.h"

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

#include "platform/android/jni/JniHelper.h"
#include <jni.h>

#endif

void WeixinShare::sendToFriend()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
    JniMethodInfo minfo;

    bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/lua/AppActivity","sendMsgToFriend", "()V");

    if (!isHave) {
        log("jni:sendMsgToFriend is null");
    }else{
        //调用此函数
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID);
    }
#endif
}

void WeixinShare::sendToTimeLine()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
    JniMethodInfo minfo;

    bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/lua/AppActivity","sendMsgToTimeLine", "()V");

    if (!isHave) {
        log("jni:sendMsgToTimeLine is null");
    }else{
        //调用此函数
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID);
    }
#endif
}

这样的话c++层的代码也完成了,大家可以编译一下工程,看看是否有错误。如果没有错误我们就导出给lua使用。

4、进入tools/tolua目录,复制一份cocos2dx.ini配置文件,改名为cocos2dx_custom.ini文件,然后修改这个文件,主要注意的地方如图所示。

Lua小游戏别撞车——接入微信分享Lua小游戏别撞车——接入微信分享

这样我们自己导出类的配置文件就OK啦,然后打开genbinding.py文件,添加一行新的配置文件内容。

Lua小游戏别撞车——接入微信分享

接着在控制台下执行脚本genbinding.py。

Lua小游戏别撞车——接入微信分享 Lua小游戏别撞车——接入微信分享

这样在auto目录下我们就成功的得到了桥接函数lua_cocos2dx_custom_auto,现在我们将这个桥接函数导入到我们的工程中,之所以要导入是为了让整个项目在编译的时候编译这些文件。在lua_cocos2dx_custom_auto.cpp中,对WeixinShare.h的包含要加入目录名。现在编译整个工程,如果编译成功代表导出的就没有问题,我们才能进行下一步。

Lua小游戏别撞车——接入微信分享 Lua小游戏别撞车——接入微信分享

现在只差最后一步注册了,之前的函数注册是在appDelegate中完成的,这次的注册和cocos2d-x的注册函数放到一起完成,路径在cocos/scripting/lua-bindings/manual/CCLuaStack.cpp文件中,需要先包含头文件,所谓的注册就是调用桥接函数的头文件中所声明的那个函数。

Lua小游戏别撞车——接入微信分享Lua小游戏别撞车——接入微信分享

函数注册完毕以后我们需要重新编译一下c++层的代码,你可以在Xcode中编译,也可以在IDE中build一下runtime,俩者的道理是一样的,都是重新编译c++层代码。不过需要注意的是,如果是在Xcode工程中编译的代码,在IDE中可能需要选择一下使用最新编译出来的runtime,如图所示。整个lua-binding的过程就是这样,这么做的方便是以后如果你写很多的自定义类那么只需要在custom目录下新建文件,在init中新增自己的类名,然后运行genbinding.py脚本就会生成绑定的接口了,不再需要注册自己的多个导出函数了,刚才的这些步骤刚开始做是比较麻烦,不过为以后再导出自定义的类省下不少功夫。

Lua小游戏别撞车——接入微信分享

5、下面我们就使用导出的函数来完成程序的功能,先对lua层的代码稍作修改,添加调用分享的菜单项。

--游戏介绍以后的处理
function MainGameScene:gameOver(layer)
    --清除数据
    GlobalData:reset()
    self.score:setString("0")
    --重新设置小车的位置
    self:resetCar()

    --添加分享和更多菜单项
    self:addButton(layer)
end

function MainGameScene:addButton(layer)
    local share = cc.MenuItemImage:create("share.png","","")
    share:setScale(0.8)
    local more = cc.MenuItemImage:create("more.png","","")
    more:setScale(0.8)
    local retry = cc.MenuItemImage:create("retry.png","","")
    retry:setScale(0.8)
    local menu = cc.Menu:create(share,retry,more)
    menu:alignItemsHorizontallyWithPadding(self.size.width*0.1)
    menu:setPosition(cc.p(self.size.width/2,self.size.height/2))
    layer:addChild(menu,3)

    --callback
    local retry_callback = function()
        --播放音效
        SoundDeal:playEffect(EffectType.Start)
        retry:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),
            cc.CallFunc:create(function()menu:removeFromParentAndCleanup(true) end)))
        self:runRedCar(self.car_red)
        self:runYelloCar(self.car_yello)
        self.play = true
        self:collision(layer)
    end
    local share_callback = function()
        share:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),cc.ScaleTo:create(0.05,0.8)))
        if cc.Application:getInstance():getTargetPlatform() == cc.PLATFORM_OS_ANDROID then
            cc.WeixinShare:sendToFriend()
        end
    end
    local more_callback = function()
        more:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),cc.ScaleTo:create(0.05,0.8)))
    end
    retry:registerScriptTapHandler(retry_callback)
    share:registerScriptTapHandler(share_callback)
    more:registerScriptTapHandler(more_callback)
    --判断游戏是否开始的变量
    self.play = false
end

通过cc.WeixinShare:sendToFriend()调用c++层导出来的接口,注意要判断一下平台。最后需要做的就是打包apk,来测试我们写的程序是否正确。在IDE中导出包的过程中,你可能会遇到类没有实现的错误,因为我们添加了自定义的类却没有告诉编译器啊,在之前我们把自定义的类添加到Xcode工程中就是告诉编译器要编译我们新建的类,现在的平台是Android,我们要怎么告诉编译器编译我们的类呢,那就是修改.mk文件。我们需要修改俩处文件,一个是将WeixinShare.cpp添加到.mk文件中,另一个是将导出的桥接函数lua_cocos2dx_custom_auto添加到.mk文件中,这俩个mk文件的位置如图所示。

Lua小游戏别撞车——接入微信分享 Lua小游戏别撞车——接入微信分享Lua小游戏别撞车——接入微信分享Lua小游戏别撞车——接入微信分享

通过这几个步骤我们就完成了微信的分享功能,这个过程刚开始比较复杂,大家在完成了某一个阶段以后最好测试一下是否正确,出现错误仔细排查一下,只要认真一些一定可以导出成功的,最后微信分享的截图如下。

Lua小游戏别撞车——接入微信分享