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

之前写了篇Cocos2d-x 3.X, iOS版添加广点通广告。但是那篇只提到了广告条的集成(有需要的,请戳这里)。不少网友反映出对插屏广告的需求。所以在这里就把插屏广告的集成也写一遍。其实方法是一样的,就是做一个C++类,把它的源文件后缀改成mm,这样就可以把广点通的Objective C指令放里面和C++一起混编。同时它的头文件不含Objective C指令,这样就可以被纯C++文件包含。


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



准备工作:

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

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

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



开干正事:

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

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

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

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

这些都是广点通帮助文档里要求添加的Frameworks。但是这些还不够,还需要往里面添加两个frameworks:GameController.framework和MediaPlayer.framework。具体原因请参考之前的那篇广告条添加(戳这里

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


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


5)找到RootViewController.h,令RootViewController继承一个广点通广告条相关协议,以便让RootViewController成为广告的代理。并且为了在测试时监控广告是否加载成功,我们在RootViewController.mm里实现两个协议方法。完整代码如下所示:

RootViewController.h

#import <UIKit/UIKit.h>
#import "GDTMobInterstitial.h"

@interface RootViewController : UIViewController<GDTMobInterstitialDelegate> {

}
- (BOOL) prefersStatusBarHidden;

@end

RootViewController.mm
#import "RootViewController.h"
#import "cocos2d.h"
#import "platform/ios/CCEAGLView-ios.h"

static NSString *INTERSTITIAL_STATE_TEXT = @"插屏状态";

@implementation RootViewController

/**
 *  广告预加载成功回调
 *  详解:当接收服务器返回的广告数据成功后调用该函数
 */
- (void)interstitialSuccessToLoadAd:(GDTMobInterstitial *)interstitial
{
    NSLog(@"%@:%@",INTERSTITIAL_STATE_TEXT,@"Success Loaded.");
}

/**
 *  广告预加载失败回调
 *  详解:当接收服务器返回的广告数据失败后调用该函数
 */
- (void)interstitialFailToLoadAd:(GDTMobInterstitial *)interstitial error:(NSError *)error
{
    NSLog(@"%@:%@",@"Fail Loaded." );
}

/*
 // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
        // Custom initialization
    }
    return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically,without using a nib.
- (void)loadView {
}
*/

/*
// Implement viewDidLoad to do additional setup after loading the view,typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
}

*/
// Override to allow orientations other than the default portrait orientation.
// This method is deprecated on ios6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsLandscape( interfaceOrientation );
}

// For ios6,use supportedInterfaceOrientations & shouldAutorotate instead
- (NSUInteger) supportedInterfaceOrientations{
#ifdef __IPHONE_6_0
    return UIInterfaceOrientationMaskAllButUpsideDown;
#endif
}

- (BOOL) shouldAutorotate {
    return YES;
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];

    auto glview = cocos2d::Director::getInstance()->getOpenGLView();

    if (glview)
    {
        CCEAGLView *eaglview = (CCEAGLView*) glview->getEAGLView();

        if (eaglview)
        {
            CGSize s = CGSizeMake([eaglview getWidth],[eaglview getHeight]);
            cocos2d::Application::getInstance()->applicationScreenSizeChanged((int) s.width,(int) s.height);
        }
    }
}

//fix not hide status on ios7
- (BOOL)prefersStatusBarHidden
{
    return YES;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data,images,etc that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}


- (void)dealloc {
    [super dealloc];
}


@end

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

//要被cpp包含,所以不能有任何Objective C指令,也就是说这里即不能#import GDTMobBannerView.h,
//也不能用@class GDTMobBannerView. 只能通过一个结构体来间接的调用GDTMobBannerView
struct AdInterImpl;

class AdInterC
{
public:
    AdInterC();
    ~AdInterC();
    
    void loadAd();//加载广告
    void showAd();//显示广告
    
private:
    AdInterImpl* impl;
};
#endif /* AdInterC_hpp */
AdInterC.mm
#include "AdInterC.h"

#include "RootViewController.h"

struct AdInterImpl
{
    GDTMobInterstitial * _interObj;
};

AdInterC::AdInterC()
{
    impl = new AdInterImpl();
}
AdInterC::~AdInterC()
{
    impl->_interObj.delegate=nil;
    delete impl;
}

void AdInterC::loadAd()
{
    impl->_interObj = [[GDTMobInterstitial alloc] initWithAppkey:@"2211675583" placementId:@"2954362461573316031"];//注意测试的时候就要改成自己申请来的ID,否则很难加载成功


    auto rootViewController = (RootViewController*) [[[UIApplication sharedApplication] keyWindow] rootViewController];
    impl->_interObj.delegate = rootViewController; //设置委托
    impl->_interObj.isGpsOn = NO; //【可选】设置GPS开关 //预加载广告
    [impl->_interObj loadAd];
}

void AdInterC::showAd()
{
    UIViewController *vc = [[[UIApplication sharedApplication] keyWindow]
                            rootViewController];
    [impl->_interObj presentFromRootViewController:vc];
}
注意要把app key和placement id改成自己申请来的,否则很难加载成功。另外要把加载方法和显示方法分开。
7)现在就可以愉快的往HelloWorld场景中添加插屏广告了。为了测试,我们在HelloWorld图层里面添加两个菜单项。左边的菜单项按下之后开始加载,右边的菜单项按下之后开始显示。加载需要一定的时间,可以通过观察控制台的输出来监控加载是否成功,然后再去按显示菜单项。否则会报如下错误:[GDTMob::GDTMobInterstitial] Info:Interstitial not ready or already presented!。完整代码如下所示:
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "AdInterC.h"

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    //加载回调
    void loadCallback(cocos2d::Ref* pSender);
    //显示回调
    void showCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
    
    
    AdInterC *ad;
};

#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    //创建一个广告类对象
    ad=new AdInterC();
    
    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.

    
    //加载菜单项
    auto loadItem = MenuItemImage::create(
                                          "CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::loadCallback,this));
    //显示菜单项
    auto showItem = MenuItemImage::create(
                                           "CloseNormal.png",CC_CALLBACK_1(HelloWorld::showCallback,this));
    
	loadItem->setPosition(Vec2(origin.x +  loadItem->getContentSize().width/2,origin.y + loadItem->getContentSize().height/2));
    
    showItem->setPosition(Vec2(origin.x + visibleSize.width - showItem->getContentSize().width/2,origin.y + showItem->getContentSize().height/2));

    // create menu,it's an autorelease object
    auto menu = Menu::create(loadItem,showItem,NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu,1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    auto label = Label::createWithTTF("Hello World","fonts/Marker Felt.ttf",24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label,1);

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite,0);
    
    return true;
}

void HelloWorld::loadCallback(Ref* pSender)
{
    ad->loadAd();
}

void HelloWorld::showCallback(Ref* pSender)
{
    ad->showAd();
}


在实际应用过程中,要给加载过程留有一定的时间,比如在游戏过程中就偷偷加载,等到游戏结束时再调用显示插屏方法。

8)编译运行。注意要用真机来运行,否则也是很难加载成功(不知道为啥)。在按下左边的加载按钮时稍等片刻,看到控制台显示加载成功后再去按右边的显示按钮。结果如下所示:


已经将横屏调成了竖屏。并且在申请广点通广告位ID时,勾选了小规格和大规格两种插屏广告。如果在运行时遇到如下报错:
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,将其下面的AllowArbitrary Loads设置为Yes。但貌似插屏广告没有这个问题。


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


参考资料:

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

2) 广大网友

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...