适用对象:第一次在 Unity 项目中接入穿山甲(Pangle)GroMore 聚合 SDK 的开发者
覆盖平台:Android + iOS
广告类型:激励视频、插屏(全屏视频)
官方文档参考:GroMore 接入指南、本地配置导入方案
目录
- [什么是 GroMore 聚合](#什么是 GroMore 聚合)
- 接入前准备
- [下载并导入 Unity SDK](#下载并导入 Unity SDK)
- [GroMore 后台配置](#GroMore 后台配置)
- 项目目录规划
- [Android 接入](#Android 接入)
- [iOS 接入](#iOS 接入)
- [SDK 初始化(核心)](#SDK 初始化(核心))
- 激励视频接入
- 插屏广告接入
- 打包与真机测试
- 常见错误排查
- 上线前检查清单
1. 什么是 GroMore 聚合
穿山甲(Pangle) 是字节跳动的广告平台。
GroMore 是穿山甲提供的**广告聚合(Mediation)**能力:一次请求,自动在多个广告网络(ADN)之间比价/瀑布流,提高填充率和收益。
在 Unity 中接入时,你需要理解三件事:
| 概念 | 说明 |
|---|---|
| App ID | 应用在穿山甲后台的唯一标识 |
| 广告位 ID(CodeId / Rit) | 每种广告样式对应一个广告位 |
| 聚合瀑布流 | 一个广告位下挂多个 ADN 代码位,按优先级请求 |
2. 接入前准备
2.1 开发环境
- Unity 2020.3 LTS 或更高(建议 LTS)
- Android:JDK、Android SDK、Gradle
- iOS:Mac + Xcode 14+、CocoaPods
- 真机(模拟器无法完整测试广告)
2.2 后台账号
- 注册 穿山甲开发者平台
- 创建应用,获取 App ID
- 创建广告位:
- 激励视频广告位
- 插屏/全屏视频广告位
- 在 GroMore 后台配置瀑布流(至少接入 2~3 个 ADN)
2.3 记录这些 ID(后面代码要用)
text
App ID: 5786*** // 示例,换成你自己的
激励视频广告位: 103867***
插屏广告位: 103866***
3. 下载并导入 Unity SDK
3.1 下载 SDK
从穿山甲官方下载 Unity 版 GroMore 融合 SDK (通常包含 CSJ 文件夹)。
3.2 导入 Unity 工程
将 SDK 文件夹拖入 Assets/ 目录,典型结构如下:
text
Assets/
└── CSJ/
├── Editor/
│ └── PangleAdapterScriptsDependencies.xml # 依赖配置(重要)
├── Plugins/
│ ├── Android/
│ └── iOS/
└── Scripts/
├── Api/ # C# API 接口
└── Implementation/
├── Android/
└── iOS/
导入后确认:
- 没有编译报错
Assets/CSJ/Scripts/Api/下有Pangle.cs、SDK.cs等文件- 命名空间为
ByteDance.Union
4. GroMore 后台配置
4.1 创建瀑布流
进入 GroMore 后台 → 广告位管理 → 配置瀑布流:
- 添加穿山甲自有代码位
- 按需添加第三方 ADN(百度、Sigmob、快手、广点通等)
- 设置超时时间(建议 20~30 秒)
- 设置并行请求数
4.2 导出本地配置文件(强烈建议)
在网络异常、冷启动等场景,线上配置可能拉取失败,导致 40045 等错误。
官方方案:配置拉取失败解决方案
操作步骤:
- GroMore 后台 → 应用管理 → 导出配置信息
- 得到
site_config_xxxxxxx.json - 放入 Unity 工程:
text
Assets/Resources/GroMore/site_config_5786***.json
注意:Android 和 iOS 设置本地配置的方式不同(后面会讲)。
4.3 注册测试设备
开发阶段必须在后台添加测试设备 IDFA/GAID,否则容易出现 10086(全部 ADN 填充失败)。
5. 项目目录规划
建议新建统一的广告管理类,不要散落在各处:
text
Assets/
├── Scripts/
│ ├── Ads/
│ │ ├── ATTManager.cs # iOS ATT 授权
│ │ └── AdManager.cs # 广告统一管理(本文核心)
│ └── Game/
│ └── GameLaunch.cs # 启动时初始化广告
├── Resources/
│ └── GroMore/
│ └── site_config_5786***.json
└── PodFileEditor/Editor/ # iOS 构建后处理(可选)
└── PostProcessBuild_InfoPlist.cs
6. Android 接入
6.1 配置依赖
编辑 Assets/CSJ/Editor/PangleAdapterScriptsDependencies.xml:
xml
<androidPackages>
<repositories>
<repository>https://artifact.bytedance.com/repository/pangle</repository>
</repositories>
<!-- 聚合 adapter,穿山甲本体不需要 adapter -->
<androidPackage spec="com.pangle.cn:mediation-baidu-adapter:9.4503.0"/>
<androidPackage spec="com.pangle.cn:mediation-sigmob-adapter:4.25.14.0"/>
<!-- 按需添加其他 ADN -->
</androidPackages>
6.2 Android 本地配置(JSON 字符串)
Android 使用 CustomLocalConfig 直接传 JSON 字符串:
csharp
private MediationConfig GetMediationConfigAndroid()
{
var mediationConfig = new MediationConfig();
// 从 Resources 读取导出的配置文件
var asset = Resources.Load<TextAsset>("GroMore/site_config_5786***");
if (asset != null)
{
mediationConfig.CustomLocalConfig = asset.text; // 仅 Android
}
return mediationConfig;
}
6.3 Android 权限
确保 AndroidManifest.xml 包含网络权限(SDK 通常会自动合并):
xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7. iOS 接入
iOS 比 Android 多三个关键步骤:ATT 授权、IDFA 配置、本地配置文件路径。
7.1 配置 CocoaPods 依赖
同样在 PangleAdapterScriptsDependencies.xml 中配置 iOS Pod:
xml
<iosPods>
<!-- 穿山甲 SDK + 聚合 -->
<iosPod name="Ads-CN" version="7.6.0.4"
bitcodeEnabled="true" minTargetSdk="10.0"
subspecs="['CSJMediation-Only']" />
<!-- 第三方 ADN adapter(按需添加) -->
<iosPod name="BaiduMobAdSDK" version="<=10.050" />
<iosPod name="GMBaiduAdapter" version="10.050.0" />
</iosPods>
7.2 配置 Info.plist
必须添加 ATT 授权说明,否则无法获取 IDFA:
xml
<key>NSUserTrackingUsageDescription</key>
<string>为了向您提供更好的广告体验,我们需要获取您的设备标识符</string>
可用 Unity 构建后处理脚本自动写入(PostProcessBuild_InfoPlist.cs):
csharp
[PostProcessBuild(999)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target != BuildTarget.iOS) return;
string plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
PlistDocument plist = new PlistDocument();
plist.ReadFromFile(plistPath);
plist.root.SetString("NSUserTrackingUsageDescription",
"为了向您提供更好的广告体验,我们需要获取您的设备标识符");
plist.WriteToFile(plistPath);
}
7.3 ATT 授权管理器
iOS 14.5+ 必须先弹 ATT 授权窗,再初始化广告 SDK:
csharp
// ATTManager.cs(简化示例)
public class ATTManager : MonoBehaviour
{
#if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void _RequestTrackingAuthorization(
string callbackTarget, string callbackMethod);
public void RequestAuthorization(Action<int> callback)
{
_authorizationCallback = callback;
_RequestTrackingAuthorization(gameObject.name, "OnATTCallback");
}
// 由 iOS 原生回调
private void OnATTCallback(string statusCode)
{
_authorizationCallback?.Invoke(int.Parse(statusCode));
}
#endif
}
ATT 状态说明:
| 状态 | 含义 | 处理方式 |
|---|---|---|
| Authorized (3) | 用户同意 | 使用真实 IDFA |
| Denied (2) | 用户拒绝 | 使用自定义设备标识 |
| NotDetermined (0) | 未选择 | 使用自定义设备标识 |
7.4 配置自定义 IDFA
必须在 Pangle.Init() 之前调用:
csharp
public bool ConfigureCustomIdfa()
{
var config = PangleConfiguration.CreateInstance();
config.customIdfa = GetCustomIdfa(); // 根据 ATT 状态返回 IDFA 或设备标识
return true;
}
private string GetCustomIdfa()
{
#if UNITY_IOS && !UNITY_EDITOR
if (ATTManager.Instance.IsAuthorized())
return GetRealIdfa(); // 真实 IDFA
else
return SystemInfo.deviceUniqueIdentifier; // 兜底标识
#else
return SystemInfo.deviceUniqueIdentifier;
#endif
}
7.5 iOS 本地配置(文件路径方式)
iOS 不支持 CustomLocalConfig 字符串,必须用文件路径:
官方原生写法:
objc
configuration.mediation.advanceSDKConfigPath =
[[NSBundle mainBundle] pathForResource:@"GroMore-config-ios" ofType:@"json"];
Unity 中需要通过 DllImport 调用原生方法。
第一步:在 PangleConfiguration.mm 添加原生函数
objc
void UnionPlatform_setAdvanceSDKConfigPath(const char* path) {
if (path == NULL) return;
NSString *oc_path = [NSString stringWithUTF8String:path];
BUAdSDKConfiguration *configuration = [BUAdSDKConfiguration configuration];
configuration.mediation.advanceSDKConfigPath = oc_path;
NSLog(@"CSJM_Unity setAdvanceSDKConfigPath: %@", oc_path);
}
第二步:在 C# 中调用(必须在 Pangle.Init() 之前)
csharp
#if !UNITY_EDITOR && UNITY_IOS
[DllImport("__Internal")]
private static extern void UnionPlatform_setAdvanceSDKConfigPath(string path);
private void SetupGroMoreLocalConfig()
{
var asset = Resources.Load<TextAsset>("GroMore/site_config_5786***");
if (asset == null) return;
// 写入持久化目录(iOS 需要真实文件路径)
string path = Path.Combine(Application.persistentDataPath, "site_config.json");
File.WriteAllText(path, asset.text);
UnionPlatform_setAdvanceSDKConfigPath(path);
Debug.Log($"✅ GroMore本地配置已设置: {path}");
}
#endif
8. SDK 初始化(核心)
8.1 正确的初始化时序
这是整个接入最关键的部分,顺序不能错:
text
① ATT 授权弹窗(iOS)
↓
② ConfigureCustomIdfa()(iOS,Pangle.Init 之前)
↓
③ SetupGroMoreLocalConfig()(iOS,Pangle.Init 之前)
↓
④ Pangle.Init(configuration) ← 预初始化
↓
⑤ Pangle.Start(callback) ← 正式启动
↓
⑥ 回调 success=true 后,延迟 2 秒
↓
⑦ 预加载激励视频 + 插屏广告
8.2 启动入口(GameLaunch.cs)
csharp
private void Start()
{
#if UNITY_IOS && !UNITY_EDITOR
InitializeIOSPlatform();
#endif
}
private void InitializeIOSPlatform()
{
// 第一步:ATT 授权
ATTManager.Instance.RequestAuthorizationOnLaunch(status =>
{
// 第二步:配置 IDFA
AdManager.Instance.ConfigureCustomIdfa();
// 第三步:初始化 SDK
AdManager.Instance.InitializeCSJSDKAfterATT();
});
}
8.3 构建 SDK 配置
csharp
private const string APP_ID = "5786***"; // 换成你的
private const string APP_NAME = "你的应用名";
private SDKConfiguration BuildSdkConfiguration()
{
var builder = new SDKConfiguration.Builder()
.SetAppId(APP_ID)
.SetAppName(APP_NAME)
.SetUseMediation(true) // 开启 GroMore 聚合
.SetDebug(true) // 开发阶段开启,上线关闭
.SetThemeStatus(0)
.SetSupportMultiProcesse(false)
.SetPrivacyConfigurationn(GetPrivacyConfiguration());
#if UNITY_ANDROID
builder.SetMediationConfig(GetMediationConfigAndroid());
#endif
return builder.Build();
}
private PrivacyConfiguration GetPrivacyConfiguration()
{
var config = new PrivacyConfiguration();
config.CanUseLocation = false;
config.CanUsePhoneState = false;
config.CanUseWifiState = false;
config.CanUseWriteExternal = false;
config.CanUseAndroidId = false;
config.MediationPrivacyConfig = new MediationPrivacyConfig();
config.MediationPrivacyConfig.LimitPersonalAds = false;
config.MediationPrivacyConfig.ProgrammaticRecommend = true;
return config;
}
8.4 执行初始化
csharp
private bool _csjInitialized;
public void InitializeCSJSDKAfterATT()
{
if (_csjInitialized) return;
#if !UNITY_EDITOR && UNITY_IOS
SetupGroMoreLocalConfig(); // 必须在 Init 之前
#endif
Pangle.Init(BuildSdkConfiguration());
Pangle.Start(OnSdkInitCallback);
}
private void OnSdkInitCallback(bool success, string message)
{
Debug.Log($"SDK初始化: success={success}, message={message}");
Debug.Log($"IsSdkReady: {Pangle.IsSdkReady()}");
if (!success) return;
_csjInitialized = true;
StartCoroutine(DelayedInitialAdLoad());
}
private IEnumerator DelayedInitialAdLoad()
{
yield return new WaitForSeconds(2f); // 等 SDK 完全就绪
LoadRewardedAd();
LoadInterstitialAd();
}
9. 激励视频接入
9.1 官方推荐的完整流程
text
LoadRewardVideoAd()
→ OnRewardVideoAdLoad() // 加载成功,但还不能展示
→ OnRewardVideoCached() // 缓存完成,可以展示了
→ IsReady() 检查
→ ShowRewardVideoAd()
→ OnRewardArrived() // 发放奖励
→ OnAdClose() // 关闭,销毁对象,预加载下一条
9.2 构造 AdSlot
csharp
private AdSlot BuildRewardAdSlot()
{
return new AdSlot.Builder()
.SetCodeId(REWARDED_AD_UNIT_ID)
.SetOrientation(AdOrientation.Horizontal) // 横屏游戏用 Horizontal
.SetMediationAdSlot(
new MediationAdSlot.Builder()
.SetMuted(false)
.Build())
.Build();
}
9.3 加载广告
csharp
private RewardVideoAd _rewardedAd;
private bool _rewardedCached;
private void LoadRewardedAd()
{
// 官方要求:每次加载前销毁旧对象
if (_rewardedAd != null)
{
_rewardedAd.Dispose();
_rewardedAd = null;
}
_rewardedCached = false;
SDK.CreateAdNative().LoadRewardVideoAd(
BuildRewardAdSlot(),
new RewardedAdListener(this));
}
9.4 加载监听器
csharp
private class RewardedAdListener : IRewardVideoAdListener
{
private readonly AdManager _manager;
public RewardedAdListener(AdManager manager) => _manager = manager;
public void OnError(int code, string message)
{
Debug.LogError($"激励视频加载失败: {code} {message}");
// 官方建议:仅重试 1 次
_manager.RetryLoadRewarded();
}
public void OnRewardVideoAdLoad(RewardVideoAd ad)
{
Debug.Log("激励视频加载成功,等待缓存...");
_manager._rewardedAd = ad;
// 注意:这里不要标记为 ready!
}
public void OnRewardVideoCached()
{
Debug.Log("激励视频缓存完成,可以展示");
_manager._rewardedCached = true;
}
public void OnRewardVideoCached(RewardVideoAd ad)
{
_manager._rewardedAd = ad;
_manager._rewardedCached = true;
}
}
9.5 展示前检查(重要)
csharp
private bool IsRewardVideoReady()
{
if (_rewardedAd == null || !_rewardedCached) return false;
var manager = _rewardedAd.GetMediationManager();
return manager != null && manager.IsReady();
}
public bool ShowRewardedAd()
{
if (!IsRewardVideoReady())
{
Debug.LogWarning("激励视频未就绪");
LoadRewardedAd(); // 触发加载
return false;
}
_rewardedAd.SetRewardAdInteractionListener(new RewardedInteractionListener(this));
_rewardedAd.ShowRewardVideoAd();
return true;
}
尊敬的主人,发奖逻辑已按你们项目里的最新实现整理如下,可直接替换博客 9.6 展示监听器 一节。
9.6 展示监听器(发奖逻辑)
激励视频的发奖不要写在 OnRewardArrived 里直接发,推荐采用「先记录、关闭时统一处理」的模式,避免广告界面还没关完,游戏就已经恢复运行。
设计原则
| 回调 | 职责 |
|---|---|
OnRewardArrived |
只记录 _rewardArrived、_rewardValid,不发奖 |
OnAdClose |
统一发奖 / 通知未完成,并做埋点 |
完整示例
csharp
private class RewardedInteractionListener : IRewardAdInteractionListener
{
private readonly AdManager _manager;
// 是否已收到 OnRewardArrived;未收到表示用户极早退出
private bool _rewardArrived;
// OnRewardArrived 返回的奖励是否有效;仅在 _rewardArrived 为 true 时有意义
private bool _rewardValid;
public RewardedInteractionListener(AdManager manager) => _manager = manager;
public void OnAdShow()
{
_rewardArrived = false;
_rewardValid = false;
// 埋点:广告开始展示
Analytics.Log("Ads_re_Show");
}
public void OnRewardArrived(bool isRewardValid, int rewardType, IRewardBundleModel extraInfo)
{
Debug.Log($"OnRewardArrived verify:{isRewardValid} rewardType:{rewardType}");
// 仅记录奖励结果,等 OnAdClose 再发奖/通知,避免广告未关时游戏已恢复
_rewardArrived = true;
_rewardValid = isRewardValid;
}
public void OnAdClose()
{
_manager._rewardedShowing = false;
if (!_rewardArrived)
{
// 极早退出:连奖励回调都没收到
Debug.Log("广告关闭时未收到奖励回调,视为未完整播放");
_manager.OnRewardAdNoCompleteClose?.Invoke();
}
else if (_rewardValid)
{
// 广告界面关闭后再发放奖励,避免关闭动画期间游戏已恢复运行
_manager.GrantRewarded();
}
else
{
// 收到了回调但 isRewardValid=false(未看完)
Debug.Log("激励视频未播放完成,不发放奖励");
_manager.OnRewardAdNoCompleteClose?.Invoke();
}
// 埋点
Analytics.Log("Ads_re_Close");
// 销毁并预加载下一条
_manager._rewardedAd?.Dispose();
_manager._rewardedAd = null;
_manager.LoadRewardedAd();
}
public void OnVideoComplete() { }
public void OnVideoError() { _manager.HandleRewardAdShowOrLoadFailure(); }
public void OnVideoSkip() { }
public void OnAdVideoBarClick() { }
}
三种关闭场景
text
场景 A:用户极早退出(未触发 OnRewardArrived)
OnAdClose → !_rewardArrived → 通知未完成
场景 B:正常看完(OnRewardArrived 且 isRewardValid=true)
OnRewardArrived → 记录 _rewardArrived=true, _rewardValid=true
OnAdClose → GrantRewarded() 发奖
场景 C:中途跳过(OnRewardArrived 但 isRewardValid=false)
OnRewardArrived → 记录 _rewardArrived=true, _rewardValid=false
OnAdClose → 通知未完成,不发奖
10. 插屏广告接入
插屏使用 全屏视频广告(FullScreenVideoAd),流程与激励视频几乎相同。
10.1 构造 AdSlot
csharp
private AdSlot BuildInterstitialAdSlot()
{
return new AdSlot.Builder()
.SetCodeId(INTERSTITIAL_AD_UNIT_ID)
.SetOrientation(AdOrientation.Horizontal)
.SetMediationAdSlot(
new MediationAdSlot.Builder()
.SetMuted(false)
.Build())
.Build();
}
10.2 加载与展示
csharp
private FullScreenVideoAd _interstitialAd;
private bool _interstitialCached;
private void LoadInterstitialAd()
{
if (_interstitialAd != null)
{
_interstitialAd.Dispose();
_interstitialAd = null;
}
_interstitialCached = false;
SDK.CreateAdNative().LoadFullScreenVideoAd(
BuildInterstitialAdSlot(),
new InterstitialAdListener(this));
}
private bool IsInterstitialReady()
{
if (_interstitialAd == null || !_interstitialCached) return false;
var manager = _interstitialAd.GetMediationManager();
return manager != null && manager.IsReady();
}
public bool ShowInterstitialAd()
{
if (!IsInterstitialReady())
{
LoadInterstitialAd();
return false;
}
_interstitialAd.SetFullScreenVideoAdInteractionListener(
new InterstitialInteractionListener(this));
_interstitialAd.ShowFullScreenVideoAd();
return true;
}
10.3 关闭后预加载
csharp
public void OnAdClose()
{
_manager._interstitialAd?.Dispose();
_manager._interstitialAd = null;
_manager.LoadInterstitialAd(); // 预加载下一条
}
11. 打包与真机测试
11.1 Android 打包
File → Build Settings → Android- 确认
PangleAdapterScriptsDependencies.xml中的依赖已解析 - 导出 APK/AAB,安装到真机测试
11.2 iOS 打包
File → Build Settings → iOS → Build- 用 Xcode 打开工程
- 确认
pod install成功(Unity 会自动触发) - 在 Xcode 中编译安装到真机
11.3 测试日志检查点
正常启动应看到如下日志顺序:
text
[ATTManager] 开始请求ATT授权
[ATTManager] ATT授权结果: Authorized
✅ 自定义IDFA配置成功
✅ GroMore本地配置已设置(路径: ...)
========== 开始调用Pangle.Init() ==========
SDK初始化: success=True
SDK初始化成功,2秒后开始加载广告
开始加载激励视频广告
激励视频缓存完成,可以展示 ← 关键!
11.4 注册测试设备
日志中找到 IDFA,例如:
text
真实IDFA: B251761C-CCFC-4D9E-89E1-184032BE3624
到穿山甲后台 → 测试工具 → 添加此 IDFA,等 5 分钟后重新测试。
12. 常见错误排查
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 40045 | 找不到广告位配置(rit) | ① 检查广告位 ID 是否正确 ② iOS 设置本地配置文件 ③ 检查网络是否正常 |
| 10086 | 瀑布流全部 ADN 填充失败 | ① 注册测试设备 IDFA ② 检查瀑布流 ADN 配置 ③ 增加 ADN 数量 |
| 10010 | 请求超时 | ① 延长瀑布流超时时长 ② 减少瀑布流层数 ③ 检查网络 |
| SDK 未初始化 | 初始化时序错误 | 确认 ATT → IDFA → Init → Start 顺序 |
| 广告加载成功但不展示 | 未等 Cached 回调 | 必须在 OnRewardVideoCached 后才展示 |
| iOS 审核广告不显示 | 审核环境无广告填充 | 开启审核容错模式,失败时直接发奖励 |
调试技巧:打印聚合加载详情
csharp
private void LogMediationLoadInfo(RewardVideoAd ad, string scene)
{
var manager = ad?.GetMediationManager();
if (manager == null) return;
foreach (var info in manager.GetAdLoadInfo())
{
Debug.Log($"{scene} LoadInfo: {info}");
// 可以看到每个 ADN 的失败原因
}
}
在 OnError 和 OnRewardVideoCached 中调用,可精确定位哪个 ADN 出了问题。
13. 上线前检查清单
代码层面
-
SetDebug(false)关闭调试日志 - 广告位 ID 换成正式环境的 ID
-
site_config_xxx.json是最新导出的配置 - 移除或关闭审核容错模式(
REVIEW_MODE_ENABLED = false) - 确认 ATT 授权弹窗文案符合苹果审核要求
后台层面
- 瀑布流至少接入 3 个 ADN
- 各 ADN 的 App ID / Slot ID 配置正确
- 测试设备 IDFA 已注册(测试阶段)
- Bundle ID / 包名与后台一致
iOS 审核
-
NSUserTrackingUsageDescription已配置 - 广告失败时有合理的用户体验(不能太突兀)
- 提审期间可开启审核容错:广告失败时直接发放奖励,保证审核员能体验完整流程
附录:完整初始化时序图
text
App 启动
│
├─ [iOS] ATTManager.RequestAuthorization()
│ └─ 用户选择 允许/拒绝
│
├─ [iOS] ConfigureCustomIdfa()
│ └─ PangleConfiguration.customIdfa = IDFA
│
├─ [iOS] SetupGroMoreLocalConfig()
│ └─ UnionPlatform_setAdvanceSDKConfigPath(path)
│
├─ Pangle.Init(SDKConfiguration)
│ └─ 预初始化(合规要求,此时不联网)
│
├─ Pangle.Start(callback)
│ └─ 正式启动,拉取线上配置
│ └─ callback(success=true)
│
├─ 延迟 2 秒
│
├─ LoadRewardedAd() ← 预加载激励
└─ LoadInterstitialAd() ← 预加载插屏
用户点击"看广告"
│
├─ IsRewardVideoReady() ?
│ ├─ false → 触发加载,显示等待 UI
│ └─ true → ShowRewardVideoAd()
│
├─ OnAdShow()
├─ OnRewardArrived() → 标记发放奖励
└─ OnAdClose()奖励发放 → Dispose() → LoadRewardedAd()(预加载下一条)
总结
接入穿山甲 GroMore 聚合的核心要点:
- 时序:ATT → IDFA → 本地配置 → Init → Start → 预加载
- 展示 :必须等
OnCached+IsReady()才能展示 - 生命周期 :每次加载前
Dispose()旧对象,关闭后预加载新对象 - 本地配置:Android 用 JSON 字符串,iOS 用文件路径
- 测试 :必须注册测试设备,否则大量
10086错误
按本文步骤操作,新手也能在 Unity 项目中独立完成接入。遇到问题时,优先查看 getAdLoadInfoList 的聚合加载详情,比盲目重试有效得多。
本文基于 Unity + 穿山甲 GroMore SDK 7.x 版本实战经验整理。SDK 版本更新时请以官方文档为准。