
1. 为什么要在项目里接入广告系统做独立游戏或者小团队项目绕不开的一个现实问题就是变现。我见过不少开发者游戏做完了玩法也打磨得不错但就是不知道怎么把流量变成收入最后项目不了了之。广告变现是移动端游戏最主流的变现方式之一尤其是对于体量不大、付费意愿不强的休闲游戏来说激励视频和插屏广告几乎是标配。UnityAds 是 Unity 官方推出的一套广告聚合与投放方案它最大的好处是和 Unity 引擎的集成度非常高不需要额外引入一堆第三方 SDK也不用为了适配不同广告平台写一堆胶水代码。你只需要在项目里接入一次就能通过后台配置来管理多个广告来源这对独立开发者来说省了太多事。这篇文章主要面向已经有一定 Unity 开发基础、准备给项目加入广告变现能力的开发者。我会从整体设计思路讲起把接入流程、关键参数、常见坑点都拆开说清楚。即使你之前完全没接触过广告 SDK跟着走一遍也能跑通。文章里涉及的具体数值和配置都是基于常见实践给出的参考实际项目里你需要根据自己的情况调整。先说一个核心认知广告接入不是“加个 SDK 就完事”的事情。它涉及到初始化时机、加载策略、展示频率控制、用户留存和收益之间的平衡。很多新手一上来就把广告塞得到处都是结果用户第二天就流失了。所以我会把“为什么这么设计”讲透而不是只给你一堆代码。2. 接入前的整体设计与思路拆解2.1 广告类型的选择逻辑UnityAds 支持几种主流的广告形式每种形式的适用场景和收益模型都不一样。选错了类型不仅收益上不去还会严重影响用户体验。激励视频是收益最高、对用户体验影响最小的一种。用户主动选择观看看完之后获得游戏内奖励比如复活一次、双倍金币、解锁皮肤等。这种广告的完成率通常很高因为用户有明确的动机。我一般建议把激励视频作为核心变现手段尤其是在关卡失败、资源不足这些节点上设置入口。插屏广告是在游戏自然停顿点弹出的全屏广告比如关卡结束、返回主菜单时。它的收益比激励视频低一些但展示频次可以更高。关键是要控制好弹出时机绝对不能在用户操作过程中突然弹出来那会让人非常烦躁。横幅广告是常驻在屏幕顶部或底部的条幅广告。它的单次收益很低但胜在持续曝光。适合放在菜单界面或者非核心玩法区域。我个人的经验是横幅广告对留存的影响比较微妙如果放的位置太显眼用户会觉得屏幕被占用体验下降。还有一种可试玩广告用户可以先试玩一小段再决定是否下载这种广告的转化率通常不错但对接入方的要求也更高一些。选择逻辑可以总结成一句话激励视频做深度插屏广告做频次横幅广告做补充。三者搭配使用收益和体验才能平衡。2.2 初始化时机的考量UnityAds 的初始化时机是一个非常关键的决策点。初始化太早会拖慢游戏启动速度初始化太晚又会导致广告来不及加载用户点击时无广告可看。我试过几种方案最后比较稳妥的做法是在游戏启动后的第一个加载界面或者主菜单出现时进行初始化。这个时候用户已经在等待了初始化占用的那点时间不会造成额外的感知延迟。同时初始化完成后立刻开始预加载第一条广告这样等用户第一次触发广告入口时大概率已经有缓存好的广告可以展示。具体来说可以在第一个场景的Start方法里调用初始化但不要阻塞主线程。UnityAds 的初始化是异步的你可以注册回调来监听初始化结果。如果初始化失败要有重试机制比如间隔几秒后重试一次最多重试三次。注意不要在Awake里做初始化因为这个时候其他系统可能还没准备好容易出问题。也不要在游戏进行到一半时才初始化那样首次广告的等待时间会很长。2.3 广告加载策略的设计广告加载策略直接决定了广告的填充率和展示成功率。核心原则是提前加载按需展示及时补充。提前加载的意思是不要等用户点击了广告按钮才去请求广告那时候已经来不及了。正确的做法是在初始化完成后就开始加载并且在每次广告关闭后立刻加载下一条。这样形成一个循环保证随时有广告可用。按需展示是指根据游戏内的具体场景来决定展示哪种广告。比如用户在关卡失败时点击“看广告复活”这时候展示激励视频用户在通关后返回菜单这时候可以展示插屏广告。不同场景对应不同的广告位后台可以分别配置。及时补充是指如果某次加载失败了要有重试机制。网络波动、广告库存不足都可能导致加载失败不能一次失败就放弃。我一般会设置一个重试队列失败后延迟几秒再试最多重试几次。2.4 收益与体验的平衡点这是最考验开发者经验的地方。广告太多用户流失广告太少收益不够。怎么找平衡点我的经验是激励视频的入口可以多设但插屏广告的频率一定要克制。激励视频是用户主动选择的多给几个入口反而能提高用户参与度。比如除了复活还可以设置“看广告获得双倍奖励”“看广告刷新每日任务”等。插屏广告则要严格控制我一般设置为每两到三关展示一次而且要在关卡结算界面之后而不是在关卡进行中。另外新用户的前几关最好不要展示插屏广告让他们先体验游戏的核心乐趣。等用户玩了几关、产生了一定粘性之后再逐步增加广告展示。这个策略对留存率的保护效果非常明显。3. 核心细节解析与实操要点3.1 后台配置的关键参数在接入代码之前需要先在 UnityAds 的后台完成一些配置。这些配置决定了广告的行为和收益。首先是项目创建。登录后台后创建一个新项目选择对应的平台iOS 或 Android。创建完成后会得到一个 Game ID这个 ID 是后续代码里要用的关键标识。然后是广告位配置。每个广告位对应一个 Placement ID。你可以为激励视频、插屏广告、横幅广告分别创建不同的广告位。广告位的名称要清晰比如“rewarded_video_level_fail”“interstitial_level_complete”这样后期看数据的时候一目了然。还有一个重要的参数是广告位类型。UnityAds 后台支持配置广告位的类型比如激励视频、插屏、横幅等。类型选错了代码里调用就会出问题。我见过有人把激励视频的广告位配成了插屏类型结果用户看完广告拿不到奖励排查了半天才发现是后台配置的问题。提示建议在后台为每个广告位设置一个“测试模式”开关开发阶段打开测试模式可以避免产生无效展示也方便调试。3.2 代码接入的核心步骤代码接入其实不复杂但有几个关键点容易出错。下面我按顺序拆解。第一步是引入命名空间。在脚本顶部加上using UnityEngine.Advertisements;这样才能调用 UnityAds 的 API。第二步是初始化。调用Advertisement.Initialize(gameId, testMode)其中gameId是后台创建项目时得到的 IDtestMode是一个布尔值开发阶段设为true上线前改成false。using UnityEngine; using UnityEngine.Advertisements; public class AdManager : MonoBehaviour { private string gameId 你的GameID; private bool testMode true; void Start() { Advertisement.Initialize(gameId, testMode); } }第三步是加载广告。对于激励视频需要调用Advertisement.Load(placementId)。加载完成后会触发OnUnityAdsAdLoaded回调。你可以在回调里更新 UI比如把广告按钮从灰色变成可点击状态。第四步是展示广告。调用Advertisement.Show(placementId)同时传入一个ShowOptions对象来处理回调。激励视频需要实现IUnityAdsShowListener接口在OnUnityAdsShowComplete回调里判断用户是否完整观看了广告如果完整观看则发放奖励。public class RewardedAd : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener { private string placementId rewarded_video; public void LoadAd() { Advertisement.Load(placementId, this); } public void ShowAd() { Advertisement.Show(placementId, this); } public void OnUnityAdsAdLoaded(string placementId) { Debug.Log(广告加载完成: placementId); } public void OnUnityAdsFailedToLoad(string placementId, UnityAdsLoadError error, string message) { Debug.LogError(广告加载失败: message); } public void OnUnityAdsShowComplete(string placementId, UnityAdsShowCompletionState state) { if (state UnityAdsShowCompletionState.COMPLETED) { // 发放奖励 } } public void OnUnityAdsShowFailure(string placementId, UnityAdsShowError error, string message) { Debug.LogError(广告展示失败: message); } public void OnUnityAdsShowStart(string placementId) { } public void OnUnityAdsShowClick(string placementId) { } }这段代码是激励视频的完整流程。注意OnUnityAdsShowComplete里的state参数只有COMPLETED才代表用户完整观看了广告SKIPPED表示用户中途跳过这种情况下不应该发放奖励。3.3 广告位 ID 的管理方式随着项目变大广告位会越来越多如果直接在代码里硬编码字符串后期维护会很痛苦。我建议用一个静态类或者 ScriptableObject 来集中管理所有广告位 ID。public static class AdPlacements { public const string RewardedVideoLevelFail rewarded_video_level_fail; public const string RewardedVideoDoubleReward rewarded_video_double_reward; public const string InterstitialLevelComplete interstitial_level_complete; public const string BannerMainMenu banner_main_menu; }这样做的好处是如果后台改了广告位 ID只需要改一个地方不用全局搜索替换。而且代码可读性也更好看到AdPlacements.RewardedVideoLevelFail就知道是哪个位置的广告。3.4 测试模式与正式模式的切换开发阶段一定要打开测试模式否则你的测试展示会被计入真实数据可能影响账号的广告投放效果。测试模式下UnityAds 会返回测试广告不会产生真实收益但流程和正式广告完全一样。上线前记得把testMode改成false。我踩过一次坑上线后忘了改结果跑了半天发现没有收益排查了半天才想起来是测试模式没关。后来我养成了一个习惯在打包脚本里加一个检查如果testMode还是true就弹一个警告。注意测试模式下广告的填充率和展示行为可能和正式模式有差异。上线前最好用正式模式在真机上跑一遍完整流程确认没有问题。4. 实操过程与核心环节实现4.1 从零搭建一个广告管理模块前面讲了零散的接入点现在把它们串起来做一个完整的广告管理模块。这个模块要负责初始化、加载、展示、重试、频率控制等所有事情。先定义模块的整体结构。我一般会分成三个部分AdInitializer负责初始化AdLoader负责加载和缓存AdController负责展示和频率控制。这样职责清晰后期扩展也方便。AdInitializer的核心逻辑是在游戏启动时调用初始化监听初始化结果成功后通知AdLoader开始加载广告。如果初始化失败启动重试计时器。public class AdInitializer : MonoBehaviour { private string gameId 你的GameID; private bool testMode false; private int retryCount 0; private const int MaxRetry 3; void Start() { Initialize(); } private void Initialize() { Advertisement.Initialize(gameId, testMode, new AdInitializationListener(this)); } public void OnInitializeSuccess() { Debug.Log(广告初始化成功); AdLoader.Instance.PreloadAll(); } public void OnInitializeFailed(string message) { if (retryCount MaxRetry) { retryCount; Invoke(nameof(Initialize), 5f); } } }AdLoader负责维护每个广告位的加载状态。它内部用一个字典记录每个广告位是否已经加载完成并提供Load和IsReady方法。public class AdLoader : MonoBehaviour { public static AdLoader Instance; private Dictionarystring, bool adReadyState new Dictionarystring, bool(); void Awake() { Instance this; } public void PreloadAll() { Load(AdPlacements.RewardedVideoLevelFail); Load(AdPlacements.InterstitialLevelComplete); } public void Load(string placementId) { adReadyState[placementId] false; Advertisement.Load(placementId, new AdLoadListener(this, placementId)); } public bool IsReady(string placementId) { return adReadyState.ContainsKey(placementId) adReadyState[placementId]; } public void OnAdLoaded(string placementId) { adReadyState[placementId] true; } public void OnAdLoadFailed(string placementId) { adReadyState[placementId] false; Invoke(nameof(RetryLoad), 3f); } private void RetryLoad() { // 重试逻辑 } }AdController负责展示广告并控制展示频率。它内部记录上次展示插屏广告的时间如果距离上次展示不足设定的间隔就跳过这次展示。public class AdController : MonoBehaviour { private float lastInterstitialTime 0f; private const float InterstitialCooldown 120f; public void ShowRewardedVideo(string placementId, System.Action onComplete) { if (!AdLoader.Instance.IsReady(placementId)) { onComplete?.Invoke(); return; } Advertisement.Show(placementId, new RewardedShowListener(onComplete)); } public void ShowInterstitial(string placementId) { if (Time.time - lastInterstitialTime InterstitialCooldown) { return; } if (!AdLoader.Instance.IsReady(placementId)) { return; } lastInterstitialTime Time.time; Advertisement.Show(placementId); } }这个模块的代码量不大但把核心逻辑都覆盖了。实际项目里你可以根据需求继续扩展比如加入更复杂的频率控制策略、A/B 测试等。4.2 激励视频的完整调用流程激励视频的调用流程比插屏广告复杂一些因为涉及到奖励发放。我把它拆成几个步骤。第一步在游戏里设置广告入口。比如关卡失败界面上放一个“看广告复活”按钮。按钮的初始状态应该是不可点击的等广告加载完成后再启用。第二步用户点击按钮后先检查广告是否已经加载好。如果没加载好可以显示一个提示“广告加载中请稍候”同时触发一次加载请求。第三步广告加载好后调用Show方法展示。展示过程中游戏应该暂停避免用户在观看广告时还能操作游戏。第四步广告关闭后根据OnUnityAdsShowComplete回调里的state判断是否发放奖励。如果是COMPLETED发放奖励并继续游戏如果是SKIPPED不发放奖励但也要继续游戏。第五步无论展示成功还是失败都要立刻触发下一次加载保证下一条广告能及时准备好。提示奖励发放一定要做幂等处理。我遇到过一种情况用户看完广告后网络波动回调触发了两次结果奖励发了两次。后来我在发放奖励前加了一个标记位确保同一次展示只发一次奖励。4.3 插屏广告的时机控制插屏广告的展示时机比激励视频更讲究。展示得太频繁用户会烦展示得太少收益又上不去。我的做法是设置一个冷却时间比如 120 秒。在冷却时间内即使触发了展示条件也不展示。同时在关卡结算界面之后展示插屏广告而不是在关卡进行中。具体实现上可以在关卡完成的回调里调用ShowInterstitial。这个方法内部会检查冷却时间如果还在冷却期内就直接返回不展示广告。另外新用户的前三关不展示插屏广告。这个可以通过记录用户已完成的关卡数来实现。如果关卡数小于 3直接跳过插屏展示。还有一个细节是插屏广告展示时游戏应该暂停。但插屏广告关闭后游戏不应该自动恢复而是等用户主动操作后再恢复。这样可以避免用户还没反应过来游戏就继续了。4.4 横幅广告的布局与刷新横幅广告的接入相对简单但布局上有一些讲究。UnityAds 的横幅广告支持几种位置顶部、底部、左上、右上等。我一般选择底部因为底部对游戏视野的影响最小。横幅广告的刷新是自动的不需要手动调用。但你可以设置刷新间隔不过这个参数在后台配置代码里改不了。横幅广告的展示和隐藏可以通过Show和Hide方法控制。比如在游戏主菜单显示横幅进入关卡后隐藏。这样可以避免横幅遮挡游戏内容。注意横幅广告在部分设备上可能会遮挡底部 UI尤其是那些有虚拟导航栏的设备。建议在布局时给底部留出足够的空间或者根据设备的安全区域动态调整。5. 常见问题与排查技巧实录5.1 广告加载失败的原因与排查广告加载失败是最常见的问题原因可能有很多。我整理了一个排查表按优先级从高到低排列。问题现象可能原因排查方法一直加载失败Game ID 配置错误检查后台的 Game ID 和代码里的是否一致一直加载失败广告位 ID 配置错误检查 Placement ID 是否和后台一致偶尔加载失败网络波动检查设备网络增加重试机制特定地区加载失败广告库存不足这是正常现象增加重试和备用广告位测试模式正常正式模式失败正式模式配置问题检查后台项目是否已上线广告位是否已激活初始化失败SDK 版本过旧更新 UnityAds SDK 到最新版本排查的时候先看日志。UnityAds 的日志会输出具体的错误码和错误信息根据错误码去查文档基本能定位到问题。我遇到过一次比较隐蔽的问题广告在编辑器里能加载但在真机上不行。后来发现是打包时漏掉了网络权限。Android 需要在AndroidManifest.xml里声明INTERNET权限iOS 需要确保NSAppTransportSecurity配置正确。这种问题日志里不一定有明确提示需要自己检查打包配置。5.2 广告展示后没有回调有时候广告展示了但回调没有触发。这种情况通常有几个原因。一是广告位类型不匹配。比如你把激励视频的广告位配成了插屏类型展示时可能不会触发OnUnityAdsShowComplete回调。检查后台的广告位类型设置。二是回调对象被销毁了。如果你在展示广告后切换了场景原来的回调对象可能已经被销毁回调自然就收不到了。解决办法是把广告管理模块做成DontDestroyOnLoad或者用静态回调。三是 SDK 版本问题。某些旧版本的 UnityAds SDK 在特定平台上存在回调丢失的 bug。更新到最新版本通常能解决。5.3 收益数据异常的处理收益数据异常通常表现为展示次数正常但收益为零或者收益远低于预期。如果展示次数正常但收益为零先检查是不是还在测试模式。测试模式下展示的广告不产生收益。如果确认不是测试模式检查后台的广告位是否已经激活有些广告位需要审核通过后才能产生收益。如果收益远低于预期可能是广告类型选择的问题。激励视频的收益通常高于插屏和横幅如果激励视频的展示占比很低整体收益就会偏低。可以调整广告入口的布局把激励视频的入口做得更显眼一些。还有一个容易被忽略的点是广告的填充率。填充率是指请求广告后成功返回广告的比例。填充率低展示次数就少收益自然上不去。填充率受地区、时间段、广告位类型等多种因素影响。如果某个地区的填充率特别低可以考虑增加备用广告位或者调整广告展示策略。5.4 用户投诉广告太多的应对用户投诉广告太多说明广告频率控制出了问题。这时候需要从几个方面排查。先看插屏广告的展示频率。如果冷却时间设置得太短比如 30 秒用户会感觉广告无处不在。我建议至少 120 秒最好 180 秒以上。再看广告入口的数量。激励视频的入口可以多但插屏广告的触发点要少。检查一下是不是在太多地方触发了插屏展示。还有一个细节是广告的关闭按钮。有些广告的关闭按钮很小用户找不到就会觉得被强制观看。这种情况可以通过后台配置来调整但更根本的是控制广告展示频率减少用户的抵触情绪。如果投诉已经比较多可以临时降低广告频率观察留存率的变化。等留存率回升后再逐步恢复。这个过程需要持续监控数据不能拍脑袋决定。5.5 不同平台的适配差异UnityAds 在 iOS 和 Android 上的行为有一些差异需要分别处理。iOS 上广告的加载速度通常比 Android 慢一些因为 iOS 对网络请求的限制更严格。所以 iOS 上的预加载要更早开始重试间隔也要适当延长。Android 上设备碎片化严重不同厂商的设备表现差异很大。有些低端设备上广告展示可能会卡顿甚至崩溃。建议在低端设备上降低广告展示频率或者只展示激励视频不展示插屏和横幅。另外iOS 的隐私政策要求更严格需要在Info.plist里配置相关的隐私描述。如果配置不正确广告可能无法加载。这个在打包前一定要检查。6. 广告策略的持续优化思路6.1 数据监控的关键指标广告接入完成后需要持续监控几个关键指标才能不断优化策略。填充率是最基础的指标反映了广告请求的成功率。填充率低说明广告库存不足或者配置有问题。展示率是展示次数除以请求次数反映了广告的实际展示比例。展示率低可能是加载失败或者用户没有触发展示。完成率主要针对激励视频反映了用户完整观看广告的比例。完成率高说明奖励设置合理用户愿意看完。eCPM是每千次展示的收益是衡量广告价值的核心指标。eCPM 高说明广告位的价值高可以适当增加展示频率。留存率是最终要的指标。广告策略的调整不能只看收益还要看对留存的影响。如果收益涨了但留存掉了长期来看是亏的。6.2 A/B 测试的实操方法A/B 测试是优化广告策略的有效手段。具体做法是把用户随机分成两组一组用策略 A一组用策略 B对比两组的收益和留存数据。比如你想测试插屏广告的冷却时间可以设置 A 组 120 秒B 组 180 秒。跑一周后对比两组的 eCPM 和次日留存。如果 B 组的 eCPM 略低但留存明显更高那 B 组策略可能更优。A/B 测试的关键是只改变一个变量。如果同时改多个参数就无法判断是哪个参数起了作用。另外测试周期要足够长至少一周避免偶然波动影响结论。6.3 季节性调整与活动配合广告收益有明显的季节性波动。比如节假日期间广告主的预算增加eCPM 通常会上升。这时候可以适当增加广告展示频率抓住收益高峰。反过来在广告淡季eCPM 下降可以降低展示频率保护用户体验。等旺季再恢复。另外游戏内做活动的时候也可以配合调整广告策略。比如活动期间把激励视频的奖励翻倍鼓励用户多看广告。活动结束后再恢复原样。6.4 长期运营的注意事项广告变现是一个长期的事情不能只看短期收益。我个人的经验是留存率比短期收益更重要。一个留存率高的游戏即使广告频率低长期收益也会超过那些广告满天飞但用户流失快的游戏。所以每次调整广告策略后都要观察留存率的变化。如果留存率下降超过 5%就要考虑回滚策略。还有一点是不要忽视用户反馈。应用商店的评论、社区里的讨论都是了解用户对广告态度的重要渠道。如果很多用户提到广告问题就要认真对待及时调整。最后广告 SDK 的版本要定期更新。新版本通常会修复一些 bug优化加载速度甚至提升收益。但更新前要在测试环境充分验证避免引入新的问题。我个人在实际操作中的体会是广告接入这件事技术实现只是第一步真正的功夫在策略调整和数据分析上。同样的 SDK同样的广告位不同的展示策略收益可能差好几倍。所以不要接完就放着不管要持续观察、持续优化。踩过几次坑之后你会慢慢找到适合自己项目的节奏。