苹果自己的广告平台iAD在中国不好使,获取不到广告,所以必须搞个本土广告平台。首先想到的是广点通。可是广点通官方文档里没有针对Cocos2d-x的版本,所以只好自己摸索。在添加过程中确实遇到了不少问题,在这里将解决方法整理一下,希望能帮助到那些还处于摸索中的朋友们。

本文所用Cocos2d-x为3.8.1版,Xcode为7.1版,广点通SDK为IOS 4.0版。(插播广告:如需添加安卓版广告请戳这里)

本文针对的是广告条的集成,应不少网友的需求已经另写了一篇插屏广告的集成,如有需要请戳这里

准备工作:

1)进入广点通官网,注册账号。注册时需要上传身份证正反面照片(好像还需要手持身份证照片)以及银行账户。然后等待审核。广点通审核时间略长,大概要一个礼拜。

2)审核通过后就可以创建应用和广告位,并得到应用和广告位ID。这两个ID会被添加到我们的程序当中。

3)下载广点通IOS版SDK。广点通的SDK文件夹里有示例代码和帮助文档Guide 4.0.pdf。可以打开看一看(可以结合本文一起阅读),但是没有针对Cocos2d-x的。

开干正事:

1)新建HelloWorld项目。在搞懂如何添加之前,建议不要直接在自己的工程里面添加,最好新建一个HelloWorld项目用于试验。新建IOS版的HelloWorld项目相对简单。配置好环境变量后,只需一行命令即可完成。这个可以参考Cocos2d-x官方文档。

2)添加广点通SDK。打开下载好的广点通SDK文件夹,将其中libs里面的所有文件都拖入Xcode的HelloWorld项目中(以group形式加入,并注意libGDTMobSDK.a得放在项目根目录下,如果放在Classes下面链接时会找不到)。

3)添加相应Frameworks。进入Xcode项目的TARGETS部分,选择HelloWorld-mobile,进入Build Phases下的Link Binary With Libraries。往里面添加以下Frameworks:

AdSupport.frameworkCoreLocation.frameworkQuartzCore.framework (注意官方文档上单词拼写错误,漏了一个t)SystemConfiguration.frameworkCoreTelephony.frameworklibz.dylib libz.tbdSecurity.frameworkStoreKit.framework

这些都是广点通帮助文档里要求添加的Frameworks。我们暂且只添加这些,留一伏笔。

4)更改导入静态库设置。再次进入Xcode项目的TARGETS部分,选择HellWorld-mobile,  进入Build Settings下面的Linking部分,往Other Linker Flags里添加两个参数-ObjC和-lstdc++。此处再留一伏笔。

以上部分和官方帮助文档Guide 4.0.pdf一致,如有不清楚的地方可以参考官方文档。接下来就不太一样了,请留意。


5)找到RootViewController.h,令RootViewController类继承一个广点通广告条相关协议。完整代码如下

RootViewController.h

#import <UIKit/UIKit.h>
#import "GDTMobBannerView.h"@interface RootViewController : UIViewController <GDTMobBannerViewDelegate>{}
- (BOOL) prefersStatusBarHidden;@end

这一步是为了让RootViewController成为广告的代理。在最简情形下,这样就可以了,无需在RootViewController.mm里添加任何东西。如果需要其他功能,比如当请求广告数据失败后想要触发一个事件(比如输出一段请求失败的信息),那么可以在此实现以下协议方法:

- (void)bannerViewFailToReceived:(NSError *)error

6)新建一个C++类,命名为AdBannerC,用来生成广告。注意我们要在Cocos2d-x的场景里生成广告,而Cocos2d-x场景又都是C++代码实现的(主要是因为Cocos2d-x都是以.cpp文件存在,它不支持对Objective C指令的编译),所以必须新建C++类,而非Objective C类。完整代码如下:

AdBannerC.h

#ifndef AdBannerC_h
#define AdBannerC_h//要被cpp包含,所以不能有任何Objective C指令, 也就是说这里即不能#import GDTMobBannerView.h,
//也不能用@class GDTMobBannerView. 只能通过一个结构体来间接的调用GDTMobBannerView
struct AdBannerImpl;class AdBannerC
{
public:AdBannerC();~AdBannerC();private:AdBannerImpl* impl;
};#endif
AdBannerC.mm
#include "AdBannerC.h"
#include "RootViewController.h"  struct AdBannerImpl
{  GDTMobBannerView * _bannerView;
};  AdBannerC::AdBannerC()
{  impl = new AdBannerImpl();  impl->_bannerView=[[GDTMobBannerView alloc] initWithFrame:CGRectMake(0, 0,  GDTMOB_AD_SUGGEST_SIZE_320x50.width,  GDTMOB_AD_SUGGEST_SIZE_320x50.height)  appkey:@"100720253"  placementId:@"9079537207574943610"];  //调用RooViewController对象  auto rootViewController = (RootViewController*) [[[UIApplication sharedApplication] keyWindow] rootViewController];  impl->_bannerView.delegate = rootViewController; // 设置Delegate  impl->_bannerView.currentViewController = rootViewController; //设置当前的ViewController  impl->_bannerView.interval = 30; //【可选】设置刷新频率;默认30秒  impl->_bannerView.isGpsOn = NO; //【可选】开启GPS定位;默认关闭  impl->_bannerView.showCloseBtn = YES; //【可选】展示关闭按钮;默认显示  impl->_bannerView.isAnimationOn = YES; //【可选】开启banner轮播和展现时的动画效果;默认开启  [rootViewController.view addSubview:impl->_bannerView]; //添加到当前的view中  [impl->_bannerView loadAdAndShow]; //加载广告并展示  }  AdBannerC::~AdBannerC()
{  impl->_bannerView.delegate = nil;  impl->_bannerView.currentViewController = nil;  [impl->_bannerView release];[impl->_bannerView removeFromSuperview];  delete impl;
} 

注意AdBannerC类的实现部分的文件名后缀是.mm。该文件支持对C++和Objective C两种语言的混合编译。代码本身并不复杂,几乎一目了然。就是创建一个BannerView对象(将其中应用和广告位ID换成我们自己的),然后把它加入到RootViewController的视图下,并设置几个参数。

7)这样就可以愉快的往HelloWorld场景中添加AdBannerC对象,获取广告了。完整代码如下所示:

HelloWorldScene.h

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__#include "cocos2d.h"
#include "AdBannerC.h"class HelloWorld : public cocos2d::Layer
{
public:static cocos2d::Scene* createScene();virtual bool init();// a selector callbackvoid menuCloseCallback(cocos2d::Ref* pSender);// implement the "static create()" method manuallyCREATE_FUNC(HelloWorld);//广告条对象AdBannerC *adBanner;
};#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"
//#include "AdBannerC.h"USING_NS_CC;Scene* HelloWorld::createScene()
{// 'scene' is an autorelease objectauto scene = Scene::create();// 'layer' is an autorelease objectauto layer = HelloWorld::create();// add layer as a child to scenescene->addChild(layer);// return the scenereturn scene;
}// on "init" you need to initialize your instance
bool HelloWorld::init()
{//// 1. super init firstif ( !Layer::init() ){return false;}Size visibleSize = Director::getInstance()->getVisibleSize();Vec2 origin = Director::getInstance()->getVisibleOrigin();/// 2. add a menu item with "X" image, which is clicked to quit the program//    you may modify it.// add a "close" icon to exit the progress. it's an autorelease objectauto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,origin.y + closeItem->getContentSize().height/2));// create menu, it's an autorelease objectauto menu = Menu::create(closeItem, NULL);menu->setPosition(Vec2::ZERO);this->addChild(menu, 1);/// 3. add your codes below...// add a label shows "Hello World"// create and initialize a labelauto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);// position the label on the center of the screenlabel->setPosition(Vec2(origin.x + visibleSize.width/2,origin.y + visibleSize.height - label->getContentSize().height));// add the label as a child to this layerthis->addChild(label, 1);// add "HelloWorld" splash screen"auto sprite = Sprite::create("HelloWorld.png");// position the sprite on the center of the screensprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));// add the sprite as a child to this layerthis->addChild(sprite, 0);return true;
}void HelloWorld::menuCloseCallback(Ref* pSender)
{
//    Director::getInstance()->end();
//
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
//    exit(0);
//#endif//加广点通广告adBanner = new AdBannerC();}

我们在这里把新建广告条函数放在了原先的关闭按钮回调函数里,这样只要按一下按钮就会弹出广告条。

8)编译运行。这时问题就来了。首先会提示出现7个错误:

 "_MPMoviePlayerPlaybackStateDidChangeNotification", referenced from:-[UIVideoViewWrapperIos dealloc] in libcocos2d iOS.a(UIVideoPlayer-ios.o)-[UIVideoViewWrapperIos setURL::] in libcocos2d iOS.a(UIVideoPlayer-ios.o)"_MPMoviePlayerPlaybackDidFinishNotification", referenced from:-[UIVideoViewWrapperIos dealloc] in libcocos2d iOS.a(UIVideoPlayer-ios.o)-[UIVideoViewWrapperIos setURL::] in libcocos2d iOS.a(UIVideoPlayer-ios.o)"_OBJC_CLASS_$_GCController", referenced from:objc-class-ref in libcocos2d iOS.a(CCController-iOS.o)(maybe you meant: _OBJC_CLASS_$_GCControllerConnectionEventHandler)"_OBJC_CLASS_$_MPMoviePlayerController", referenced from:objc-class-ref in libcocos2d iOS.a(UIVideoPlayer-ios.o)"_GCControllerDidDisconnectNotification", referenced from:-[GCControllerConnectionEventHandler observerConnection:disconnection:] in libcocos2d iOS.a(CCController-iOS.o)"_GCControllerDidConnectNotification", referenced from:-[GCControllerConnectionEventHandler observerConnection:disconnection:] in libcocos2d iOS.a(CCController-iOS.o)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这时如果把第四步中设置的Other Linker Flags中的-ObjC去掉的话,便可以编译运行成功,但是当我们按下弹出广告条按钮时就会报错(包括unrecognized selector sent to instance 0x79277930)。因此这个方法行不通,还得添加回去。那么问题在哪呢?决解这个问题的答案非常简单,就是添加两个Frameworks:GameController.framework和MediaPlayer.framework。但是要理解为什么就需要科普一下连接器的工作原理了:

连接器工作原理

在C语言里面,函数的调用和定义可以处于两个不同的文件里。比如main.c文件里调用了foo()函数(在调用之前必须声明该函数,但不必定义该函数),但是foo()的定义在B.c里面。那么编译器会在生成的目标文件main.o里给foo()函数做一个待定标记,告诉连接器该函数定义在别处。这样连接器就会找到定义该函数的目标文件B.o,并把它加进来。Unix的静态库其实就是一堆目标文件。在一般情况下,如果一个函数被声明了,但是没有被调用,那么它是没有待定标记的,连接器不会把静态库中定义该函数的目标文件加载进来,以减小可执行文件的大小。

对于Objective C,由于它的动态特性,情况略有不同。只有当某个方法(对应于C的函数)在运行时被调用了,计算机才知道它的实现(对应于C的函数定义)是什么(比如父类和子类对同一方法有不同实现。如果采用动态绑定,那么某个id对象即可以是父类也可以是子类,到底是什么只有在运行时才知道)。所以Objective C不对方法做待定标记,即便做了在连接时也不知道去哪里找实现。Objective C只对类做待定标记,因为类的实现是确定的。另一方面,Objective C里还有一个独特的存在叫做分类(category),它其实就是一堆方法的集合,是某个类的扩展。所以,如果某个文件调用了某个分类里面的方法,是不会产生这个分类的待定标记的,只会产生这个类的待定标记。所以默认情况下连接器不会把(静态库中)定义该分类的目标文件加进来。这样就会在运行时报错,找不到实现(unrecognized selector)。解决方法就是“宁可错杀,不可放过”。添加-ObjC标志之后,连接器就把静态库中所有相关的分类实现都加进来,无论它们有没有被调用,只要头文件被包含过(也就是只要被声明过)就都加进来。

理解了这个原理之后就知道为啥去掉-ObjC后会在运行时出错,因为找不到某个实现了。但是加了-ObjC这个标志之后呢,连接器会将静态库中所有相关类和分类的实现都加进来,恰巧Cocos2d-x的文件里包含了两个静态库头文件GameController/GameController.h和MediaPlayer/MediaPlayer.h,连接器根据-ObjC必须加载它们对应的目标文件,但是又找不到,所以只好报错。解决办法就是添加这两个目标文件所在的Frameworks。参考资料在这里。

加了这两个Frameworks之后,果然可以编译运行了。但是在点击弹出广告按钮时还是报错:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

将这段报错信息拷贝到百度搜索栏里面,很快就能找到解决方法(参考这里)。原来是IOS9新增了App Transport Security特性,默认使用HTTPS协议,如果在IOS9下直接进行HTTP请求就会报错。解决方法就是关闭这个特性: 进入项目TARGETS下的HelloWorld-mobile,找到info下的Cumstom ios Target Properties, 添加App Transport Security Settings,将其下面的Allow Arbitrary Loads设置为Yes。如下图所示:

现在我们再来运行试试,发现没有报错了。但是在模拟器里一直获取不到广告。如果改用真机运行可以很容易获取到。运行结果如下图所示:

从上图可见我在新建HelloWorld项目中已经把横屏调成了竖屏。这只是个人喜好而已。在用真机测试时或者打包上传时可能还会遇到一个错误就是:-fembed-bitcode is not supported on versions of iOS prior to 6.0。这个问题是因为Xcode默认支持bitcode(程序的一种中间代码,它可以让苹果在后期对我们的二进制码进行优化),但是有些第三方库并不支持bitcode,所以解决办法就是关闭它:找到TARGETS->Build Settings->Build Options->Enable Bitcode,将其设置为NO。参考在这里。

水平有限,如有不妥,欢迎指正!

参考资料:

1)广点通官方文档Guide 4.0.pdf

2)  广大网友

Cocos2d-x 3.X, IOS版添加广点通广告平台相关推荐

  1. Cocos2d-x 3.X, Android Studio版添加广点通广告平台

    考虑到以下两点: 1. Cocos2d-x从3.7版开始优化对Android Studio的支持,相信这一趋势还会继续.可惜目前相关帮助文档还不完善,很多东西需要自己摸索. 2. 广点通广告平台的官方 ...

  2. Cocos2d-x 3.X, IOS版添加广点通插屏广告

    之前写了篇Cocos2d-x 3.X, iOS版添加广点通广告.但是那篇只提到了广告条的集成(有需要的,请戳这里).不少网友反映出对插屏广告的需求.所以在这里就把插屏广告的集成也写一遍.其实方法是一样 ...

  3. android广告弹窗插屏,cocos2dx android添加广点通广告平台的---插屏广告

    方式一: 1. 在cocos2dx编译完成的项目(proj.android)加入 GDTUnionSDK.4.8.524.min.jar广点通jar包. 2. 添加权限声明 3.添加activity, ...

  4. unity3d 5.2 添加广点通广告

    今天向项目中添加插屏广告.之前找了一圈,看评论说腾讯的广点通还可以,于是决定放弃有米,试试. 回到正题,之前还没试过用unity3d和java交互.于是先找了一圈的资料.这里先BS下腾讯,你妹的有的是 ...

  5. android 腾讯广告接入,cocos2dx 3.x android studio 添加广点通广告

    1,接入广告条 按照官方文档接入的时候遇到个问题,官方文档中是使用的布局文件来显示广告条,但是cocos2dx 没有使用布局文件,所以这里需要我们在代码中手动创建一个布局容器来显示广告条,代码如下: ...

  6. 腾讯广点通广告投放-转化归因API回传接口对接踩坑指南

    对于腾讯广点通广告平台的文档,实在是忍不住要吐槽一番.本来接收到回传接口文档,看到给的PDF文档没有备注说明,但是看到回传方式,挺简单的.以为一下就能搞定了,但是对接下来才发现,各个字段根本不知道什么 ...

  7. Unity3D使用腾讯广点通广告

    目前,游戏开发平台有很多,今天咱们主要介绍Unity3d开发的Android游戏加广点通广告. 1. 下载广点通SDK 广点通网址:http://e.qq.com/dev/index.html 在 流 ...

  8. 广点通广告,oppo广告集成

    年前做的事情,年后才来总结. 因公司项目需要接入广点通广告,花费了一天时间接入到项目中.如何接入项目以及使用,广点通的DEMO已经很详细,很容易理解了,在此不做过多说明. 本想项目上线后再做优化和整合 ...

  9. 无需上架,接入穿山甲广告和广点通广告 sdk

    我们都知道广告是商业流量变现最重要的方式之一,所以基本所有的游戏或者app都会接入广告sdk,而我们都知道穿山甲广告sdk和广点通广告sdk是国内收益相对比较高的,但是想接入穿山甲广告sdk,你的游戏 ...

最新文章

  1. mysql字符串外键约束_MySQL中的约束函数主外键
  2. 通用类 RemoteUpload 远程上传从其他网站复制过来的图片
  3. 使用HTML5技术开发的超酷颜色选择器
  4. 移动app部分机型无法唤起h5支付宝支付_谜之wxs,uni-app如何用它大幅提升性能
  5. python c4.5完整代码_python实现c4.5/Id3自我练习
  6. VisualStudio2010 SP1 SP1 SDK SQLServer 2008 SP3 下载地址
  7. sql server 2008 r2卸载重装_免费下载:Intouch软件、Windows操作系统、SQL数据库,VB6.0、C#...
  8. 二叉树最大深度:给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
  9. Java消息中间件--JMS规范
  10. 开源 java CMS - FreeCMS2.6 站点设置
  11. python爬取系统_python应用:爬虫框架Scrapy系统学习第四篇——scrapy爬取笔趣阁小说...
  12. 从RSAC2017看威胁情报如何落地
  13. 微猫恋爱撩妹术V2 4.1.0-多开版
  14. 光学仿真(python菲涅尔公式)
  15. 逻辑结构定义及其分类
  16. docker desktop 阿里云镜像加速
  17. math的用法在java中的使用,Java Math cbrt() 使用方法及示例
  18. 时空、光年、过去与现在
  19. 简单的将linux二进制程序、资源打包成rpm
  20. php 微信二维码设置中间logo

热门文章

  1. 对局匹配(动态规划)
  2. kali2.0 mysql_kali linux2.0下MariaDB修改密码
  3. 关于股市行情与情绪指数的相关性
  4. python血压测量程序代码_基于单片机的血压测量
  5. iphone界面元素的尺寸[转]
  6. 徕卡 sl android app,随时随地无缝互联 徕卡发布最新版FOTOS App
  7. RNN、LSTM、GRU实战——imdb情感分析
  8. 云讯健身管理系统-11--NUXT和Redis
  9. 不公平才是世界的真相----摘自罗辑思维
  10. 微信小程序内添加腾讯地图 导航