[Unity] リワード広告 の実装手順 [Admob]

IT

アプリにおける リワード広告 とはアプリ内でユーザーに動画の視聴や広告の閲覧をしていただくことでその報酬として、アプリ内で使用できる様々な権利を与えるような広告です。たとえば、広告を視聴してもらうことで、ユーザーにアプリ内で使用できるコインを配布したり、カードを配布したりというような仕組みです。

今回はUnityでリワード広告を実装するにあたり、その手順と気を付けるべき点についてまとめます。




Admobの準備

Admobの準備についてはこちらの記事にまとめてあります。

こちらの例ではバナー広告を表示する例を説明していますが、初期化までの流れはリワード型広告も同じです。

アプリの振る舞いを決める

バナー型広告を表示させる時、広告の在庫不足などで広告が表示できなかったとしても、ユーザにとっては広告が表示されないだけなので、問題ないかもしれません。

しかし、リワード型の広告はユーザーが視聴する意思を持って広告を表示させます。もし広告の在庫がないなどを理由に、広告が表示できなかった場合、リワードを受け取りたいユーザーの期待を裏切ってしまうことになります。

私はアプリを実装する際、このように表示する広告がロードできていない場合でもユーザーが広告を表示する意思を示したのであれば、報酬を与えるように実装しています。

しかし、そもそもネットワークに接続せずリワード広告を表示させようとするなど、ユーザーの瑕疵で広告が表示できないような場合はエラーメッセージを表示しせ、対応を促します。

この辺りの異常系のハンドリングをきちんと定義しておくことが大事です

リワード型広告表示の実装

前述のアプリの振る舞いを踏まえての実装を以下にまとめます。

using GoogleMobileAds.Api;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StartWindow : MonoBehaviour
{
    private RewardedAd rewardedAd = null;
    // テスト用広告ユニットID
    private string adId = "ca-app-pub-3940256099942544/5224354917";
    private bool rewardedAdRetry = false;

    // Start is called before the first frame update
    void Start()
    {
        // Initialize the Google Mobile Ads SDK.
        MobileAds.Initialize(initStatus => { });
        LoadRewardAd();
    }

    // Update is called once per frame
    void Update()
    {
        if (rewardedAdRetry) {
            LoadRewardAd();
            rewardedAdRetry = false;
        }
    }

    public bool ShowRewardAd()
    {
        if (rewardedAd.IsLoaded()) {
            rewardedAd.Show();
            return true;
        } else {
            Debug.Log("not loaded");
            return false;
        }
    }

    void LoadRewardAd()
    {
        // Clean up banner ad before creating a new one.
        if (rewardedAd != null) {
            rewardedAd = null;
        }

        rewardedAd = new RewardedAd(adId);
        // Register for ad events.
        rewardedAd.OnAdLoaded += HandleRewardAdLoaded;
        rewardedAd.OnAdFailedToLoad += HandleRewardAdFailedToLoad;
        rewardedAd.OnAdOpening += HandleRewardedAdAdOpened;
        rewardedAd.OnAdClosed += HandleRewardedAdAdClosed;
        rewardedAd.OnUserEarnedReward += HandleUserEarnedReward;
        rewardedAd.OnAdFailedToShow += HandleRewardedAdFailedToShow;

        AdRequest adRequest = new AdRequest.Builder().Build();
        this.rewardedAd.LoadAd(adRequest);
    }
    public void HandleRewardAdLoaded(object sender, EventArgs args)
    {
        Debug.Log("HandleRewardAdLoaded event received with message: " + args);
        rewardedAdRetry = false;
    }

    public void HandleRewardAdFailedToLoad(object sender, AdFailedToLoadEventArgs args)
    {
        LoadAdError loadAdError = args.LoadAdError;
        int code = loadAdError.GetCode();
        string message = loadAdError.GetMessage();


        Debug.Log("Load error string: " + loadAdError.ToString());
        Debug.Log("code: " + code.ToString());

        MonoBehaviour.print(
            "HandleRewardedAdFailedToLoad event received with message: "
                             + message);
        if (code == 2 || code == 0) {
            Debug.Log("error");
        } else {
            Debug.Log("error no fill");
        }
        rewardedAdRetry = true;

    }
    public void HandleRewardedAdAdOpened(object sender, EventArgs args)
    {
        Debug.Log("HandleRewardedAdAdOpened event received");
    }
    public void HandleRewardedAdFailedToShow(object sender, AdErrorEventArgs args)
    {
        MonoBehaviour.print(
            "HandleRewardedAdFailedToShow event received with message: "
                             + args.AdError.GetMessage());
    }
    public void HandleUserEarnedReward(object sender, Reward args)
    {
        string type = args.Type;
        double amount = args.Amount;
        MonoBehaviour.print(
            "HandleRewardedAdRewarded event received for "
                        + amount.ToString() + " " + type);
    }
    public void HandleRewardedAdAdClosed(object sender, EventArgs args)
    {
        Debug.Log("HandleRewardedAdClosed event received");
        rewardedAdRetry = true;
    }
}

初期化

初期化はStart()でしています。初期化後にLoadRewardAd()でリワード広告のロードを開始します。もしかしたら、MobileAds.Initialize()の完了コールバックで広告のロードを開始する方がよいかもしれませんが、このコードで動作はしています。

広告ロード完了

広告のロードが完了したら、HandleRewardAdLoaded()がコールバックされます。一方、失敗した場合はHandleRewardAdFailedToLoad()がコールバックされます。 ここではHandleRewardAdFailedToLoad ()がコールバックされたらUpdate()で再度 LoadRewardAd ()で広告のロードを試みています。

頻発するとシステムへの負荷が心配なので、 Update()で LoadRewardAd ()を実行する場合は数秒時間を開けるなどするとよいです。

また、HandleRewardAdFailedToLoad()には様々なエラー要因があり、広告の在庫不足(no fill)という状態も存在します。 no fill の状態は広告提供側の在庫不足に問題があり、アプリやユーザーに問題があるわけではないので、報酬を提供してもいいかもしれません。loadAdError.GetCode();で取得したエラーコードでno fillかどうかを判定しています。

広告表示

広告の表示はShowRewardAd()で行います。IsLoaded()でロード済みか否かを判定し、ロード済みであれば広告を表示します。

ロード済みでない場合や、no fillの場合はこの ShowRewardAd ()でエラーの要因を通知、リターンするのがよいでしょう。

おわりに

リワード型はユーザーに見てもらった対価として、報酬を付与する広告です。なので、ネットワーク接続されていることが必要となるので、広告表示前にネットワーク状態を確認してもいいかもしれません。

また、広告の在庫がない場合のアプリの振る舞いについてしっかりと定義しておくことが必要です。ユーザーは広告の在庫の有無など知ったことではなく、報酬を得たいのに得られないという状態が起こることははアプリの評価を落とすことにもつながりかねません。

そして、思っているほど、単価が引くいことにも注意が必要です。視聴されても、その広告のアプリダウンロードやサイトリンクをクリックしなければ報酬が入らない広告もありますし、広告を視聴する国によっては非常に低い報酬しか得られないこともあります。

私のアプリはなぜかインドに気に入られているのですが、インドの単価は低いようで、結局バナーの方が収益は高かったりします。

実装する苦労の割りに収益は大したことがないというのが率直な感想です。

コメント