UnityのAdMobでUMPの同意取得を実装する方法

白い背景の中央に配置した公式AdMobロゴ

AdMobのテスト広告を表示できたら、次に整理したいのがユーザーのプライバシー選択と広告開始の順序です。この記事では、GoogleのUser Messaging Platform(UMP)を使って、必要な同意フォームを表示し、広告をリクエストできる状態になってから既存の広告処理を始める流れを紹介します。

プラグイン導入とアプリID設定を済ませた方向けの記事です。まだ準備できていない場合は、AdMobの導入記事から先に進めてください。手順の詳細は公式のUnity導入ガイドでも確認できます。

1. UMPで何をするのか

UMPは、AdMobの「プライバシーとメッセージ」で作成したメッセージをアプリに表示し、ユーザーの選択を扱うSDKです。表示には、Unityに設定したAdMobアプリIDに対応するメッセージを使用します。公式:UMPの設定

Googleは、EEA(欧州経済領域)、英国、スイスのユーザーにパーソナライズ広告を配信する際、Google認定CMPとIAB TCFの利用を要件にしています。これは「世界中の全ユーザーに、毎回、同じ同意画面を出せばよい」という意味ではありません。配信地域、メッセージ設定、広告やデータの扱いに応じて確認が必要です。UMPの導入だけで、アプリ全体の法令対応が完了すると判断しないでください。公式:同意管理の要件

2. AdMobでメッセージを準備する

アプリにコードを入れる前に、管理画面側を確認します。

  1. AdMobの「プライバシーとメッセージ」を開きます。
  2. 欧州の規制に関するメッセージを作成し、対象のアプリを選択します。
  3. 言語、ユーザーに提示する選択肢、対象地域を設定します。
  4. アプリのプライバシーポリシーURLを追加し、内容を確認してメッセージを公開します。

下書き保存と公開は別の操作です。対象地域はEEA・英国・スイス、または全地域を選択できるため、想定した設定になっているか確認します。公式:アプリ向けメッセージの作成

3. 広告を始める順序を変更する

初回の同意取得は、次の順序で進めます。

アプリ起動
  → ConsentInformation.Update(状態を更新)
  → LoadAndShowConsentFormIfRequired(必要ならフォーム表示)
  → CanRequestAds が true
  → MobileAds.Initialize
  → 既存の広告ロード処理

Update()はアプリ起動ごとに呼びます。自作の「同意済み」フラグだけで省略すると、期限切れなどの変化を見落とします。フォームが不要な場合も、LoadAndShowConsentFormIfRequired()のコールバックは呼ばれます。公式:同意情報とフォーム

CanRequestAds()は、Update()を呼ぶまではfalseです。呼び出し直後にも確認すると、前回起動時の同意状態を使って広告を開始できる場合があります。フォーム完了後と合わせて2か所で判定するため、開始処理には重複防止フラグを使います。更新やフォーム表示でエラーが出た場合も、エラーを記録してCanRequestAds()を確認し、falseなら広告を開始しません。公式:広告リクエスト可否

SDKやメディエーション先は初期化時に広告を先読みすることがあります。したがって、必要な同意処理やリクエスト設定を、既存のMobileAds.Initialize()より前に配置します。広告初期化はアプリ内で一度にまとめてください。公式:SDK初期化時の注意

4. Unityに組み込む実装骨格

以下は単一の起動管理オブジェクトを想定した、未実機検証の実装骨格です。広告のロード処理は含めていません。既存の広告管理クラスに統合して、コメントの位置からバナーなどの読み込みを呼び出します。

using GoogleMobileAds.Api;
using GoogleMobileAds.Common;
using GoogleMobileAds.Ump.Api;
using UnityEngine;
using UnityEngine.UI;

public class ConsentStartup : MonoBehaviour
{
    [SerializeField] private Button privacyButton;
    private bool adsInitializationStarted;

    private void Start()
    {
        if (privacyButton != null)
            privacyButton.gameObject.SetActive(false);

        var request = new ConsentRequestParameters();
        ConsentInformation.Update(request, error =>
        {
            MobileAdsEventExecutor.ExecuteInUpdate(() =>
            {
                RefreshPrivacyButton();
                if (error != null)
                {
                    Debug.LogWarning("UMP Update: " + error.Message);
                    TryStartAds();
                    return;
                }

                ConsentForm.LoadAndShowConsentFormIfRequired(formError =>
                {
                    MobileAdsEventExecutor.ExecuteInUpdate(() =>
                    {
                        if (formError != null)
                            Debug.LogWarning("UMP Form: " + formError.Message);
                        RefreshPrivacyButton();
                        TryStartAds();
                    });
                });
            });
        });

        // 前回起動時の状態でリクエスト可能な場合も確認します。
        TryStartAds();
    }

    private void TryStartAds()
    {
        if (adsInitializationStarted || !ConsentInformation.CanRequestAds())
            return;

        adsInitializationStarted = true;
        MobileAds.Initialize(status =>
        {
            MobileAdsEventExecutor.ExecuteInUpdate(() =>
            {
                if (status == null)
                {
                    Debug.LogWarning("広告SDKの初期化結果を取得できませんでした。");
                    return;
                }
                // ここから既存のテスト広告ロード処理を呼び出します。
            });
        });
    }

    private void RefreshPrivacyButton()
    {
        if (privacyButton == null) return;
        bool required = ConsentInformation.PrivacyOptionsRequirementStatus
            == PrivacyOptionsRequirementStatus.Required;
        privacyButton.gameObject.SetActive(required);
        privacyButton.interactable = required;
    }

    public void OpenPrivacyOptions()
    {
        ConsentForm.ShowPrivacyOptionsForm(error =>
        {
            MobileAdsEventExecutor.ExecuteInUpdate(() =>
            {
                if (error != null)
                    Debug.LogWarning("UMP Privacy options: " + error.Message);
                RefreshPrivacyButton();
                TryStartAds();
            });
        });
    }
}

UpdateのコールバックはFormErrorを受け取り、フォーム用の2つのメソッドもエラーを受け取るコールバックを使用します。API名と引数は、ConsentInformationのリファレンスとConsentFormのリファレンスで確認できます。

UnityのUI操作はメインスレッドへ渡しています。公式サンプルもプライバシーボタンの操作にMobileAdsEventExecutor.ExecuteInUpdate()を使用しています。公式:同意管理サンプル

既存のAdMobコードと統合する場所

  • 既存スクリプトのStart()などにある初期化と広告ロードを、上記の広告開始箇所へ移します。
  • 起動管理オブジェクトを1つにします。シーンごとに追加すると、インスタンスごとのフラグでは重複を防げません。複数シーンでは既存の常駐広告マネージャーに統合します。
  • 初期化後の再ロードやリワード広告の読み込みでも、その時点のCanRequestAds()を確認する設計にします。

CanRequestAds()は、必要な同意フローを終えて広告をリクエストできるかを調べるAPIです。「パーソナライズ広告への同意が得られた」「広告が必ず表示される」という判定には使いません。ConsentStatus.Obtainedも、パーソナライズ/非パーソナライズの区別を示しません。公式:状態の定義

同意処理が完了してCanRequestAds()がtrueになってもテスト広告が表示されない場合は、テスト広告が表示されないときで初期化や読み込みログを確認してください。

5. 同意を見直せるボタンを付ける

ゲームの設定画面などに「プライバシー設定」ボタンを用意します。InspectorでprivacyButtonを割り当て、ボタンのOn ClickからOpenPrivacyOptions()を呼ぶよう設定します。フォーム自身のUIをUnityで作り直す必要はありません。

導線が必要かはPrivacyOptionsRequirementStatus.Requiredで確認します。必要な場合は見えて操作できる状態にし、不要な場合は非表示にします。Unknownを「更新済み」と扱わず、状態更新後に確認します。公式:プライバシーオプション

このコードは初期化までの骨格です。ユーザーが設定を変更した後の、読み込み済み広告の破棄・再ロード、表示中の広告、メディエーション先の挙動は含んでいません。実際の広告マネージャー側で扱いを決め、実機で確認してください。

6. 開発用端末で地域条件をテストする

  1. デバッグ設定を入れる前のコードで開発用端末のアプリを起動し、ConsentInformation.Update()を呼びます。
  2. AndroidならLogcat、iOSならXcodeのコンソールでUMPのログを開き、テスト端末登録を案内する行のハッシュIDをコピーします。
  3. 下のコードでrequest作成部分を置き換え、TestDeviceHashedIdsにコピーしたIDを入れます。複数の端末で試す場合は、それぞれのIDを登録します。
  4. アプリをビルドし直して起動します。DebugGeographyによる地域指定は、登録したテスト端末だけに適用されます。公式:UMPのテスト
var request = new ConsentRequestParameters
{
    ConsentDebugSettings = new ConsentDebugSettings
    {
        DebugGeography = DebugGeography.EEA,
        TestDeviceHashedIds = new System.Collections.Generic.List<string>
        {
            "UMPログで確認したテスト端末のハッシュID"
        }
    }
};

初回起動相当を試す場合は、開発用の専用操作でConsentInformation.Reset()を呼び、その後もう一度Update()から開始します。Reset()は同意状態の永続保存を消去するAPIです。本番の毎回起動処理には入れません。公式:Reset API

地域を変えて比較する場合は、同意状態もリセットしてから試します。DebugGeographyは次の値から選びます。公式:DebugGeography

値 テストする条件
EEA EEA内の端末として扱う
RegulatedUSState 規制対象の米国州の端末として扱う
Other 規制が適用されていない地域の端末として扱う
Disabled 地域のデバッグ指定を無効にする

Otherは「EEA以外のすべて」を意味しません。古い例にあるNotEEAは非推奨で、現行APIではOtherを使用します。

UMPのテスト端末登録だけで広告のテスト設定も完了すると考えず、広告ロードには引き続きテスト広告ユニットなどを使用します。公開ビルドでは、UMPのデバッグ地域指定とテスト端末ID設定を除去してください。公式:UMPのテスト

7. よくあるつまずき

症状・思い込み 確認すること
フォームが表示されない 対象アプリ、アプリID、メッセージの公開状態、地域のデバッグ設定、既存の同意状態を確認します。表示不要なら正常です。
広告開始が2回走る Update()呼び出し直後とフォーム完了後の両方から開始し得ます。初期化を始める前に重複防止フラグを立てます。
エラー後は必ず広告を禁止すべき? 前回の状態でCanRequestAds()がtrueの場合があります。エラーを記録したうえでAPIの判定を確認し、falseならロードしません。
同意フォームを閉じたので広告をロードする フォーム終了だけで判断せず、CanRequestAds()を確認します。
前回同意済みだから更新は省略する アプリ独自のキャッシュを根拠に省略せず、起動ごとに更新します。

8. 公開前のチェックリスト

  • 自分のアプリIDと、公開したメッセージの対象アプリが一致している。
  • プライバシーポリシーURL、表示言語、対象地域、選択肢が意図した内容になっている。
  • 初回、再起動、フォーム不要、通信失敗を実機で確認した。
  • 同意フォームを操作した場合と、プライバシー設定を見直した場合の両方を確認した。
  • 必要な場合に設定画面のボタンが表示され、実際にフォームを開ける。
  • 広告初期化と初回ロードが重複せず、広告処理が判定を飛び越えて始まらない。
  • テスト専用のReset()、地域指定、UMPテスト端末設定を公開コードから除去した。
  • Android/iOSそれぞれの対象環境でビルドと動作を確認した。
  • 子ども向け設定や年齢に関するパラメーターは、アプリの対象者に合わせて別途確認した。上記コードは年齢判定を実装していない。

ストア公開に向けて開発者サイトの設定も準備する場合は、app-ads.txtの設定方法を確認してください。

参考コードのライセンス:Apache 2.0。