First Look

让 CloudX 优先填充广告位,然后回退到您现有的聚合设置

First Look 让 CloudX 优先获得广告位填充机会,同时保留您现有的聚合设置作为回退路径。建议先从一个广告位开始,验证加载和展示行为后,再扩展到更多广告位。

此示例通过 Google Mobile Ads Unity 插件 使用 AdMob 插屏广告作为回退。相同模式也适用于其他回退聚合平台:先加载 CloudX;只有当 CloudX 未加载成功时才加载回退广告;当两个来源都未准备好时,让游戏流程继续。

FirstLookInterstitialController.cs
using System;
using CloudX;
using GoogleMobileAds.Api;

public sealed class FirstLookInterstitialController : IDisposable
{
    public enum Source
    {
        CloudX,
        AdMob,
    }

    public event Action<Source> AdLoaded;
    public event Action<Source, string> AdLoadFailed;
    public event Action<Source> AdShown;
    public event Action<Source, string> AdShowFailed;
    public event Action<Source> AdClosed;
    public event Action<Source> AdClicked;

    private readonly string cloudXAdUnitId;
    private readonly string adMobAdUnitId;

    private InterstitialAd adMobInterstitial;
    private bool isLoadingCloudX;
    private bool isLoadingAdMob;
    private bool isDisposed;

    public FirstLookInterstitialController(
        string cloudXAdUnitId,
        string adMobAdUnitId)
    {
        this.cloudXAdUnitId = cloudXAdUnitId;
        this.adMobAdUnitId = adMobAdUnitId;

        CloudXAdsCallbacks.Interstitial.OnAdLoadSuccess += OnCloudXLoaded;
        CloudXAdsCallbacks.Interstitial.OnAdLoadFailed += OnCloudXLoadFailed;
        CloudXAdsCallbacks.Interstitial.OnAdShowSuccess += OnCloudXShown;
        CloudXAdsCallbacks.Interstitial.OnAdShowFailed += OnCloudXShowFailed;
        CloudXAdsCallbacks.Interstitial.OnAdHidden += OnCloudXClosed;
        CloudXAdsCallbacks.Interstitial.OnAdClicked += OnCloudXClicked;
    }

    public void Load()
    {
        if (isDisposed ||
            isLoadingCloudX ||
            isLoadingAdMob ||
            CloudXSdk.IsInterstitialReady(cloudXAdUnitId) ||
            (adMobInterstitial != null && adMobInterstitial.CanShowAd()))
        {
            return;
        }

        isLoadingCloudX = true;
        CloudXSdk.LoadInterstitial(cloudXAdUnitId);
    }

    public bool Show()
    {
        if (isDisposed)
        {
            return false;
        }

        if (CloudXSdk.IsInterstitialReady(cloudXAdUnitId))
        {
            CloudXSdk.ShowInterstitial(cloudXAdUnitId);
            return true;
        }

        return ShowAdMobFallback();
    }

    public void Dispose()
    {
        if (isDisposed)
        {
            return;
        }

        isDisposed = true;

        CloudXAdsCallbacks.Interstitial.OnAdLoadSuccess -= OnCloudXLoaded;
        CloudXAdsCallbacks.Interstitial.OnAdLoadFailed -= OnCloudXLoadFailed;
        CloudXAdsCallbacks.Interstitial.OnAdShowSuccess -= OnCloudXShown;
        CloudXAdsCallbacks.Interstitial.OnAdShowFailed -= OnCloudXShowFailed;
        CloudXAdsCallbacks.Interstitial.OnAdHidden -= OnCloudXClosed;
        CloudXAdsCallbacks.Interstitial.OnAdClicked -= OnCloudXClicked;

        CloudXSdk.DestroyInterstitial(cloudXAdUnitId);
        DestroyAdMobInterstitial();
    }

    private void OnCloudXLoaded(CloudXAd ad)
    {
        if (ad.AdUnitId != cloudXAdUnitId)
        {
            return;
        }

        isLoadingCloudX = false;
        AdLoaded?.Invoke(Source.CloudX);
    }

    private void OnCloudXLoadFailed(string adUnitId, CloudXError _)
    {
        if (adUnitId != cloudXAdUnitId)
        {
            return;
        }

        isLoadingCloudX = false;
        LoadAdMobFallback();
    }

    private void OnCloudXShown(CloudXAd ad)
    {
        if (ad.AdUnitId == cloudXAdUnitId)
        {
            AdShown?.Invoke(Source.CloudX);
        }
    }

    private void OnCloudXShowFailed(CloudXAd ad, CloudXError error)
    {
        if (ad.AdUnitId != cloudXAdUnitId)
        {
            return;
        }

        if (!ShowAdMobFallback())
        {
            AdShowFailed?.Invoke(Source.CloudX, error.Message);
        }
    }

    private void OnCloudXClosed(CloudXAd ad)
    {
        if (ad.AdUnitId == cloudXAdUnitId)
        {
            AdClosed?.Invoke(Source.CloudX);
        }
    }

    private void OnCloudXClicked(CloudXAd ad)
    {
        if (ad.AdUnitId == cloudXAdUnitId)
        {
            AdClicked?.Invoke(Source.CloudX);
        }
    }

    private void LoadAdMobFallback()
    {
        if (isDisposed ||
            isLoadingAdMob ||
            (adMobInterstitial != null && adMobInterstitial.CanShowAd()))
        {
            return;
        }

        isLoadingAdMob = true;
        DestroyAdMobInterstitial();

        InterstitialAd.Load(
            adMobAdUnitId,
            new AdRequest(),
            (ad, error) =>
            {
                isLoadingAdMob = false;

                if (isDisposed)
                {
                    ad?.Destroy();
                    return;
                }

                if (error != null || ad == null)
                {
                    AdLoadFailed?.Invoke(
                        Source.AdMob,
                        error?.GetMessage() ?? "AdMob returned no ad");
                    return;
                }

                adMobInterstitial = ad;
                RegisterAdMobEvents(ad);
                AdLoaded?.Invoke(Source.AdMob);
            });
    }

    private bool ShowAdMobFallback()
    {
        if (adMobInterstitial == null || !adMobInterstitial.CanShowAd())
        {
            return false;
        }

        adMobInterstitial.Show();
        return true;
    }

    private void RegisterAdMobEvents(InterstitialAd ad)
    {
        ad.OnAdFullScreenContentOpened += () =>
            AdShown?.Invoke(Source.AdMob);

        ad.OnAdFullScreenContentClosed += () =>
        {
            DestroyAdMobInterstitial();
            AdClosed?.Invoke(Source.AdMob);
        };

        ad.OnAdFullScreenContentFailed += error =>
        {
            DestroyAdMobInterstitial();
            AdShowFailed?.Invoke(Source.AdMob, error.GetMessage());
        };

        ad.OnAdClicked += () =>
            AdClicked?.Invoke(Source.AdMob);
    }

    private void DestroyAdMobInterstitial()
    {
        adMobInterstitial?.Destroy();
        adMobInterstitial = null;
    }
}

在广告位触发时调用 Show()。如果返回 false,表示 CloudX 和回退广告都没有准备好,此时应继续游戏流程,不展示广告。

if (!firstLookInterstitial.Show())
{
    ContinueToNextScene();
}

广告展示、关闭或最终失败后,请在游戏准备好下一次广告位机会时再次调用 Load()。拥有此控制器的 MonoBehaviour 销毁时,请调用 Dispose()