First Look
让 CloudX 优先填充广告位,然后回退到您现有的聚合设置
First Look 让 CloudX 优先获得广告位填充机会,同时保留您现有的聚合设置作为回退路径。建议先从一个广告位开始,验证加载和展示行为后,再扩展到更多广告位。
以下示例通过 Google Mobile Ads Unity 插件 使用 AdMob 作为回退。相同模式也适用于其他回退聚合平台:先加载 CloudX;只有当 CloudX 未加载成功时才加载回退广告;当两个来源都未准备好时,让游戏流程继续。
这条规则有两种形态,本页各提供一个控制器:
- 横幅广告 — 内嵌广告会持续留在屏幕上,永远不会被消耗,因此控制器需要一个显式的轮次周期,才能让 CloudX 再次获得优先填充机会。MREC 完全沿用此控制器。
- 插屏广告 — 全屏广告在展示后即被消耗,因此两个 SDK 各自的“广告是否就绪”会自动变为 false,下一次
Load()自然又从 CloudX 开始。激励视频广告完全沿用此控制器。
横幅广告
横幅广告并不是把插屏广告换个方法名。全屏广告在展示后即被消耗,因此它的就绪判断会自动变为 false,下一次 Load() 又从 CloudX 开始——下文的插屏广告正是依赖这一点。内嵌广告则永远不会被消耗:CloudX 横幅广告只上报加载和点击,没有展示或关闭回调。若不加处理,第一次填充就会一直占据该广告位,直到场景销毁;也就是说,CloudX 只要有一次未填充,该广告位在整个会话期间都会归回退广告所有。
该控制器用轮次周期解决这个问题。一个轮次就是一次广告机会:先请求 CloudX,只有 CloudX 失败时才请求 AdMob,然后展示胜出方。展示胜出方即结束该轮次并触发 PassSpent;接下来由您启动下一个轮次,而它又从 CloudX 开始。
FirstLookBannerController.cs
决定由哪个 SDK 填充某个轮次。完整的控制器,关键位置均有注释。
FirstLookBannerCycle.cs
决定下一个轮次何时开始。场景侧的那一半约定,是一个 MonoBehaviour。
FirstLookSource.cs
每个事件上报的 CloudX / AdMob 枚举。
三个文件都需要复制。它们合起来就是完整的流程。
它们位于 CloudX Unity 演示应用中——该版本有 CI 覆盖并已在真机上验证,出现问题时修复的也是它。请直接阅读仓库中的文件,而不是本页上的副本:副本会逐渐与之不一致,而过时的那一份一定是副本。
为什么宿主要单独一个文件
控制器是普通类,没有计时能力——没有 Update、没有协程、也没有 Invoke——因此它无法决定下一个轮次何时开始。FirstLookBannerCycle 就是这个计时器,它所遵守的三条规则,正是集成时最容易出错的地方:
- 在
PassSpent后经过一个冷却时间再启动下一个轮次。 30 秒是常见的横幅广告间隔。立即启动会形成请求循环,因为新广告会渲染到可见的广告位中,立刻又结束下一个轮次。 - 横幅广告被隐藏时取消该待执行的轮次。 否则已隐藏的广告位会在整个会话期间持续请求。
- 隐藏期间不要重试失败的加载。 仅取消并不足够:玩家隐藏时若已有加载在进行中,它会在隐藏之后才返回结果。填充成功并无妨——该广告会保留到下一次
Show();失败则不同:它的重试会让请求重新开始,而此时CancelInvoke已无可取消。
直接原样使用该 cycle 组件,这三条便都已满足;冷却、退避和隐藏处理都在该文件中实现,并有注释说明。至于哪个 SDK 赢得某个轮次、以及填充如何保留到广告位再次展示,则属于控制器一侧,在该文件中有注释。
在场景中使用
把 FirstLookBannerCycle 添加到一个 GameObject 上,然后在 Inspector 中:
- 把该组件拖到宿主的
banner字段上; - 填写
cloudXBannerAdUnitId和adMobBannerAdUnitId——这三个都是序列化字段,默认为空,而Begin需要这两个 ID; - 把按钮的
OnClick指向OnBannerButton。
然后在 CloudX 返回结果后——无论初始化成功、失败,还是超过您自己设置的超时——调用一次 BeginFirstLook。其余部分——冷却、退避、隐藏时取消——都在 cycle 组件内部。
不需要同时等待 Google Mobile Ads。初始化期间发起的加载会被它排队,而且回退本身就是惰性的,等待它只会推迟第一个轮次。
using UnityEngine;
public class BannerButton : MonoBehaviour
{
[SerializeField] private FirstLookBannerCycle banner;
[SerializeField] private string cloudXBannerAdUnitId;
[SerializeField] private string adMobBannerAdUnitId;
void Start()
{
banner.AdShown += source => Debug.Log($"First Look banner shown: {source}");
}
/*
* 请在 CloudX 初始化返回结果时调用本方法——无论初始化成功、失败,还是超过
* 您自己设置的超时——并传入 CloudX 是否真的可用。不要在 Start() 中调用:
* Start() 只表示对象已启用,并不代表 SDK 已经返回结果。
*
* Begin 会预加载一个轮次,让玩家第一次请求时就已有广告就绪。
*/
public void BeginFirstLook(bool cloudXAvailable)
{
banner.Begin(cloudXBannerAdUnitId, adMobBannerAdUnitId, cloudXAvailable);
}
// 把这个方法接到您的按钮上。
public void OnBannerButton() => banner.Toggle();
}如果 CloudX 初始化失败,请传入 cloudXAvailable: false,此时该组件会直接使用回退广告,而不会等待永远不会到来的加载回调。
演示应用在 FirstLookScreen.cs 中的做法与此相同,同样是在 CloudX 初始化返回结果时调用 Begin,而不是在 Start 中调用。它只有一处不同,而且并非必需:它在运行时用 AddComponent 添加该组件,而不是放在场景中,因为它要等初始化返回后才构建整个横幅流程。它的广告单元 ID 都是编译期常量——真正需要等待的是 cloudXAvailable。
插屏广告
两者中较简单的一种,因为展示即消耗广告:不需要清除任何标记,也不需要驱动周期。激励视频广告就是把本控制器换成激励视频相关调用,再加上奖励回调。
FirstLookInterstitialController.cs
完整的控制器,关键位置均有注释。可直接原样复制。
FirstLookSource.cs
每个事件上报的 CloudX / AdMob 枚举,同样需要复制。
在广告位触发时调用 Show()。如果返回 false,表示 CloudX 和回退广告都没有准备好,此时应继续游戏流程,不展示广告。
if (!firstLookInterstitial.Show())
{
ContinueToNextScene();
}广告展示、关闭或最终失败后,请在游戏准备好下一次广告位机会时再次调用 Load()。拥有此控制器的 MonoBehaviour 销毁时,请调用 Dispose()。
检查您的集成
顺序由控制器决定。控制器无法决定的是:您的 app key、广告单元 ID、后台配置,以及横幅广告场景中的接线是否正确——这些都在您的项目里。其中任何一项出错时,症状都是无声的:回退广告照常填充,广告照常出现,一切看起来都很正常。
一项检查即可覆盖。把来源打印出来,每个事件本身就带有它:
banner.AdLoaded += (source) => Debug.Log($"First Look banner loaded: {source}");使用真实的广告单元 ID 运行,并留意填充时是否出现 CloudX。如果始终只看到 AdMob,说明 CloudX 根本没有填充——请先检查 app key、广告单元 ID 和后台配置,然后再去排查控制器。
对于横幅广告,请观察超过一个冷却时间:每一次 AdMob 填充之前都应当先有一次新的 CloudX 请求,而不只是第一次。如果 CloudX 只被请求了一次就再无下文,说明 PassSpent 没有接线,或者除隐藏之外还有其他操作取消了待执行的轮次。
若要主动验证回退路径——例如上线前确认您的 AdMob 广告单元配置无误——可以把 CloudX 广告单元 ID 换成一个不在您后台配置中的字符串,这样每次 CloudX 加载都会失败,只能由回退广告填充。