1. 项目概述:为什么Unity游戏必须关注广告变现?
如果你是一名Unity游戏开发者,无论是独立开发者还是身处中小型团队,我相信“如何让游戏赚钱”这个问题,一定在你的待办清单里占据了重要位置。在众多变现模式中,广告变现,尤其是激励视频广告,因其对免费玩家体验的友好性和稳定的收益能力,成为了许多休闲、超休闲乃至中度游戏的首选。而在国内移动广告市场,穿山甲(Ocean Engine)无疑是一个你无法绕开的平台。它背靠庞大的流量生态,提供了从开屏、信息流到激励视频、全屏视频等丰富的广告形式,其填充率和eCPM(每千次展示有效收益)在业内也颇具竞争力。
然而,将穿山甲广告SDK接入到Unity项目中,远不是下载一个插件、拖拽几个Prefab那么简单。这个过程涉及到Unity与Android/iOS原生平台的交互、复杂的配置流程、各种权限和依赖项的适配,以及上线前后必须关注的策略与优化。网上能找到的官方文档或零散教程,要么过于简略跳过了关键细节,要么版本陈旧已不适用,导致开发者在接入过程中频频踩坑,轻则广告无法正常加载,重则导致应用崩溃或审核被拒。
这篇文章,就是我结合多次从零到一成功接入穿山甲SDK并上线的实战经验,为你梳理的一份全流程指南。我会假设你是一个对Unity熟悉,但对原生平台开发和广告集成相对陌生的开发者,用最直白的语言,拆解每一个步骤背后的逻辑,并附上那些官方文档不会告诉你的“避坑指南”。我们的目标很明确:让你能跟着步骤,稳扎稳打地完成接入,并把那些常见的“雷”提前排掉。
2. 接入前的核心准备与环境搭建
在开始敲代码之前,充分的准备工作能让你在后续的流程中事半功倍。这个阶段的核心是理清思路、备齐工具、创建正确的工程环境。
2.1 明确你的技术栈与目标平台
首先,你需要明确你的开发环境和技术选型,这直接决定了后续的接入路径。
- Unity版本:穿山甲SDK对Unity版本有一定要求。通常,建议使用Unity 2018.4 LTS或更高版本,尤其是2021 LTS和2022 LTS是当前比较稳定且兼容性好的选择。避免使用过于前沿的预览版(Alpha/Beta),以免遇到未知的兼容性问题。你可以在Unity Hub中清晰地管理多个版本。
- 目标平台:国内市场主要面向Android和iOS。本文将以Android平台的接入作为主要讲解对象,因为其流程更具代表性,且问题通常更多。iOS的接入逻辑类似,但证书、描述文件等配置是在Xcode和苹果开发者网站完成,我会在关键点指出iOS的差异。
- 开发环境:
- Android:你需要安装并配置好Android SDK和JDK。确保你的环境中
JAVA_HOME和ANDROID_HOME(或ANDROID_SDK_ROOT)环境变量正确设置。一个常见的坑是使用Unity内置的JDK版本可能过旧,建议单独安装JDK 8或JDK 11,并在Unity的Preferences -> External Tools中指定路径。 - iOS:需要一台Mac电脑,安装最新稳定版的Xcode,并拥有有效的Apple Developer账号。
- Android:你需要安装并配置好Android SDK和JDK。确保你的环境中
2.2 获取穿山甲开发者账号与关键信息
这是接入的“门票”和“身份标识”,必须在编码前拿到。
注册与登录:访问穿山甲开发者平台,使用手机号或邮箱注册账号并完成企业或个人开发者认证。个人开发者适合独立开发者,但某些广告形式或更高权限可能需要企业资质。
创建应用与广告位:
- 在控制台点击“创建应用”,填写你的游戏名称、平台(Android/iOS)、包名等。这里的包名必须与你Unity项目中
Player Settings里设置的Bundle Identifier(Android叫Package Name)完全一致,一个字符都不能差,否则后续所有请求都会失败。 - 应用创建后,你需要为它创建“广告位”。广告位是广告请求的逻辑单元。例如,你可以创建一个“激励视频广告位”用于观看奖励,创建一个“插屏广告位”用于关卡结束。每个广告位都会生成一个唯一的代码位ID(Slot ID或Ad Unit ID)。这个ID是SDK区分在哪里展示什么广告的核心依据,务必妥善保管。
- 在控制台点击“创建应用”,填写你的游戏名称、平台(Android/iOS)、包名等。这里的包名必须与你Unity项目中
下载SDK:在穿山甲开发者平台的文档中心,找到Unity版本的SDK进行下载。建议总是下载最新稳定版的SDK。同时,最好也下载一份最新的官方接入文档作为离线参考。
2.3 创建与配置Unity工程
一个好的工程习惯能避免很多混乱。
- 新建或清理工程:建议为一个新的广告接入测试创建一个干净的Unity工程,或者在你现有工程的备份上进行操作。确保工程路径没有中文或特殊字符。
- 设置包名与版本:打开
File -> Build Settings -> Player Settings。- Android:在
Other Settings里,找到Identification部分,正确填写Package Name(例如:com.YourCompany.YourGame)。Minimum API Level建议设置为API Level 21 (Android 5.0)或以上,以覆盖绝大多数用户。Target API Level建议设置为你能编译通过的最高版本(如33),以满足应用商店的要求。 - iOS:在
Identification部分,填写Bundle Identifier,同样需要与穿山甲后台设置一致。
- Android:在
- 导入SDK:将下载的穿山甲Unity SDK包(通常是一个
.unitypackage文件)直接拖入Unity的Project窗口,在弹出的导入对话框中,通常全选所有文件并点击Import。导入后,你会在Project中看到类似PangleSDK或OceanEngine_Ads的文件夹。
注意:导入SDK后,务必检查Unity Console是否有报错。常见的错误包括:DLL冲突、AndroidManifest合并错误等。如果有报错,先根据错误信息解决,不要带着错误进行下一步。
3. SDK核心模块解析与初始化流程
成功导入SDK后,我们首先要理解它的核心构成,并完成最关键的初始化步骤。这一步是广告能正常工作的基石。
3.1 理解穿山甲SDK的架构
穿山甲Unity SDK通常采用分层设计,对开发者暴露的是一个统一的C#接口层,底层则通过Android的jar/aar包和iOS的framework与原生平台通信。你需要了解几个关键对象:
- SDK: 全局管理类,负责SDK的初始化、权限申请、全局配置等。
- AdSlot: 广告请求配置类。它包含了广告位ID、广告类型、尺寸、方向等所有关于“你要请求一个什么样的广告”的信息。你可以把它看作一个“广告订单模板”。
- 激励视频广告 (RewardedVideoAd)、插屏广告 (InterstitialAd)、Banner广告: 这些是具体的广告交互类。你需要为每一种广告类型创建对应的实例,并为其设置监听器(Listener)来接收广告加载、展示、奖励发放等回调事件。
这种设计模式是典型的“配置-实例-回调”模式,清晰地将广告的定义、生命周期管理和事件反馈分离。
3.2 编写初始化脚本
初始化必须在任何广告请求之前进行,通常放在游戏启动的早期,例如一个启动场景的Awake或Start方法中。
using UnityEngine; using Pangle; // 或具体的命名空间,根据SDK版本可能为OceanEngine.Ads等 public class PangleAdManager : MonoBehaviour { private string _androidAppId = "你的Android应用App ID"; private string _iOSAppId = "你的iOS应用App ID"; private bool _isTestMode = true; // 上线前务必改为 false! void Start() { InitializePangleSDK(); } void InitializePangleSDK() { // 1. 设置测试模式(非常重要!) // 在开发阶段,务必开启测试模式,使用测试广告位ID,避免产生无效流量导致封号。 PangleSDK.SetIsPaid(false); // 非付费应用 PangleSDK.SetTestMode(_isTestMode); // 2. 准备初始化配置 var config = new PangleConfig.Builder() .AppId(GetPlatformAppId()) // 根据平台获取对应的App ID .UseTextureView(true) // Android上建议使用TextureView以获得更好的兼容性 .AllowShowNotify(true) // 是否允许通知,根据游戏类型决定 .AllowShowPageWhenScreenLock(true) // 锁屏下是否展示,通常为false .DebugLog(true) // 开启调试日志,方便排查问题 .Build(); // 3. 执行初始化 PangleSDK.Init(config, (bool success, string message) => { if (success) { Debug.Log("穿山甲SDK初始化成功!"); // 初始化成功后,可以开始预加载广告 PreloadRewardedVideoAd(); } else { Debug.LogError($"穿山甲SDK初始化失败: {message}"); // 处理初始化失败,可能是网络问题或App ID错误 } }); } string GetPlatformAppId() { #if UNITY_ANDROID return _androidAppId; #elif UNITY_IOS return _iOSAppId; #else return ""; #endif } }关键点与避坑指南:
- 测试模式开关:
_isTestMode这个变量至关重要。在开发、调试、内部测试阶段,必须设置为true。这会让你请求到穿山甲提供的测试广告,确保功能正常,同时绝对不会产生真实的广告计费和展示数据。如果你在测试阶段使用了真实的广告位ID并关闭了测试模式,产生的任何展示或点击都可能被视为“开发者自刷”,严重违反平台规则,会导致应用ID或广告位ID被封禁。上线前,记得将其改为false。 - App ID:Android和iOS的应用App ID是不同的,务必从各自平台的后台获取并正确填写。混淆使用会导致初始化失败。
- 异步回调:初始化是网络操作,因此是异步的。你的后续逻辑(比如预加载广告)一定要放在初始化成功的回调里执行,否则可能因为SDK未就绪而失败。
- 权限配置:初始化配置中的
AllowShowNotify等选项,需要根据你游戏的实际情况决定。例如,如果你的游戏是全屏沉浸式体验,可能不希望被通知打扰,可以设置为false。
4. 各类型广告的集成与实现细节
初始化成功后,我们就可以开始集成具体的广告形式了。激励视频是变现效率最高的形式之一,我们就以它为例,详细拆解从创建到展示的完整流程。
4.1 激励视频广告集成全步骤
激励视频广告的流程可以概括为:创建广告位配置 -> 加载广告 -> 监听加载结果 -> 检查并展示 -> 处理奖励回调。
public class PangleAdManager : MonoBehaviour { private string _rewardedVideoSlotId = "你的激励视频广告位ID"; private RewardedVideoAd _rewardedVideoAd; private bool _isRewardedVideoLoaded = false; void PreloadRewardedVideoAd() { // 1. 创建广告请求配置 (AdSlot) AdSlot adSlot = new AdSlot.Builder() .SetCodeId(_rewardedVideoSlotId) .SetRewardName("金币") // 奖励名称,用于后台数据统计,需与客户端发放的奖励对应 .SetRewardAmount(100) // 奖励数量 .SetUserID("optional_user_id") // 可选,设置用户ID用于服务端验证 .SetMediaExtra("optional_extra") // 可选,额外信息 .SetOrientation(AdOrientation.Vertical) // 广告方向,通常竖屏 .Build(); // 2. 创建激励视频广告实例 if (_rewardedVideoAd != null) { _rewardedVideoAd.Dispose(); // 销毁旧的实例,避免内存泄漏 } _rewardedVideoAd = new RewardedVideoAd(adSlot); // 3. 设置广告事件监听器 _rewardedVideoAd.SetRewardInteractionListener(new RewardInteractionListener(this)); _rewardedVideoAd.SetFullScreenVideoInteractionListener(new FullScreenInteractionListener(this)); // 4. 加载广告 _rewardedVideoAd.LoadAd(); Debug.Log("开始加载激励视频广告..."); } // 提供给UI按钮调用的方法 public void ShowRewardedVideoAd() { if (_isRewardedVideoLoaded && _rewardedVideoAd != null) { // 展示广告前,可以暂停游戏背景音乐、计时器等 PauseGame(); _rewardedVideoAd.ShowRewardVideoAd(); } else { Debug.LogWarning("激励视频广告尚未加载完成,无法播放。"); // 可以给玩家一个提示,如“广告加载中,请稍后” ShowToast("广告正在加载,请稍候..."); // 尝试重新加载 PreloadRewardedVideoAd(); } } // 内部类:处理奖励互动回调 private class RewardInteractionListener : IRewardInteractionListener { private PangleAdManager _manager; public RewardInteractionListener(PangleAdManager manager) { _manager = manager; } public void OnAdShow() { Debug.Log("激励视频广告开始展示。"); } public void OnAdVideoBarClick() { Debug.Log("激励视频广告被点击。"); } public void OnAdClose() { Debug.Log("激励视频广告关闭。"); // 广告关闭后,恢复游戏 _manager.ResumeGame(); // 广告关闭后,无论是否获得奖励,都可以开始预加载下一个广告 _manager.PreloadRewardedVideoAd(); } // 这是最关键的回调!只有收到此回调,才能给玩家发放奖励。 public void OnVideoComplete() { Debug.Log("激励视频播放完成。"); } public void OnVideoError(string error) { Debug.LogError($"激励视频播放出错: {error}"); _manager.ResumeGame(); } // 服务器验证奖励有效回调(需要配置服务端验证) public void OnRewardVerify(bool verify, string info) { Debug.Log($"奖励验证结果: {verify}, 信息: {info}"); if (verify) { _manager.GrantRewardToPlayer(); } } } // 内部类:处理全屏视频交互回调(如加载状态) private class FullScreenInteractionListener : IFullScreenVideoInteractionListener { private PangleAdManager _manager; public FullScreenInteractionListener(PangleAdManager manager) { _manager = manager; } public void OnAdLoad(RewardedVideoAd ad, AdSlot slot) { Debug.Log("激励视频广告加载成功。"); _manager._isRewardedVideoLoaded = true; // 可以在这里更新UI按钮状态,比如将“看广告”按钮变为可点击状态 _manager.UpdateAdButtonState(true); } public void OnAdLoadFail(int code, string message) { Debug.LogError($"激励视频广告加载失败,错误码: {code}, 消息: {message}"); _manager._isRewardedVideoLoaded = false; _manager.UpdateAdButtonState(false); // 可以记录失败原因,用于分析填充率问题 } public void OnAdShow(RewardedVideoAd ad) { } public void OnAdClick(RewardedVideoAd ad) { } public void OnAdClose(RewardedVideoAd ad) { } } // 以下是游戏逻辑相关方法示例 private void PauseGame() { /* 暂停游戏逻辑、音乐 */ } private void ResumeGame() { /* 恢复游戏逻辑、音乐 */ } private void UpdateAdButtonState(bool isReady) { /* 更新UI按钮 */ } private void ShowToast(string msg) { /* 显示提示信息 */ } private void GrantRewardToPlayer() { Debug.Log("向玩家发放奖励!"); // 这里增加玩家的金币、道具等 // 注意:强烈建议结合 OnRewardVerify 服务端验证来确保奖励发放的安全 } }避坑指南与最佳实践:
- 奖励发放时机:这是最容易出错的地方。绝对不能在
OnAdClose(广告关闭)或OnVideoComplete(视频播放完成)回调里直接发放奖励。因为用户可能在视频播放中途就关闭广告,或者网络问题导致播放未完成。穿山甲平台要求,必须在收到OnRewardVerify(验证成功)或(如果未配置服务端验证)在收到OnVideoComplete回调后,才能发放奖励。这是防止奖励被滥用的关键。 - 广告预加载:不要在玩家点击“看广告”按钮时才去加载广告,那会带来数秒的等待,极大损害体验。应该在初始化成功后、或者上一个广告关闭后,立即调用
PreloadRewardedVideoAd预加载下一个广告。_isRewardedVideoLoaded这个状态标志位就是用来管理广告是否就绪的。 - 实例管理:每次展示广告后,旧的
RewardedVideoAd实例就失效了。在预加载新广告前,务必调用_rewardedVideoAd.Dispose()释放旧资源,再创建新的实例。否则会导致内存泄漏或加载异常。 - 错误处理:
OnAdLoadFail回调会提供错误码和消息。常见的错误码如20001(网络错误)、30001(包名/App ID不匹配)、40001(广告位ID错误)等。你需要根据这些信息进行相应的处理或提示。
4.2 插屏与Banner广告集成要点
插屏广告和Banner广告的集成模式与激励视频类似,但更简单。
- 插屏广告:通常用于关卡之间、暂停界面等。其加载和展示流程与激励视频几乎一致,只是类名换成了
InterstitialAd,并且没有奖励相关的回调(OnVideoComplete,OnRewardVerify)。同样需要注意预加载和实例管理。 - Banner广告:这是一种常驻的矩形广告。集成时需要额外指定广告的尺寸(如320x50, 300x250)和位置。Banner广告通常不需要频繁加载和销毁,可以在场景初始化时创建并显示,在不需要时隐藏。注意Banner广告可能会遮挡游戏UI,需要精心设计其摆放位置。
// Banner广告简单示例 public void CreateAndShowBanner() { AdSlot bannerSlot = new AdSlot.Builder() .SetCodeId(_bannerSlotId) .SetImageAcceptedSize(300, 250) // 设置期望的Banner尺寸 .Build(); _bannerAd = new BannerAd(bannerSlot); _bannerAd.SetInteractionListener(new MyBannerListener()); _bannerAd.LoadAd(); // 加载成功后,通过 _bannerAd.ShowBannerAd(Rect position) 在指定位置显示 }5. Android平台专项配置与打包避坑
Unity项目最终需要打包成APK或AAB文件,在Android平台上,这一步的配置尤为复杂,是问题高发区。
5.1 AndroidManifest.xml 配置详解
穿山甲SDK在导入时,通常会自带一个AndroidManifest.xml文件,并尝试通过Unity的Gradle构建系统或Manifest合并功能与你的主Manifest合并。但自动合并时常出问题,手动检查和配置更稳妥。
你需要找到并检查你项目中的主AndroidManifest.xml文件(通常位于Assets/Plugins/Android目录下,如果没有,在构建时Unity会生成一个基础版本)。
需要确保包含以下关键元素:
- 权限:穿山甲SDK需要一些基本的网络和存储权限。
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 如果需要下载广告素材(如视频),可能需要 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 对于Android 10 (API 29)及以上,推荐使用Scoped Storage,可能不需要上述存储权限,具体看SDK要求 --> - Application标签内的组件:SDK需要注册一些
Activity、Service和Provider。这些通常由SDK自带的aar包提供,但需要在Manifest中声明。重点检查是否有重复声明或冲突。例如,PangleADActivity的configChanges属性必须包含orientation|keyboardHidden|screenSize。<activity android:name="com.bytedance.sdk.openadsdk.activity.TTDelegateActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:exported="false" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> <!-- 确保所有SDK声明的Activity的android:exported属性符合Android新规(Android 12+要求) --> - Queries 语句(针对 Android 11/API 30+):如果SDK需要查询其他应用的信息(例如跳转到应用商店),需要添加
<queries>标签。<queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="https" /> </intent> <!-- 可能还需要包名特定的查询 --> <package android:name="com.android.vending" /> <!-- Google Play Store --> </queries>
5.2 Gradle与依赖冲突解决
Unity默认使用内建的打包系统,但对于复杂的SDK集成,切换到Gradle构建是更推荐且更稳定的方式。
- 启用Gradle:在
File -> Build Settings -> Player Settings -> Publishing Settings(或Build System)下,将Build System从Internal改为**Gradle**。 - 配置主Gradle模板:勾选
Custom Main Gradle Template和Custom Gradle Properties Template。这会在Assets/Plugins/Android下生成mainTemplate.gradle和gradleTemplate.properties文件,让你能自定义构建配置。 - 解决依赖冲突:这是最大的坑点。穿山甲SDK可能依赖特定版本的
androidx库、Gson、OkHttp等,而你的项目或其他第三方插件(如Firebase、Unity服务)可能依赖了不同版本,导致构建失败。- 打开
mainTemplate.gradle,在dependencies块中,你可以看到所有引入的库。冲突通常表现为Duplicate class或Conflict with dependency错误。 - 解决方案是统一版本号。你可以使用Gradle的强制分辨率策略。在
dependencies块之前或allprojects块中添加:configurations.all { resolutionStrategy { // 强制指定某个库的版本 force 'com.google.code.gson:gson:2.8.9' force 'androidx.appcompat:appcompat:1.3.1' // 排除特定模块的传递依赖 // exclude group: 'com.squareup.okhttp3', module: 'okhttp' } } - 另一种方法是,仔细检查穿山甲SDK的文档,看它声明了哪些依赖,然后尝试让你的其他插件与之对齐。
- 打开
5.3 打包、安装与真机调试
- 构建APK/AAB:配置完毕后,进行构建。首次使用Gradle构建会下载大量依赖,时间较长。
- 安装到真机:构建出的APK安装到测试手机上。务必使用真机测试,模拟器无法正常播放广告,且很多权限和功能在模拟器上行为异常。
- 查看日志:在Unity编辑器中使用
adb logcat命令,或者在手机上安装日志查看App,过滤Pangle或TTAd等关键字,是排查运行时问题的最有效手段。初始化成功、广告加载、展示失败等信息都会在日志中打印。
6. 上线前必查清单与优化策略
当广告功能在测试模式下运行稳定后,在上线前,还有一系列关乎“生死”的检查项和优化点需要完成。
6.1 上线前终极检查清单
对照下表,逐项打勾确认:
| 检查项 | 说明与操作 | 不做的后果 |
|---|---|---|
| 测试模式关闭 | 将代码中_isTestMode或PangleSDK.SetTestMode参数设为false。 | 产生无效流量,导致封号。 |
| 应用包名/ID核对 | 确认Unity工程包名、穿山甲后台应用包名、代码中初始化App ID三者完全一致。 | 广告无法加载,错误码30001。 |
| 广告位ID核对 | 确认代码中使用的广告位ID与后台创建的广告位ID一致,且类型匹配(激励视频ID不能用于请求插屏)。 | 广告无法加载,错误码40001。 |
| 权限与组件 | 检查最终的合并版AndroidManifest.xml,确保权限齐全,所有Activity的exported属性正确设置(Android 12+要求)。 | 应用在部分新机型上崩溃或无法拉起广告。 |
| 混淆配置 | 如果你启用了代码混淆(ProGuard/R8),必须在混淆规则文件中添加穿山甲SDK的keep规则。规则通常由SDK提供。 | 发布包中广告相关类被混淆,导致功能异常或崩溃。 |
| 隐私合规 | 确保在初始化SDK之前,已经弹出了符合规范的《隐私政策》并获得了用户同意。只有在用户同意后,才能调用PangleSDK.Init。 | 违反法律法规,应用可能被下架。 |
| 奖励发放逻辑 | 再次确认奖励发放逻辑严格绑定在OnRewardVerify或OnVideoComplete回调中,且没有在其他地方(如OnAdClose)重复发放。 | 奖励被刷,造成经济损失;平台判定违规。 |
| 网络状态处理 | 在请求广告前,增加网络状态判断(如Application.internetReachability)。无网络时给予用户友好提示,而不是默默失败。 | 用户体验差,可能误以为功能失效。 |
6.2 性能与体验优化策略
广告预加载策略:
- 时机:在游戏启动初始化后、在非性能关键帧(如加载界面)、在上一个广告关闭后,立即预加载下一个广告。
- 数量:对于激励视频,可以预加载1-2个备用。对于插屏,可以在进入可能触发插屏的场景前预加载。
- 内存管理:不展示的广告实例及时
Dispose()。对于Banner广告,在不需要时(如进入无广告的内购界面)可以调用_bannerAd.Destroy()将其移除。
填充率与瀑布流优化:
- 穿山甲后台可以配置瀑布流。确保你的广告位接入了多个广告源(穿山甲会默认提供),并合理设置底价,以最大化填充率和收益。
- 监控穿山甲后台的数据报表,关注展示率、eCPM等指标。如果某个广告位的填充率持续很低,可以尝试调整广告类型、位置或用户定向策略。
用户体验平衡:
- 频率控制:不要过度展示广告,尤其是插屏广告。设置合理的展示间隔,避免引起玩家反感。
- 展示时机:激励视频的触发点要自然,如“复活机会”、“双倍奖励”,让玩家觉得“值得看”。插屏广告最好放在自然的断点,如关卡结束、返回主菜单时。
- 加载提示:当广告未预加载好而玩家点击时,给予明确的“广告加载中”提示,而不是让按钮无响应。
7. 疑难杂症排查与常见问题实录
即使按照指南操作,在实际开发中仍会遇到各种奇怪的问题。这里记录一些我踩过的坑和解决方案。
问题1:Unity编辑器运行正常,打包到Android后广告不加载,日志显示“未初始化”或“包名不匹配”。
- 排查:这是最经典的问题。99%的原因在于包名不一致。
- 解决:
- 用反编译工具(如
jadx-gui)打开你打出的APK,查看AndroidManifest.xml中的实际包名。 - 对比Unity
Player Settings中的包名、穿山甲后台应用配置的包名、代码中_androidAppId对应的应用包名。必须三者一字不差。 - 检查是否在打不同渠道包时,使用了不同的包名后缀,但后台只配置了一个。
- 用反编译工具(如
问题2:构建时Gradle报错,提示“Duplicate class”或“Conflict with dependency”。
- 排查:依赖冲突。
- 解决:
- 在
mainTemplate.gradle中使用resolutionStrategy.force统一关键库的版本(如前文所述)。 - 如果冲突无法解决,尝试排除冲突模块的传递依赖:
exclude group: 'xxx', module: 'xxx'。 - 检查穿山甲SDK的版本是否过旧,与当前Unity或Android Gradle Plugin版本不兼容。尝试升级到SDK最新版。
- 在
问题3:广告能加载,但点击播放后黑屏或闪退。
- 排查:
- Manifest中Activity配置错误:检查所有穿山甲相关的
Activity声明,特别是TTVideoActivity,其configChanges属性必须包含orientation|keyboardHidden|screenSize。 - 权限问题:在Android 6.0+以上,部分权限需要运行时申请。虽然穿山甲SDK可能会自己处理,但确保你的游戏有基本的存储权限(如果SDK需要)。
- TextureView兼容性:在初始化配置中尝试设置
.UseTextureView(true)。某些设备或Unity版本下,SurfaceView可能有问题。 - 真机系统WebView版本过低:穿山甲部分广告素材依赖系统WebView。提示用户更新系统WebView或Chrome浏览器。
- Manifest中Activity配置错误:检查所有穿山甲相关的
问题4:iOS构建成功,但初始化失败,错误码未知。
- 排查:
- 网络权限:iOS需要在
Info.plist中添加NSAppTransportSecurity设置允许任意加载,或正确配置白名单。确保已添加。 - SKAdNetwork:iOS 14+要求配置
SKAdNetwork以支持归因。穿山甲SDK的SKAdNetworkID列表需要添加到你的Xcode工程的Info.plist中。这个列表在穿山甲iOS SDK的下载包或文档里可以找到。 - IDFA:如果使用了IDFA(广告标识符),需要在Xcode工程中勾选
Advertiser ID权限,并在App Store Connect的隐私问卷中如实声明。
- 网络权限:iOS需要在
问题5:激励视频播放完成后,没有收到OnVideoComplete或OnRewardVerify回调。
- 排查:
- 测试广告问题:确认是否在使用测试广告位ID和测试模式。部分测试广告的行为可能与真实广告有细微差别,但核心回调应该一致。
- 回调绑定时机:确保
SetRewardInteractionListener是在LoadAd之前调用的。如果在加载后才设置监听器,可能会错过一些回调。 - 设备时间不准:极少数情况下,设备时间与网络时间不同步,可能影响服务器验证回调。确保设备时间自动同步。
接入广告SDK是一个细致活,考验的是开发者的耐心和对细节的把控。最有效的调试方式永远是查看日志。把穿山甲SDK的调试日志打开,结合Unity的Debug.Log和Android的logcat,大部分问题的根源都能被定位。记住,在测试阶段大胆地用测试模式,把所有流程跑通;在上线前,严格地对照检查清单,切换回正式环境。这样一套组合拳下来,你的Unity游戏广告变现之路,就能走得又稳又远了。