> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# First Look

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

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

此示例通过 [Google Mobile Ads Unity 插件](https://developers.google.com/admob/unity/quick-start) 使用 AdMob 插屏广告作为回退。相同模式也适用于其他回退聚合平台：先加载 CloudX；只有当 CloudX 未加载成功时才加载回退广告；当两个来源都未准备好时，让游戏流程继续。

<Info>
  创建控制器前，请先完成 CloudX 和 Google Mobile Ads 初始化。控制器外部仍应保留您现有的频控、重试和广告位触发时机规则。
</Info>

```csharp FirstLookInterstitialController.cs theme={null}
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 和回退广告都没有准备好，此时应继续游戏流程，不展示广告。

```csharp theme={null}
if (!firstLookInterstitial.Show())
{
    ContinueToNextScene();
}
```

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

<Tip>
  此示例采用懒加载回退：只有 CloudX 加载失败后才加载 AdMob。除非 CloudX 针对您的游戏建议其他上线策略，否则不要并行加载两个来源。
</Tip>
