从Cocos JS到iOS原生:手把手教你实现AdMob广告回调与激励视频奖励发放
2026/6/1 6:17:40 网站建设 项目流程

Cocos Creator与iOS原生深度整合:AdMob广告回调与激励视频奖励发放实战指南

在移动游戏开发中,广告变现与用户体验的平衡一直是个技术难点。本文将深入探讨如何通过Cocos Creator的JavaScript/TypeScript与iOS原生代码的深度整合,实现AdMob广告系统的完整闭环,特别是激励视频观看后的游戏内奖励发放机制。

1. 环境准备与基础配置

1.1 Cocos Creator项目设置

首先确保你的Cocos Creator项目已正确配置iOS平台支持:

# 检查Cocos Creator版本 cocos -v # 构建iOS项目 cocos compile -p ios

在构建前,需在project.json中确认以下配置:

{ "engine": "cocos2d-x", "platform": "ios", "jsbFolder": "src" }

1.2 iOS工程配置

Xcode工程需要添加AdMob SDK和相关依赖:

  1. 手动下载AdMob SDK(推荐版本8.9.0+)
  2. 将以下框架添加到工程:
    • GoogleMobileAds.framework
    • UserMessagingPlatform.framework(用于GDPR合规)

Build Settings中确认:

  • Other Linker Flags包含-ObjC
  • Enable Modules (C and Objective-C)设为YES

2. 原生代码架构设计

2.1 广告管理器实现

创建AdmobManager单例类,统一管理各类广告:

// AdmobManager.h #import <Foundation/Foundation.h> #import "RootViewController.h" typedef NS_ENUM(NSInteger, AdLoadStatus) { AdLoadStatusNotLoaded, AdLoadStatusLoading, AdLoadStatusReady }; @interface AdmobManager : NSObject + (instancetype)sharedInstance; - (void)initializeWithViewController:(RootViewController *)viewController; // 广告控制方法 + (void)showRewardedAd; + (void)showInterstitialAd; + (void)showBannerAd; @end

对应的.m文件实现核心广告加载逻辑:

// AdmobManager.m #import <GoogleMobileAds/GoogleMobileAds.h> @interface AdmobManager() <GADFullScreenContentDelegate> @property (nonatomic, strong) GADRewardedAd *rewardedAd; @property (nonatomic, strong) GADInterstitialAd *interstitialAd; @property (nonatomic, strong) GADBannerView *bannerView; @property (nonatomic, weak) RootViewController *rootViewController; @property (nonatomic, assign) AdLoadStatus rewardedAdStatus; @end @implementation AdmobManager + (instancetype)sharedInstance { static AdmobManager *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[AdmobManager alloc] init]; }); return instance; } - (void)initializeWithViewController:(RootViewController *)viewController { self.rootViewController = viewController; [[GADMobileAds sharedInstance] startWithCompletionHandler:nil]; [self loadRewardedAd]; [self loadInterstitial]; [self setupBannerAd]; } - (void)loadRewardedAd { self.rewardedAdStatus = AdLoadStatusLoading; GADRequest *request = [GADRequest request]; [GADRewardedAd loadWithAdUnitID:@"YOUR_AD_UNIT_ID" request:request completionHandler:^(GADRewardedAd *ad, NSError *error) { if (error) { NSLog(@"Rewarded ad failed to load: %@", error); self.rewardedAdStatus = AdLoadStatusNotLoaded; return; } self.rewardedAd = ad; self.rewardedAd.fullScreenContentDelegate = self; self.rewardedAdStatus = AdLoadStatusReady; }]; } // 其他广告类型实现类似... @end

3. Cocos到原生的通信桥梁

3.1 JavaScript调用原生方法

在Cocos TypeScript中,通过jsb.reflection.callStaticMethod调用原生代码:

// AdmobBridge.ts const {ccclass, property} = cc._decorator; @ccclass export default class AdmobBridge extends cc.Component { private static _instance: AdmobBridge = null; public static get instance(): AdmobBridge { return AdmobBridge._instance; } onLoad() { AdmobBridge._instance = this; cc.game.addPersistRootNode(this.node); } showRewardedAd(onReward: Function, context: any): void { if (cc.sys.os === cc.sys.OS_IOS) { jsb.reflection.callStaticMethod( "AdmobManager", "showRewardedAd" ); this._rewardCallback = onReward.bind(context); } } private _rewardCallback: Function = null; // 提供给原生调用的奖励发放方法 public onUserRewarded(): void { if (this._rewardCallback) { this._rewardCallback(); this._rewardCallback = null; } } }

3.2 原生回调JavaScript

创建ObjectToJs.mm文件处理原生到JavaScript的通信:

// ObjectToJs.mm #import "ObjectToJs.h" #import "cocos2d.h" #import "cocos/scripting/js-bindings/jswrapper/SeApi.h" @implementation ObjectToJs + (instancetype)sharedInstance { static ObjectToJs *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[ObjectToJs alloc] init]; }); return instance; } - (void)dispatchRewardToJS { std::string jsCallStr = "cc.find('AdmobBridge').getComponent('AdmobBridge').onUserRewarded();"; se::ScriptEngine::getInstance()->evalString(jsCallStr.c_str()); } @end

在广告回调中触发奖励发放:

// 在AdmobManager.m中 - (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad { if ([ad isKindOfClass:[GADRewardedAd class]] && self.userEarnedReward) { [[ObjectToJs sharedInstance] dispatchRewardToJS]; } [self loadRewardedAd]; // 重新加载广告 }

4. 高级功能与优化

4.1 广告状态同步

实现Cocos与原生之间的广告状态同步:

// 在AdmobBridge.ts中添加 interface AdStatus { isRewardedAdReady: boolean; isInterstitialReady: boolean; } getAdStatus(): Promise<AdStatus> { return new Promise((resolve) => { if (cc.sys.os === cc.sys.OS_IOS) { const status = jsb.reflection.callStaticMethod( "AdmobManager", "getAdStatus" ); resolve(status); } else { resolve({ isRewardedAdReady: false, isInterstitialReady: false }); } }); }

对应的Objective-C实现:

// AdmobManager.m + (NSDictionary *)getAdStatus { return @{ @"isRewardedAdReady": @([AdmobManager sharedInstance].rewardedAdStatus == AdLoadStatusReady), @"isInterstitialReady": @([AdmobManager sharedInstance].interstitialAdStatus == AdLoadStatusReady) }; }

4.2 内存管理注意事项

跨语言通信中的内存管理要点:

  1. Objective-C到JavaScript的字符串转换
std::string jsString = [@"Objective-C String" UTF8String];
  1. 避免循环引用
__weak typeof(self) weakSelf = self; [self.rewardedAd presentFromRootViewController:self.viewController userDidEarnRewardHandler:^{ typeof(self) strongSelf = weakSelf; if (!strongSelf) return; // 处理奖励 }];
  1. 及时释放资源
// TypeScript中 onDestroy() { this._rewardCallback = null; AdmobBridge._instance = null; }

5. 调试与问题排查

5.1 常见问题解决方案

问题现象可能原因解决方案
广告加载失败网络问题/AdUnitID错误检查网络连接,确认AdUnitID正确
JavaScript调用无响应方法名不匹配检查类名和方法名大小写
奖励未发放回调函数未正确设置确保rewardCallback在显示广告前设置
内存泄漏循环引用使用weak/strong dance模式

5.2 调试技巧

  1. 在Xcode中启用调试日志:
NSLog(@"%@", @"详细调试信息");
  1. 在Cocos Creator中监听JavaScript异常:
window.onerror = function(message, source, lineno, colno, error) { console.error("Global error:", message, error); };
  1. 使用Safari开发者工具调试iOS WebView:
  • 在iOS设置中启用Web检查器
  • 通过Safari的"开发"菜单连接设备

6. 性能优化建议

  1. 广告预加载策略
// 游戏启动时预加载广告 - (void)preloadAds { [self loadRewardedAd]; [self loadInterstitial]; } // 广告使用后立即重新加载 - (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad { if ([ad isKindOfClass:[GADRewardedAd class]]) { [self loadRewardedAd]; } else if ([ad isKindOfClass:[GADInterstitialAd class]]) { [self loadInterstitial]; } }
  1. 减少跨语言调用频率
// 批量获取广告状态而不是多次单独查询 const adStatus = await AdmobBridge.instance.getAdStatus(); if (adStatus.isRewardedAdReady) { // 显示激励视频按钮 }
  1. 线程安全注意事项
dispatch_async(dispatch_get_main_queue(), ^{ // 所有UI操作和广告展示必须在主线程 [self.rewardedAd presentFromRootViewController:self.rootViewController userDidEarnRewardHandler:^{ // 奖励处理 }]; });

在实际项目中,我们发现激励视频的最佳展示时机是在游戏自然停顿点(如关卡结束、角色死亡复活等)。通过合理的预加载策略,可以将广告准备时间对用户体验的影响降到最低。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询