AnySDK项目实战教程

一、Cocos2d-x环境的搭建
关于Cocos2d-x的环境搭建,网上相关的教程已经有很多了,在此就简单的介绍一下。

1.1 所需工具及软件
1、http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html(1.7版本)
2、Android SDK(直接在Eclipse里下载所需要的SDK版本)
3、http://developer.android.com/sdk/index.html(r9d及以上)
4、http://ant.apache.org/bindownload.cgi
5、https://www.python.org/downloads/release/python-278/(2.7版本)
6、http://developer.android.com/tools/sdk/ndk/index.html
7、VS2012(Windows)或XCode(Mac OS)
8、https://github.com/cocos2d/cocos2d-x/tree/d1437540343dbf6c2ff4def708d60ed05bd3ec37(本项目所使用的版本)

1.2 配置环境
准备好如上软件后,配置好JDK与Python的环境变量,执行Cocos2d-x下的setup.py进行各个环境变量的设置,然后就可以使用cocos命令了。

例子:
创建工程:cocos new demo -p com.game.demo -l cpp -d d:\cocos
编译Android:cocos compile -p android -j 4

二、AnySDK环境的搭建
下载http://www.anysdk.com/downloads,解压文件夹,安装AnySDK打包客户端。详细使用可查看http://docs.anysdk.com/。

三、实战项目的搭建

3.1 项目源码下载
从github上下载https://github.com/AnySDK/EarthWarrior3D,由于cocos2d目录是引用Cocos2d-x-3.3alpha0的github,要确保有下载下来。(注意:Cocos2d-x 3.3alpha0中的plugin、bindings-generator、cocos2d-console也是引用)

由于external目录里的库并不完全,所以要运行cocos2d-x的download-deps.py下载external库。

3.2 项目搭建

  1. 将AnySDK的Framework导入项目
    打开之前下载好的AnySDK文件夹,在AnySDK_Framework_C++里有三个文件夹,查看项目工程里的Application.mk(这里应该是proj.android/jni/Application.mk这个文件)里的STL库类型APP_STL := gnustl_static,所以这里要使用的就是protocols_gnustl_static文件夹。

将protocols_gnustl_static文件夹复制到EarthWarrior3D的proj.android目录下,重命名为protocols。然后将其中的res文件夹里的东西放到proj.android的res目录里。

  1. 修改Android.mk文件配置Framework编译选项
    1、头文件目录里添加protocols下的android和include目录。
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \   
           $(LOCAL_PATH)/../protocols/android    \   
           $(LOCAL_PATH)/../protocols/include 

```
 
2、添加PluginProtocolStatic静态库 
 LOCAL_WHOLE_STATIC_LIBRARIES += PluginProtocolStatic 

```
 

3、引入模块库 
 $(call import-module,protocols/android) 

```
 


3. 添加新路径至ndk module path 
本项目使用build_native编译,所以修改build_native.py文件: 
 if platform == 'win32': 
    ndk_module_path = 'NDK_MODULE_PATH=%s;%s;%s/external;%s/cocos' % ("./",cocos_root, cocos_root, cocos_root) 
else: 
    ndk_module_path = 'NDK_MODULE_PATH=%s:%s:%s/external:%s/cocos' % ("./",cocos_root, cocos_root, cocos_root) 

```
 
如果使用cocos compile命令编译,则是修改build-cfg.json文件: 
 "ndk_module_path" : 
        "../cocos2d", 
        "../cocos2d/cocos", 
        "../cocos2d/external", 
        "" 
], 

```
 


4. 导入框架自带的的jar包并勾选export选项 
在Eclipse里导入游戏工程和libcocos2dx工程(cocos/platform/android/java),右键点击工程,选择Properties后选择Java Build Path,在面板上点击 Libraries,通过Add JARs...将libPluginProtocol.jar引进游戏工程。 
 
 

PS:如果使用cocos compile命令编译,要将libPluginProtocol.jar放到libs目录下,还需要在Application.mk里面添加一句NDK_TOOLCHAIN_VERSION=clang。 


5. 配置AndroidManifest.xml添加框架需要的权限 
  
 
 
 
 

```
 


四、项目程序编写 

4.1 初始化AnySDK Framework 

1. 初始化JavaVM 
首先,要在游戏工程加载jni的时候为AnySDK Framework设置JavaVM引用。先找到JNI_OnLoad函数,此函数是jni被加载时会首先被调用的函数。 

本项目工程将platform/android目录下的javaactivity.cpp的JNI_OnLoad函数注释掉,然后拷贝JNI_OnLoad函数到main.cpp里。 

1、导入头文件并声明命名空间。 
 #include "PluginJniHelper.h" 
using namespace anysdk::framework ; 

```
 
 

2、并且添加上javaVM代码。 
 PluginJniHelper::setJavaVM(vm); 

```
 
 


2. 在JAVA层初始化AnySDK Framework框架 
1、首先找到游戏工程的主Activity,本项目为AppActivity。 
2、然后重写Activity的onCreate()方法,并新增如下代码来初始化AnySDK Framework: 
 import com.anysdk.framework.PluginWrapper; 

protected void onCreate(Bundle savedState) { 
    super.onCreate(savedState); 
    PluginWrapper.init(this); 
} 

```
 

3、重写Activity生命周期相关方法,代码如下: 
 import android.content.Intent; 

@Override 
protected void onResume() { 
    PluginWrapper.onResume();     
    super.onResume(); 
} 
@Override 
public void onPause(){ 
    PluginWrapper.onPause(); 
    super.onPause(); 
} 
@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data){ 
    PluginWrapper.onActivityResult(requestCode, resultCode, data); 
    super.onActivityResult(requestCode, resultCode, data); 
} 
@Override 
protected void onNewIntent(Intent intent) { 
    PluginWrapper.onNewIntent(intent); 
    super.onNewIntent(intent); 
} 

```
 


3. 创建好需要的JAVA类和C++类 
创建新的C++的文件来编写AnySDK的逻辑代码,本项目创建了个PluginChannel类来实现用户系统和支付系统的功能,PluginChannel类需要继承UserActionListener和PayResultListener,重写其事件回调函数。 

JAVA创建一个wrapper类,用来调用C++的函数。在PluginChannel.cpp里定义wrapper的函数(nativeInitPlugins)。 

wrapper.java的定义: 
 public class wrapper { 
    public static native void nativeInitPlugins(); 
} 

```
 

PluginChannel.cpp里nativeInitPlugins的定义: 
 extern "C" 
{ 
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_wrapper_nativeInitPlugins(JNIEnv*  env, jobject thiz) 
    { 
        PluginChannel::getInstance()->loadPlugins(); 
    }  
} 

```
 


4. 在C++层初始化AnySDK Framework框架 
在PluginChannel的loadPlugins函数里编写初始化、加载插件、设置监听等逻辑,然后调用该函数来进行初始化。在onCreate函数里的PluginWrapper.init(this)之后添加如下代码来进行初始化: 
 wrapper.nativeInitPlugins(); 

```
 

在PluginChannel的loadPlugins函数里所要做的处理如下: 
1、初始化appKey、appSecret、privateKey、oauthLoginServer参数。 
 std::string oauthLoginServer = "http://oauth.anysdk.com/api/OauthLoginDemo/Login.php"; 
std::string appKey = "BC26F841-DAC5-9244-D025-759F49997A28"; 
std::string appSecret = "1dff378a8f254ec8ad4b492cae72381b"; 
std::string privateKey = "696064B29E9A0B7DDBD6FCB88F34A555"; 

_agent = AgentManager::getInstance(); 
_agent->init(appKey,appSecret,privateKey,oauthLoginServer); 

```
 

2、加载所有插件。 
 _agent->loadALLPlugin(); 

```
 

3、设置各个插件的Debug模式和监听等。 
 _pluginUser = _agent->getUserPlugin(); 
if(_pluginUser) 
{ 
    _pluginUser->setDebugMode(true); 
    _pluginUser->setActionListener(this); 
} 

_pluginsIAPMap  = _agent->getIAPPlugin(); 
std::map::iterator iter; 
for(iter = _pluginsIAPMap->begin(); iter != _pluginsIAPMap->end(); iter++) 
{ 
    (iter->second)->setDebugMode(true); 
    (iter->second)->setResultListener(this); 
} 

```
 


5. 编写PluginChannel类需要的函数 
PluginChannel类里的函数众多,这里就介绍下其中的几个函数。 

1、login函数,通过getUserPlugin判断下是否有存在用户插件,存在则调用login函数弹出登陆界面。 
 void PluginChannel::login() 
{ 
    if(_pluginUser) 
    { 
        _pluginUser->login(); 
    } 
} 

```
 

2、payment函数,因为AnySDK支持多种支付方式,所以通过getIAPPlugin获取所有的支付插件。使用map容器来添加支付所需的各种参数信息,调用支付插件的payForProduct进行支付。如果使用多种支付方式,还需要自己处理相关UI及界面。 
 void PluginChannel::payment() 
{ 
    if(_pluginsIAPMap) 
    { 
        std::map productInfo; 
        productInfo"Product_Price"] = "1"; 
        productInfo"Product_Id"] = "1"; 
        productInfo"Product_Name"] = "豌豆荚测试a1"; 
        productInfo"Server_Id"] = "13"; 
        productInfo"Product_Count"] = "1"; 
        productInfo"Role_Id"] = "1"; 
        productInfo"Role_Name"] = "1"; 
        productInfo"Role_Grade"] = "1"; 
        productInfo"Role_Balance"] = "1"; 

        std::map::iterator it = _pluginsIAPMap->begin(); 
        if(_pluginsIAPMap->size() == 1) 
        { 

            (it->second)->setDebugMode(true); 
            (it->second)->payForProduct(productInfo); 
        } 
        else if(_pluginsIAPMap->size() > 1) 
        { 

            //多支付,游戏开发商自己处理相关UI及界面 
        } 
    } 

} 

```
 

3、exit函数,通过isFunctionSupported判断下是否有存在exit函数,存在则调用函数来弹出退出界面。退出界面点击确定将回调kExitPage事件。 
 void PluginChannel::exit() 
{ 
    if(_pluginUser && _pluginUser->isFunctionSupported("exit")) 
    { 
        _pluginUser->callFuncWithParam("exit",NULL); 
    } 
} 

```
 

4、退出游戏处理,在接收到退出游戏的回调事件后,释放PluginChannel的内存,调用cocos2d-x的Director类的end函数结束游戏。 
 void PluginChannel::onActionResult(ProtocolUser* pPlugin, UserActionResultCode code, const char* msg) 
{ 
    switch(code) 
    { 
        case kExitPage://退出游戏回调 
            purge(); 
            Director::getInstance()->end(); 
            break; 
    } 
} 

```
 
在析构函数里销毁用户系统以及卸载所有插件。 
 PluginChannel::~PluginChannel() 
{ 
    destroy(); 
    unloadPlugins(); 
} 

void PluginChannel::destroy() 
{ 
    if(_pluginUser && _pluginUser->isFunctionSupported("destroy")) 
    { 
        _pluginUser->callFuncWithParam("destroy",NULL); 
    } 
} 

void PluginChannel::unloadPlugins() 
{ 
    _agent->unloadALLPlugin(); 
} 

```
 


6. 在游戏C++代码中调用PluginChannel类 
为了保证Win32工程可以正常运行,所有涉及AnySDK的代码都采用条件宏判断,在Win32工程下不进行编译。 

本项目只修改游戏工程的MainMenuScene类,也就是只在主菜单的场景里添加AnySDK的功能。 

1、添加PluginChannel头文件 
 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
#include "PluginChannel.h" 
#endif 

```
 

2、修改startgame_callback函数,点击START GAME的时候将会调用login函数来弹出登陆框。 
 void MainMenuScene::startgame_callback() 
{ 
    CCLOG("login"); 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
    CCLOG("PluginChannel"); 
    bReady = true; 
    PluginChannel::getInstance()->login();//调用渠道登陆 
#else 
    CCLOG("startgame_callback"); 
    CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(); 
    GameLayer::isDie=false; 
    auto scene = (LoadingScene::audioloaded) ? HelloWorld::createScene() :LoadingScene::createScene(); 
    Director::getInstance()->replaceScene(scene); 
    CCLOG("startgame_callback"); 
#endif 
     
} 

```
 

3、修改update函数,通过isLogined函数判断登陆状态,当状态为登陆则跳转场景开始游戏。 
 void MainMenuScene::update(float dt){ 
    pRate+=0.01; 
    plane->setPosition3D(Vec3(visible_size_macro.width/2+50,480-20*sin(1.05*pRate),0)); 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
    if(PluginChannel::getInstance()->isLogined()&&bReady)//判断登陆状态 
    { 
        CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(); 
        GameLayer::isDie=false; 
        auto scene = (LoadingScene::audioloaded) ? HelloWorld::createScene() :LoadingScene::createScene(); 
        Director::getInstance()->replaceScene(scene); 
    } 
#endif 

} 

```
 

4、修改recharge_callback函数,调用payment函数来显示支付界面。 
 void MainMenuScene::recharge_callback() 
{ 
    CCLOG("recharge"); 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
    CCLOG("PluginChannel"); 
    PluginChannel::getInstance()->payment();//调用渠道支付 
#else 

#endif 
} 

```
 

5、重写onKeyReleased函数来监听按键事件,在Android按返回键的时候调用退出界面。 
在init函数里调用如下代码开启按键监听: 
 setKeyboardEnabled(true); 

```
 
重写onKeyReleased函数: 
 void MainMenuScene::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event) 
{ 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
    switch(keyCode) 
    { 
        case EventKeyboard::KeyCode::KEY_BACK: 
            PluginChannel::getInstance()->exit(); 
            break; 
    } 
#endif 
} 

```
 


五、编译运行游戏 
最后进行编译,在编译完工程后,就可以在手机或模拟器上安装apk运行游戏了,可使用测试账号(用户名:csdn,密码:csdn)进行登陆,游戏效果如下: 
 
   

这个帖子 超级酷啊

:2: :2: :2: :2: :2:

好帖子,要分享!

看到那个飞机又一次想起了少强。。。

你们这样自己顶自己好么:877:

LZ 威武啊!

好复杂的说,偶可以卖个萌么、?:14:

仔细研究下。

本人小白一个对JNI不了解不知道wrapper.java这个文件应该放在哪个目录下?

— Begin quote from ____

引用第8楼木头人_8023于2015-03-27 19:10发表的 :
本人小白一个对JNI不了解不知道wrapper.java这个文件应该放在哪个目录下? http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1269586

— End quote

无所谓啊,你C++定义函数的时候能对应得上包名类名函数名就是了

我想问一下,我们要打渠道包的话,因为接的支付的SDK是三大运营商的,每个渠道用的计费文件不一样,这样可以用这个anysdk吗?感觉不好用啊!

— Begin quote from ____

引用第10楼yinhe888675于2015-04-24 15:52发表的 :
我想问一下,我们要打渠道包的话,因为接的支付的SDK是三大运营商的,每个渠道用的计费文件不一样,这样可以用这个anysdk吗?感觉不好用啊! http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1292294

— End quote

接三大运营商手动接吧,运营商的我们AnySDK上的版本都是很旧的。

  1. 在C++层初始化AnySDK Framework框架
    在PluginChannel的loadPlugins函数里编写初始化、加载插件、设置监听等逻辑,然后调用该函数来进行初始化。在onCreate函数里的PluginWrapper.init(this)之后添加如下代码来进行初始化:

wrapper.nativeInitPlugins();

在PluginChannel的loadPlugins函数里所要做的处理如下:

-----在这步进行不下去了。

1:PluginChannel是从例子里面拷贝一份把,放到xcode项目的class文件中,还要拷贝其他的文件么?
2:PluginChannel里面包含了#include “AgentManager.h” ,,而这个文件在以前的步骤里,包含在protocols/include里,是不是也要拷贝protocols/include这个到xcode的工程里,才可以编译通过??
感觉这一步很难进行,是不是我理解错了,,,请楼主帮忙

— Begin quote from ____

引用第12楼alpen于2015-08-07 19:45发表的 :
4. 在C++层初始化AnySDK Framework框架
在PluginChannel的loadPlugins函数里编写初始化、加载插件、设置监听等逻辑,然后调用该函数来进行初始化。在onCreate函数里的PluginWrapper.init(this)之后添加如下代码来进行初始化:

wrapper.nativeInitPlugins();

http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1364306

— End quote

Sample的文件也不用拷贝,Sample的代码只是让你瞧瞧代码怎么写而已。iOS肯定也需要AnySDK的库,看iOS的集成指南吧http://docs.anysdk.com/CppTutorial#iOS.E6.8E.A5.E5.85.A5

意思是不是这样的:
我们的项目是在xcode里开发的,为了打包android项目,我已经在eclipese建了一个android工程,前面的步骤我都是在android工程里配置的,
现在我还要-------看iOS的集成指南吧http://docs.anysdk.com/CppTutorial#iOS.E6.8E.A5.E5.85.A5----根据这个步骤来配置一下,来进行C++层初始化AnySDK Framework框架。。。然后才可以打母包,,。

母包可以运行,但打了一个华为包,出现下面的错误,大家有遇见过么?

— Begin quote from ____

引用第14楼alpen于2015-08-08 13:59发表的 回 13楼(孤独の快乐) 的帖子 :
意思是不是这样的:
我们的项目是在xcode里开发的,为了打包android项目,我已经在eclipese建了一个android工程,前面的步骤我都是在android工程里配置的,
现在我还要-------看iOS的集成指南吧http://docs.anysdk.com/CppTutorial#iOS.E6.8E.A5.E5.85.A5----根据这个步骤来配置一下,来进行C++层初始化AnySDK Framework框架。。。然后才可以打母包,,。 http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1364526

— End quote

你是啥引擎啊?iOS和Android的cocos2d-x的C++代码不都一样

— Begin quote from ____

引用第15楼alpen于2015-08-08 17:16发表的 :
母包可以运行,但打了一个华为包,出现下面的错误,大家有遇见过么?

http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1364588

— End quote

这是sdk自己返回了初始化失败的回调了,至于崩溃,是你回调里的代码有问题吧。

— Begin quote from ____

引用第17楼孤独の快乐于2015-08-08 18:39发表的 :

这是sdk自己返回了初始化失败的回调了,至于崩溃,是你回调里的代码有问题吧。 http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1364608

— End quote

这个初始化失败,跟配置的sdk参数密切相关么??为什么母包可以初始成功,而子包却不可以。

— Begin quote from ____

引用第18楼alpen于2015-08-09 10:28发表的 :

这个初始化失败,跟配置的sdk参数密切相关么??为什么母包可以初始成功,而子包却不可以。

http://www.cocoachina.com/bbs/job.php?action=topost&tid=239087&pid=1364735

— End quote

母包又没sdk,这是华为sdk自己回调的失败