1. 项目概述:当Unity遇见微信小游戏,如何让激励广告成为你的“印钞机”?
如果你正在用团结引擎(Unity)开发微信小游戏,并且已经走到了“如何变现”这一步,那么“激励广告”绝对是你绕不开的核心课题。这不仅仅是简单地在游戏里插个广告位那么简单,它关乎着用户体验、留存率,以及最直接的——你的收入。我见过太多开发者,游戏做得不错,但一接入广告,要么是弹出时机不对导致玩家流失,要么是收益远低于预期,最后项目不了了之。今天,我就结合自己多次从零到一接入微信小游戏激励广告的实战经验,把整个流程掰开揉碎了讲清楚,从设计思路、技术集成到调优避坑,让你不仅能接上,更能接得好,真正把广告变成可持续的收入来源。
简单来说,这个“团结引擎 + 微信小游戏广告集成 - 激励广告”项目,就是要在你的Unity游戏(通过团结引擎或Unity WebGL Build发布到微信小游戏平台)中,无缝地嵌入微信官方提供的激励式视频广告组件。玩家观看一段15-30秒的视频广告后,可以获得游戏内的虚拟奖励(如复活机会、额外金币、道具等)。这个过程,需要你打通Unity侧的逻辑调用与微信小游戏平台侧的广告API,并处理好加载、展示、回调、错误处理等一系列细节。它适合所有希望通过广告变现的微信小游戏开发者,无论你是独立开发者还是小团队,这套流程都是必经之路。接下来,我们就从最核心的设计思路开始拆解。
2. 整体设计与核心思路拆解:为什么是激励广告?又该如何设计?
2.1 激励广告的核心价值与设计哲学
在动手写代码之前,我们必须先想清楚:为什么在众多广告形式中(Banner、插屏、原生),激励视频成为了小游戏的主流选择?答案在于其“价值交换”的本质。玩家不是被动接受广告打扰,而是主动选择用观看时间来换取明确的游戏内利益。这是一种双赢的设计:玩家获得了他们想要的进度或资源,开发者获得了收入。因此,我们的集成思路必须围绕“提升玩家主动观看意愿”来展开,而不是粗暴地强塞广告。
首先,奖励必须明确且诱人。不要给一些无关痛痒的奖励。比如,在玩家角色死亡时,提供“观看广告立即复活并保留本轮所有金币”的选项,就比“观看广告获得100金币”要有效得多。奖励的价值感要直观,让玩家觉得“这30秒花得值”。
其次,触发时机必须自然且非强迫。激励广告的触发点应该设计在玩家的“需求时刻”或“自然断点”。常见的黄金点位包括:游戏失败后的复活环节、宝箱开启前(可选择免费或看广告获得高级奖励)、体力/能量耗尽时补充、每日签到或任务奖励翻倍等。绝对要避免在游戏核心操作流程中突然弹出,那会极大破坏体验。
最后,频率需要精细控制。虽然激励广告是自愿的,但过于频繁的提示也会引起反感。我们需要在后台设计一个冷却机制或每日次数限制,并可以通过游戏内经济系统进行调节。例如,前3次复活看广告是免费的,后续可能需要消耗少量金币或增加冷却时间。这背后的逻辑是平衡短期收益与长期留存。
2.2 技术架构与选型考量
从技术实现层面看,Unity微信小游戏的广告集成是一个典型的“桥接”架构。你的Unity C#代码运行在微信小游戏的JavaScript环境中(通过WebGL),而微信的广告API是纯JS的。因此,核心思路就是建立一座从C#到JavaScript的通信桥梁。
为什么选择微信官方广告插件?市面上或许有一些第三方聚合SDK,但对于微信小游戏这个特定平台,我强烈建议直接使用微信官方提供的插件。原因有三:第一,稳定性与兼容性最佳。官方插件由平台方直接维护,能确保在微信环境下稳定运行,避免因环境更新导致的莫名bug。第二,收益透明直接。广告收入直接进入你的微信小游戏后台,结算清晰,没有中间商抽成(第三方聚合SDK通常会收取一定服务费)。第三,集成相对标准化。虽然仍有坑,但官方文档和社区案例是最丰富的,遇到问题更容易找到解决方案。
整个数据流可以这样理解:Unity游戏逻辑(C#)在需要广告时,通过一个封装好的接口,调用一个全局的JavaScript函数。这个JS函数再去调用微信的wx.createRewardedVideoAd()API创建广告实例,并监听其加载、播放、关闭、错误等事件。这些事件最终又需要通过某种方式回调给Unity,以触发游戏内的发奖逻辑。我们后续的代码实现,就是把这个双向通信的管道搭建稳固。
3. 核心细节解析与实操要点:从零搭建通信桥梁
3.1 Unity与JavaScript互通的三种方式
要让C#调用JS,我们主要有三种方式,每种都有其适用场景和注意事项。
方式一:Application.ExternalCall (WebGL传统方式)这是Unity WebGL导出时较老的一种方式。你可以在C#中写Application.ExternalCall(“函数名”, 参数)。但在微信小游戏环境里,这种方式可能不稳定或不可用,因为微信对全局变量的封装和WebGL播放器的具体实现可能导致函数找不到。除非有明确证据表明项目可用,否则不建议作为主要方案。
方式二:JSLib插件文件(推荐)这是Unity官方支持的、更可靠的通信机制。你需要在Assets目录下创建一个后缀为.jslib的文本文件。在这个文件里,用特定的语法声明你的JavaScript函数,这些函数会自动合并到最终的构建中。然后,在C#中通过[DllImport(“__Internal”)]来调用它们。这种方式的好处是依赖关系清晰,打包时自动处理,兼容性好。
方式三:通过全局变量/事件监听(灵活补充)我们可以在页面加载的JavaScript中,将一个回调函数挂载到window对象上,比如window.UnityToWeChat = function(){}。同时,也可以在JS里直接调用Unity实例上的方法,前提是你在C#中通过[DllImport(“__Internal”)]暴露了一个函数给JS。这种方式常作为JSLib的补充,用于处理一些动态的回调。
实操心得:对于微信小游戏广告集成,我推荐以方式二(JSLib)为主,方式三为辅的混合策略。用JSLib来封装创建广告、展示广告等主动调用操作,因为其调用更直接稳定。用全局事件回调(方式三)来处理广告的播放完成、关闭等异步事件,因为事件监听和回调在JS侧处理起来更自然,再通过调用Unity暴露的方法来通知C#侧。
3.2 广告实例的生命周期管理
一个常见的误区是每次需要广告时都创建新的实例。这会导致内存浪费,并可能触发平台的频率限制。正确的做法是单例管理。
我们需要在游戏初始化时(比如一个全局的GameManager的Awake或Start方法里),就通过JS桥接创建好激励视频广告的实例,并缓存其引用。之后整个游戏生命周期内,都复用这个实例。你需要管理好它的状态:
- 加载(Load):创建实例后或每次播放后,主动调用
.load()预加载下一段广告物料。确保广告就绪,减少玩家点击后等待的时间。 - 展示(Show):当玩家触发观看条件时,调用
.show()。如果此时广告未加载完成,需要有一个友好的提示(如“广告加载中,请稍候”),并自动尝试加载。 - 关闭与回调(Close & Callback):监听广告的关闭事件(
onClose)。这是最关键的一步!必须在回调中根据res.isEnded字段判断玩家是否完整观看了广告。只有isEnded为true时才能发放奖励!如果用户提前关闭了广告,则不能给奖。这是平台规则,也是公平性的体现。 - 错误处理(Error):务必监听
onError事件。网络异常、广告拉取失败、系统错误等都会触发。你需要有降级方案,比如记录日志、提示用户“广告暂时无法播放,请检查网络”,并可能提供一种替代的获取奖励途径(如消耗少量游戏货币),避免玩家流程卡死。
3.3 关键参数与配置解析
在调用wx.createRewardedVideoAd()时,有几个参数至关重要:
adUnitId: 广告位ID。这是你在微信小游戏管理后台创建广告位后获取的唯一标识。测试阶段和上线阶段要使用不同的ID。测试ID通常以“test”开头,仅用于开发调试,没有实际收益。正式上线前务必替换为正式的广告位ID。multiton: 是否支持创建多例。通常设为false,因为我们采用单例管理。adTheme/adStyle: 广告的主题与样式(如深色/浅色)。可以根据你的游戏UI主题进行设置,让广告与游戏界面更融合,提升观感。
此外,在微信小游戏后台,你需要仔细配置广告位的类型(激励视频)、名称,并设置好广告投放的频次控制和用户分层策略。这些后台设置会直接影响广告的填充率(是否有广告可播)和eCPM(千次展示收益),需要结合游戏数据和平台建议不断优化。
4. 实操过程与核心环节实现:一步步写出健壮的代码
4.1 第一步:创建JSLib桥接文件
在你的Unity项目Assets文件夹下(或任意子目录,但建议放在Plugins下便于管理),新建一个文本文件,重命名为WeChatAdBridge.jslib。注意后缀名必须是.jslib。
打开该文件,写入以下内容。这段代码声明了我们将要从C#调用的JavaScript函数。
mergeInto(LibraryManager.library, { // 创建激励视频广告实例 WeChat_CreateRewardedVideoAd: function (adUnitIdPtr) { var adUnitId = Pointer_stringify(adUnitIdPtr); // 将广告实例存储在全局变量中,方便其他函数调用 if (!window._rewardedVideoAd) { window._rewardedVideoAd = wx.createRewardedVideoAd({ adUnitId: adUnitId, multiton: false }); // 监听广告加载成功事件(可选,用于调试或状态更新) window._rewardedVideoAd.onLoad(function () { console.log('激励视频广告加载成功'); // 这里可以通知Unity更新UI状态,比如将“广告未就绪”按钮变为可点击 if (window.unityInstance && window.unityInstance.SendMessage) { window.unityInstance.SendMessage('WeChatAdManager', 'OnAdLoaded', ''); } }); // 监听广告错误事件 window._rewardedVideoAd.onError(function (err) { console.error('激励视频广告错误:', err); // 将错误信息传递给Unity处理 var errMsg = JSON.stringify(err); if (window.unityInstance && window.unityInstance.SendMessage) { window.unityInstance.SendMessage('WeChatAdManager', 'OnAdError', errMsg); } }); // 监听广告关闭事件 - 核心回调! window._rewardedVideoAd.onClose(function (res) { console.log('广告关闭,是否播放完成:', res.isEnded); // 将关闭结果传递给Unity,isEnded是布尔值,需要转换为字符串或数字传递 var result = res.isEnded ? '1' : '0'; if (window.unityInstance && window.unityInstance.SendMessage) { window.unityInstance.SendMessage('WeChatAdManager', 'OnAdClose', result); } }); } return 0; }, // 展示激励视频广告 WeChat_ShowRewardedVideoAd: function () { if (window._rewardedVideoAd) { window._rewardedVideoAd.show().catch(function (err) { // show()调用失败,可能是广告未加载好,尝试重新加载并提示用户 console.warn('广告展示失败,尝试加载:', err); window._rewardedVideoAd.load() .then(() => window._rewardedVideoAd.show()) .catch(err => { console.error('重新加载并展示也失败:', err); // 通知Unity展示失败 if (window.unityInstance && window.unityInstance.SendMessage) { window.unityInstance.SendMessage('WeChatAdManager', 'OnAdError', 'SHOW_FAILED'); } }); }); } else { console.error('广告实例未创建,请先调用CreateRewardedVideoAd'); } return 0; }, // 预加载广告 WeChat_PreloadRewardedVideoAd: function () { if (window._rewardedVideoAd) { window._rewardedVideoAd.load().catch(function (err) { console.error('广告预加载失败:', err); }); } return 0; } });这段代码做了几件关键事:1. 将微信广告API的创建和事件监听封装起来。2. 通过window.unityInstance.SendMessage这个通用方法,将广告状态(加载成功、错误、关闭)回调给Unity中名为WeChatAdManager的GameObject上的特定方法。这是Unity WebGL与页面JavaScript通信的标准方式之一。
4.2 第二步:编写C#广告管理器
在Unity中创建一个C#脚本,命名为WeChatAdManager.cs。这个脚本将作为Unity侧控制广告的核心单例。
using UnityEngine; using System.Runtime.InteropServices; using System; // 用于Action回调 public class WeChatAdManager : MonoBehaviour { // 单例实例 public static WeChatAdManager Instance { get; private set; } // 广告位ID(在Inspector中配置,方便测试和正式环境切换) [SerializeField] private string rewardedVideoAdUnitId = "你的测试或正式广告位ID"; // 定义回调事件,方便UI或其他逻辑模块订阅 public event Action OnAdLoaded; // 广告加载成功 public event Action<string> OnAdError; // 广告错误,参数为错误信息 public event Action<bool> OnAdClose; // 广告关闭,参数为是否播放完成 // 导入JSLib中声明的函数 [DllImport("__Internal")] private static extern void WeChat_CreateRewardedVideoAd(string adUnitId); [DllImport("__Internal")] private static extern void WeChat_ShowRewardedVideoAd(); [DllImport("__Internal")] private static extern void WeChat_PreloadRewardedVideoAd(); void Awake() { // 简单的单例模式,确保全局只有一个管理器 if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); // 跨场景不销毁 InitializeAd(); } else { Destroy(gameObject); } } void InitializeAd() { // 检查是否在WebGL平台(微信小游戏环境) #if UNITY_WEBGL && !UNITY_EDITOR Debug.Log("初始化微信激励视频广告..."); WeChat_CreateRewardedVideoAd(rewardedVideoAdUnitId); // 创建后立即预加载一次 WeChat_PreloadRewardedVideoAd(); #else Debug.LogWarning("非WebGL环境,广告功能仅在微信小游戏平台有效。"); #endif } // 提供给游戏逻辑调用的公开方法:请求展示广告 public void ShowRewardedVideoAd() { #if UNITY_WEBGL && !UNITY_EDITOR Debug.Log("请求展示激励视频广告"); WeChat_ShowRewardedVideoAd(); #else Debug.Log("模拟:激励视频广告展示(开发环境)"); // 在编辑器下模拟广告关闭回调,方便测试发奖逻辑 // 可以在这里直接触发OnAdClose(true)来测试 OnAdClose?.Invoke(true); #endif } // 提供给游戏逻辑调用的公开方法:手动预加载广告 public void PreloadAd() { #if UNITY_WEBGL && !UNITY_EDITOR WeChat_PreloadRewardedVideoAd(); #endif } // ========== 以下方法由JSLib中的JavaScript代码调用 ========== // 方法名必须与SendMessage中指定的完全一致 // 广告加载成功回调 public void OnAdLoaded(string msg) { Debug.Log("Unity收到:广告加载成功"); OnAdLoaded?.Invoke(); // 可以在这里更新UI,比如将“看广告”按钮置为可点击状态 } // 广告错误回调 public void OnAdError(string errMsg) { Debug.LogError($"Unity收到:广告错误 - {errMsg}"); OnAdError?.Invoke(errMsg); // 这里可以根据不同的错误码给玩家不同的提示 // 例如:如果是网络错误,提示“网络异常,请重试” // 如果是广告拉取失败,提示“广告暂时 unavailable” } // 广告关闭回调 - 这是最核心的方法! public void OnAdClose(string result) { // result是从JS传过来的字符串,我们约定“1”代表完成,“0”代表未完成 bool isCompleted = (result == "1"); Debug.Log($"Unity收到:广告关闭,播放完成={isCompleted}"); OnAdClose?.Invoke(isCompleted); if (isCompleted) { // !!!在这里发放游戏奖励!!! GrantReward(); // 广告播放完成后,立即预加载下一次广告,保证下次能快速展示 PreloadAd(); } else { // 玩家未看完广告,可以给出提示 Debug.Log("玩家未完整观看广告,不发放奖励。"); // 可以在这里触发一个UI提示:“观看完整广告才能获得奖励哦~” } } // 发放奖励的具体逻辑 private void GrantReward() { Debug.Log("发放激励广告奖励!"); // 这里需要你连接自己游戏的奖励系统 // 例如: // GameDataManager.Instance.AddCoins(100); // PlayerController.Instance.Revive(); // UIManager.Instance.ShowRewardPopup("获得复活机会!"); // 重要:奖励发放最好有日志记录,方便后续核对广告收益与游戏内道具发放是否匹配。 } }将这个脚本挂载到一个空的GameObject上,并将该对象放入你的初始场景(如启动场景或常驻场景)。在Inspector中配置好rewardedVideoAdUnitId。
4.3 第三步:在游戏UI中调用
假设你有一个复活按钮,玩家点击后可以观看广告复活。
// 在复活按钮的点击事件处理函数中 public void OnReviveButtonClicked() { // 先检查广告是否就绪(可以通过监听OnAdLoaded事件来更新按钮状态) // 这里简单起见,直接调用展示 WeChatAdManager.Instance.ShowRewardedVideoAd(); // 同时,监听广告关闭事件以处理复活逻辑 // 注意:要避免重复订阅,通常在Start或OnEnable中订阅一次即可 } // 在某个管理器的Start方法中订阅事件 void Start() { WeChatAdManager.Instance.OnAdClose += HandleAdClose; } void OnDestroy() { // 记得取消订阅,防止内存泄漏 if (WeChatAdManager.Instance != null) { WeChatAdManager.Instance.OnAdClose -= HandleAdClose; } } void HandleAdClose(bool isCompleted) { if (isCompleted) { // 执行复活逻辑 RevivePlayer(); // 更新UI,比如隐藏复活按钮 } else { // 提示玩家未完成观看 ShowToast("请观看完整广告以获得复活机会"); } }4.4 第四步:微信小游戏项目配置
Unity打包出WebGL后,你需要使用微信开发者工具导入并发布。这里有几个关键配置点:
game.json配置:确保在game.json文件中正确配置了广告插件。{ "deviceOrientation": "portrait", "networkTimeout": { "request": 5000, "connectSocket": 5000 }, "plugins": { // 广告插件是微信基础能力,通常无需额外声明插件,但需在后台开通广告位 }, // 确保有必要的权限声明(虽然广告可能不需要特殊权限,但其他功能可能需要) "requiredBackgroundModes": [], "openDataContext": "" }- 开通广告位:登录 微信小游戏管理后台 ,在“变现”->“广告管理”中创建“激励视频广告”位,获取
adUnitId。 - 域名与安全:确保你的游戏资源服务器(CDN)域名已在后台配置好,避免出现资源加载失败的问题。同时,广告API要求小游戏必须在非调试模式下才能正常请求和展示正式广告,在开发者工具的真机调试或预览模式下,只能使用测试
adUnitId。
5. 常见问题与排查技巧实录:填平那些“坑”
即使按照步骤操作,在实际集成过程中你依然会遇到各种问题。下面是我踩过坑后总结的“排坑指南”。
5.1 广告加载失败或无法展示
这是最常见的问题。请按以下顺序排查:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
调用show()无任何反应,或报错“ad unit not found” | 1. 广告位ID错误或未生效。 2. 广告实例创建失败。 3. 平台广告填充率低(新账号或非活跃时段)。 | 1.核对adUnitId:检查C#脚本中配置的ID与微信后台创建的激励视频广告位ID是否完全一致。注意测试ID(以test开头)和正式ID的区别。 |
2.检查JS环境:在微信开发者工具的Console中,输入wx.createRewardedVideoAd看是否存在。如果不存在,检查基础库版本是否过低(需>=2.0.0)。在game.json中可设置"libVersion": "2.16.0"(或更高)。 | ||
3.查看网络请求:在开发者工具的Network面板,过滤https://ad.weixin.qq.com,查看广告请求是否发出,以及返回状态码。403错误通常代表未开通广告权限或ID无效。 | ||
4.使用测试ID:开发阶段务必使用官方提供的测试adUnitId(如“adunit-xxxxxxxxxxxxxxxx”格式的测试号),确保流程跑通。 | ||
广告加载成功但show()后黑屏/闪退 | 1. 广告物料下载失败(网络问题)。 2. 系统资源不足。 3. 个别机型兼容性问题。 | 1.强化错误监听:确保onError事件监听已注册,并详细打印错误信息。常见错误码:1000(系统错误)、1001(网络错误)。 |
2.增加重试机制:在show()失败后的catch中,加入重试逻辑(如代码示例所示),先尝试.load()再.show()。 | ||
| 3.真机测试:在开发者工具上正常,不代表真机正常。一定要用真机扫码预览测试。 |
实操心得:一定要封装一个健壮的
ShowAd()方法。我的做法是,在调用show()前,先检查广告实例是否存在,然后使用.show().catch()链式调用。在catch中,先尝试调用.load()加载广告,加载成功后再尝试.show()。如果连加载都失败,再给用户一个明确的错误提示(如“广告加载失败,请检查网络”)。这个“加载-展示”的容错链能解决90%的偶发性展示问题。
5.2 回调不触发或奖励发放错乱
这个问题更隐蔽,直接关系到玩家体验和你的收入。
问题:广告播放完了,但游戏里没给奖励。排查:
- 检查
OnAdClose回调是否被触发:在微信开发者工具的Console中,查看JSLib里onClose事件的console.log有没有输出。如果没有,说明微信的关闭事件没触发,可能是广告播放流程被异常中断。 - 检查
isEnded判断逻辑:确保你的C#代码正确解析了从JS传来的result字符串。我遇到过因为传递的数据类型不匹配(JS布尔值转C#字符串)导致的判断错误。所以代码中我用了result == "1"这种明确的方式。 - 检查Unity消息发送路径:确认
window.unityInstance.SendMessage调用的对象名和方法名完全匹配。对象名是挂载WeChatAdManager脚本的GameObject的名字,默认是“WeChatAdManager”。方法名是“OnAdClose”,大小写敏感。
问题:玩家没看完广告,却意外获得了奖励。排查:
- 绝对信任
isEnded:发放奖励的唯一依据必须是isEnded === true。不要在onClose之外的其他地方(比如onLoad)发放奖励。 - 防止重复点击:在广告展示期间,禁用触发广告的按钮,防止玩家快速连续点击导致创建多个广告实例或逻辑混乱。
- 网络延迟与状态同步:在弱网环境下,广告关闭事件回调可能有延迟。确保你的游戏在等待回调期间,UI状态是明确的(比如显示“奖励发放中…”),避免玩家误操作。
5.3 性能与体验优化
- 预加载策略:不要在玩家点击按钮时才去加载广告,那样会有明显的等待时间。在游戏启动时、场景加载后或上一次广告播放完毕时,就调用
PreloadAd()预加载下一个广告。让广告“时刻准备着”。 - 广告展示频率与提示:不要在每个死亡点都疯狂弹出广告按钮。设计一个合理的冷却时间(例如,每局游戏最多看3次复活广告)。同时,在广告按钮上可以增加简单的状态提示,比如“广告加载中…”、“可观看”,提升体验。
- 替代方案:对于广告加载失败或用户不想看广告的情况,提供一个替代方案。比如,复活除了看广告,也可以消耗一定量的游戏内货币(金币、钻石)。这给了玩家选择权,也保证了游戏流程不被卡死。
- 数据埋点与监控:在关键节点(广告请求、展示成功、展示失败、播放完成、奖励发放)加入数据统计。这能帮你分析广告填充率、完成率、以及广告收益与游戏内经济系统的平衡关系,为后续调优提供依据。
6. 进阶:广告与游戏经济的平衡艺术
集成成功只是第一步,让广告健康地成为游戏的一部分才是长期运营的关键。这里分享几点更深度的思考:
1. 奖励价值的动态调整奖励不能一成不变。可以通过算法,根据玩家的活跃度、付费能力、当前关卡难度等因素,动态调整广告奖励的数值。例如,对新玩家或卡关玩家,提供更有吸引力的奖励;对高活跃玩家,则逐步降低单次奖励,鼓励他们通过其他方式(如轻度付费)推进游戏。
2. 广告场景的A/B测试不要凭感觉决定广告位。可以对不同的广告触发场景(如复活、宝箱翻倍、获取稀有道具)进行A/B测试,用数据说话,找到那个“玩家接受度高且收益好”的甜蜜点。微信小游戏后台提供的数据分析工具可以帮到你。
3. 与IAP(内购)的协同广告(尤其是激励视频)和内购不是对立关系,而是互补的。广告服务于大众玩家,是低门槛的变现方式;内购服务于核心玩家,提供更深度、更便捷的体验。设计时,可以让部分广告奖励的物品,也能通过内购直接获得,满足不同玩家的需求。
集成微信小游戏激励广告,技术上是一套标准的桥接流程,但真正的功夫在技术之外——在于你对玩家心理的理解、对游戏节奏的把握,以及对商业模式的思考。从创建一个稳定的通信桥梁开始,到设计一个让玩家乐于接受的广告体验,每一步都需要耐心打磨。希望这篇超详细的指南,能帮你避开我当年踩过的坑,顺利跑通从代码到收益的完整闭环。记住,好的广告体验,是让玩家感觉不到“被迫”,而是觉得“划算”。