用cocos2d-x lua开发完一个程序之后,要想在iOS端和Android端同时嵌入广告,是一件很麻烦的事情,如果还需要嵌入多家广告平台的SDK那就更蛋疼了,所以选择使用芒果广告平台来做广告集成,至于芒果广告平台是什么东东,还不了解的就自行百度谷歌吧,这里简单说就是一个广告平台的聚合平台,里面聚合了很多其他的广告平台,比如admob,百度,广点通。
芒果广告平台自己的SDK提供了各个版本的,包括Android原生和iOS原生,并且也提供了Cocos2d-x版本的demo和集成说明,但是我有一个感觉芒果广告平台提供的Cocos2d-x的集成说明有太多问题,也许对高手来说解决问题分分钟的事,但是对于我这种初学者来说,真是一步一个坑,照着说明文档走一遍,各种报错,各种崩溃。下面介绍一下集成的流程并且把这几天碰到的坑列一下,碰到坑的地方就用"坑x(x == index):"表示,其中芒果的后台配置各个广告平台的key和各种设置这里就不列了。
先说一下这个实现的原理,这个很重要,我要是最早能清楚原理,不至于掉那么多坑,原理是我们可以从芒果得到iOS的SDK和Android的SDK,把他们分别放到对应的程序结构下,做对应的配置之后,在当前平台是能正常运行的,但是因为我们是使用lua来开发程序的,所以涉及到要如何通过lua来调用到对应的iOS和Android的代码呢?
答案是:通过C++代码来做中间层,Android可以通过jni来和C++做交互,使用C++来封装一些调用Android SDK里面对应的展示广告的方法,iOS同理。这一层没问题之后,通过lua来调用C++的方法,从而实现在lua里调用方法让两个平台都兼容。
坑0:还没走就先掉到坑里面了,其实对开发过Cocos2d-x的人来说,这个不算事,但是因为我不知道原理,我开始看到他们官网提供的SDK下载里有cocos2d-x的,我挺开心的,因为之前只开发了iOS,在iOS里嵌入SDK是比较简单的,导入SDK,然后添加一些必要的framework,再配置一下对应的接口,基本就能通了。所以我之前认为既然提供了Cocos2d-x的SDK了,那基本也就和iOS导入差不多了吧,往项目里面导入SDK配置一下基本就完事了,没想到我错了,根本不是那么一回事呀。而且我后来才发现,官网上竟然提供了iOS版的Cocos2d-x SDK和Android版的Cocos2d-x SDK。
后来我研究了这两个SDK包,iOS的包里就是告诉开发者使用Cocos2d-x的项目怎么在ios平台下嵌入SDK,Android同理。并没有我之前想的那么简单。那就一个一个来吧,于是先按照说明文档开始嵌入Android部分
我上面提到过Cocos2d-x的集成文档问题很多,很多东西必须要用到的文档并没有提到,对于我这种新手来说,报个错就抓瞎了,都不知道从哪入手。
Android
按照步骤走
第一步导入各个集成各个广告平台的SDK,这些SDK其实就是jar包,按照说明导入,然后Add to Build Path就完事了,不过有的广告平台需要额外的一些jar包,比如广点通的就需要android-support-v4.jar,这个就需要开发者在嵌入什么广告平台的时候去对应的开发平台看一下嵌入需要的一些配置,做对应的处理。
第二步添加用户权限,在mainifest.xml里添加文档里说必须要添加的配置之后,根据每个广告平台的需要配置对应的内容
坑1:配置各个广告平台的时候最好下载一个它们官网的android完整demo,照那个里面的配,cocos2d-x版本的demo和说明文档都不全,我在这浪费了不少时间和他们的客服和技术沟通,还被因为是新手被鄙视问太基础的问题。
第三步在代码里添加调用的代码,这个没什么好说的,按照说明文档配置就行了,没有坑。这里的目的是在Android代码里配置了一些静态方法,等和C++交互调通了以后,让C++调用的。第四步,新建C++文件,MOGOAd.h和MOGOAd.cpp文件都是demo里提供的,把他们放在项项目目录下的/frameworks/runtime-src/Classes下就行了
.h文件的代码:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include
#include "platform/android/jni/JniHelper.h"
#include
#include "cocos2d.h"
#endif
using namespace cocos2d;
class MOGOAd
{
public:
MOGOAd();
virtual ~MOGOAd();
static void showBanner();
static void hideBanner();
};
.cpp的代码:
#include "MOGOAd.h"
MOGOAd::MOGOAd(){}
MOGOAd::~MOGOAd(){}
void MOGOAd::showBanner()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo showBanner;
bool isHave = JniHelper::getStaticMethodInfo(showBanner,"org/cocos2dx/cpp/AdsMogoCoCos2dx","showBannerStatic","()V");
if (!isHave) {
CCLog("jni:showBannerStatic false");
}else{
showBanner.env->CallStaticVoidMethod(showBanner.classID, showBanner.methodID);
}
#endif
}
void MOGOAd::hideBanner()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo hideBanner;
bool isHave = JniHelper::getStaticMethodInfo(hideBanner,"org/cocos2dx/cpp/AdsMogoCoCos2dx","hideBannerStatic","()V");
if (!isHave) {
CCLog("jni:hideBannerStatic false");
}else{
CCLog("jni:hideBannerStatic true");
hideBanner.env->CallStaticVoidMethod(hideBanner.classID, hideBanner.methodID);
}
#endif
}
因为做测试的时候只用了banner样式的广告,所以我这里只提取了banner的两个方法,其他样式的根据自己的需求添加对应的方法就行了,这些C++的文件后面做兼容iOS的时候会修改里面的内容,后面再详细说,这里先用默认的。
第五步是配置jni,让C++通过jni和Android能交互,在Android的项目目录下有个jni文件夹,里面有Android.mk文件,这个是配置jni对交互的相关的,里面找到LOCAL _ SRC _ FILES参数(我这里的""之间加了空格的,因为编辑器会把两个""做转义,大加参照下面的代码):
//这里本来会有一堆系统默认添加的内容,我这里就列了最后几个
LOCAL_SRC_FILES := \
../../Classes/AppDelegate.cpp \
../../Classes/ConfigParser.cpp \
lua/Runtime_android.cpp \
lua/main.cpp
//---------------
//我们需要在lua/main.cpp的后面添加一个" \",然后再后面添加开始新加的MOGOAd.cpp文件的路径,改完后的样子:
LOCAL_SRC_FILES := \
../../Classes/AppDelegate.cpp \
../../Classes/ConfigParser.cpp \
lua/Runtime_android.cpp \
lua/main.cpp \
../../Classes/MOGOAd.cpp
添加完之后,他们完档上写了:
但是我的Cocos2d-x lua项目里,对应的目录下根本没有这个东西呀
但是这里的大概意思是明白的,就是虽然配置好了,所以这里需要编译一下,使用:
cocos compile -p android
不出意外是能编译通过的,但是编译之后只能说明Android到C++层面联通了,但是如何让Lua能和C++交互呢?参考我老大的博客Cocos2d-x下Lua调用自定义C++类和函数的最佳实践,我想我自己写还不如他写的1/10详细,我也是按照这篇文章一步一步调通的,文章比较长,如果只想调通程序,可以直接看里面的"第五层:使用cocos2d-x的方式来将C++类注册进Lua环境",其中里面列出的流程:"5、用Xcode将自定义的C++类和生成的桥接文件加入工程,不然编译不到"的解决方案是在Xcode里添加编译后的文件,但是Android下还需要单独配置一下:
在/项目根目录/frameworks/cocos2d-x/cocos/scripting/lua-bindings下有一个Android.mk,需要在里面的LOCAL _ SRC _ FILES里添加上:
auto/lua_MOGOAd_auto.cpp \
和上面添加LOCAL _SRC _ FILES类似,因为lua_MOGOAd_auto.cpp里面用到了MOGOAd.cpp,所以还需要在Android.mk里找到LOCAL _ C _ INCLUDES:在里面添加对应的Classes路径:
$(LOCAL_PATH)/../../../../runtime-src/Classes \
这一通处理完成之后,在编译一次:
cocos compile -p android
不出意外应该是能编译通过的,接下来直接通过Cocos Code IDE去跑编译之后的apk包就行了。显示效果:
iOS
第一步把下载的SDK导入到项目里,下载下来的SDK分成了三个文件夹AdsMoGoRes,AdsMoGOSDK,Utils,用Xcode打开Cocos2d-x的iOS_Mac项目,把这三个文件夹导入到项目的根目录下,其真实路径是/cocos2d-x项目根目录/frameworks/runtime-src/proj.ios_mac/
坑2:导入的时候需要注意一个问题,就是Cocos2d-x创建出来的项目默认是带了Mac版本的,而导入的SDK是给iOS用的,里面用到了的比如UIKit之类的framework在Mac下是没有的,所以在导入这些SDK文件选择Target的时候记得不要勾选Mac版本的Target,要不然后面编译Mac的时候各种报错各种找不到对应的framework,还得到Build Phases里的Compile Sources把添加进去的再挨个移除掉。
第二步添加各种必要的Framework,这个没什么好说的,添加就行了
坑3:这里再一次吐槽Cocos2d-x的说明文档写的烂,必须要的包都没有列全,EventkitUI.framework、GameController.framework都是必须要的,文档没列出来,只是把他们放在了建议添加的framework里
第三步更改静态库设置,点击程序Target文件,选择Build Settings标签页,找到Linking下面的Other Linker Flags,添加参 数-ObjC:
第四步添加各广告平台SDK,直接在官网下载芒果的iOS版的SDK,里面有很全的各个广告平台的SDK包,根据自己的需要导入到项目里就可以了,导入完之后最好去已经导入过的广告平台官网看一下,他们的SDK需要哪些framework支持,然后根据需求导入framework。
第五步配置MOGOAd.h和.cpp,如果集成Android的时候已经配置过lua调用C++的程序了,这里只需要修改MOGOAd.h和.cpp,使之能同时兼容iOS和Android就完成了,如果还没调通lua和C++的交互,请看上面介绍Android部分里的lua和C++交互部分。
MOGOAd.h:
#include "cocos2d.h"
//原来这里做的#include 都放到.cpp统一处理了
using namespace cocos2d;
class MOGOAd
{
public:
MOGOAd();
virtual ~MOGOAd();
static void showBanner();
static void hideBanner();
};
MOGOAd.cpp:
#include "MOGOAd.h"
//如果是Android,就导入jni的一套东西,如果是iOS,就导入SDK里需要引入的头文件
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include
#include "platform/android/jni/JniHelper.h"
#include
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#include "AdsmogoBanner.h"
#include "AdsMogoInterstitial.h"
#endif
MOGOAd::MOGOAd(){}
MOGOAd::~MOGOAd(){}
void MOGOAd::showBanner()
{
//下面也是同样的操作,不同的平台不同的处理方式
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo showBanner;
bool isHave = JniHelper::getStaticMethodInfo(showBanner,"org/cocos2dx/lua/AppActivity","showBannerStatic","()V");
if (!isHave) {
CCLog("jni:showBannerStatic false");
}else{
showBanner.env->CallStaticVoidMethod(showBanner.classID, showBanner.methodID);
}
#elif (CC_TARGET_PLATFORM==CC_PLATFORM_IOS)
char mogoid []="芒果的appid";
AdsmogoBanner::sharedBanner()->showBanner(mogoid, AdsmogoBannerTypeNormalBanner, 0, 0, false);
#endif
}
void MOGOAd::hideBanner()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo hideBanner;
bool isHave = JniHelper::getStaticMethodInfo(hideBanner,"org/cocos2dx/lua/AppActivity","hideBannerStatic","()V");
if (!isHave) {
CCLog("jni:hideBannerStatic false");
}else{
CCLog("jni:hideBannerStatic true");
hideBanner.env->CallStaticVoidMethod(hideBanner.classID, hideBanner.methodID);
}
#elif (CC_TARGET_PLATFORM==CC_PLATFORM_IOS)
AdsmogoBanner::sharedBanner()->hideBanner();
#endif
}
配置完成,执行:
cocos compile -p ios
不出意外,编译通过,然后在lua里调用:
-- 这里的ad指的是当时配置.ini文件时的target_namespace
ad.MOGOAd:showBanner()
ad.MOGOAd:hideBanner()
就能实现Banner的开启和关闭了,模拟器测试效果如下:
坑4:如果程序还在Mac上运行,这个时候如果编译Mac会报错,因为当时配置lua和C++交互的时候,在AppDelegate.cpp里添加过一段代码:
register_all_MOGOAd(stack->getLuaState())
而这个MOGOAd其实不应该加在Mac版本里的,有两种方法解决这个问题
第一种,添加这段代码的时候需要加一个判断:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID ||CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
register_all_MOGOAd(stack->getLuaState());
#endif
只有Android和iOS的情况下把这段代码添上就OK了,但是这样修改在lua层调用的时候不方便,需要判断一次设备才行,要不然lua会找不到对应的方法,应为Mac版本把那些方法都屏蔽了。
第二种,这里不改,修改Mac的Target,在Mac的Target里Build Phases里的Compile Sources里添加MOGOAd.cpp,添加之后:
然后点击项目下里的cocos2d _ lua _ bindings.xcodeproj,Target选择luabindings Mac,在后在Build Phases里的Compile Sources里添加lua _ MOGOAd _ auto.cpp,在Copy Headers里添加lua _ mogo _ auto.hpp,添加之后:
这种方法的目的就是把MOGOAd.cpp也放入到Mac的项目中,让其可以调用到,不至于在lua端报错,等于是把lua端的判断放到.cpp里做了,MOGOAd.cpp里面如果Mac什么都不做的话,什么都不写就行了。设置完成之后再编译运行,一切正常了,Mac下也能点对应的按钮,只是没有广告,效果图:
折腾了一个星期,总算搞定了。
添加一个新的问题,发现SDK里的ShowBanner是用来做初始化操作的,而hideBanner是直接对Banner的View做remove操作,并不是方法名所说的hide和show的关系,这样的设计的感受就是每次show都要等上一段时间,广告才能显示出来,已经反馈给对方的技术,会在下一个版本修复这个问题。