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 开始。

三个文件都需要复制。它们合起来就是完整的流程。

它们位于 CloudX Unity 演示应用中——该版本有 CI 覆盖并已在真机上验证,出现问题时修复的也是它。请直接阅读仓库中的文件,而不是本页上的副本:副本会逐渐与之不一致,而过时的那一份一定是副本。

为什么宿主要单独一个文件

控制器是普通类,没有计时能力——没有 Update、没有协程、也没有 Invoke——因此它无法决定下一个轮次何时开始。FirstLookBannerCycle 就是这个计时器,它所遵守的三条规则,正是集成时最容易出错的地方:

  • PassSpent 后经过一个冷却时间再启动下一个轮次。 30 秒是常见的横幅广告间隔。立即启动会形成请求循环,因为新广告会渲染到可见的广告位中,立刻又结束下一个轮次。
  • 横幅广告被隐藏时取消该待执行的轮次。 否则已隐藏的广告位会在整个会话期间持续请求。
  • 隐藏期间不要重试失败的加载。 仅取消并不足够:玩家隐藏时若已有加载在进行中,它会在隐藏之后才返回结果。填充成功并无妨——该广告会保留到下一次 Show();失败则不同:它的重试会让请求重新开始,而此时 CancelInvoke 已无可取消。

直接原样使用该 cycle 组件,这三条便都已满足;冷却、退避和隐藏处理都在该文件中实现,并有注释说明。至于哪个 SDK 赢得某个轮次、以及填充如何保留到广告位再次展示,则属于控制器一侧,在该文件中有注释。

在场景中使用

FirstLookBannerCycle 添加到一个 GameObject 上,然后在 Inspector 中:

  • 把该组件拖到宿主的 banner 字段上;
  • 填写 cloudXBannerAdUnitIdadMobBannerAdUnitId——这三个都是序列化字段,默认为空,而 Begin 需要这两个 ID;
  • 把按钮的 OnClick 指向 OnBannerButton

然后在 CloudX 返回结果后——无论初始化成功、失败,还是超过您自己设置的超时——调用一次 BeginFirstLook。其余部分——冷却、退避、隐藏时取消——都在 cycle 组件内部。

不需要同时等待 Google Mobile Ads。初始化期间发起的加载会被它排队,而且回退本身就是惰性的,等待它只会推迟第一个轮次。

BannerButton.cs
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

插屏广告

两者中较简单的一种,因为展示即消耗广告:不需要清除任何标记,也不需要驱动周期。激励视频广告就是把本控制器换成激励视频相关调用,再加上奖励回调。

在广告位触发时调用 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 加载都会失败,只能由回退广告填充。