こんにちは。ちでソフトです。
本記事では、Google Mobile Ads Unity プラグイン の更新作業について紹介しています。
こちらは後編になります。
前編
・Google Mobile Ads Unity Plugin v7.0.1をv8.6.0へ更新します。
・バナー広告の実装の仕方をコード付きで紹介します。
後編
・リワード広告の実装の仕方をコード付きで紹介します。
・ビルド時のエラー対応を紹介します。
共通
・Unityエディタは2021.3.24fを使用しています。
スクリプトの編集
前編の内容を再掲させていただきます。
・最低限こんな具合に記述していけば、各広告が表示されるようになるはずですよ、といったレベルの内容です。また、すでにGoogle Mobile Ads Unity Pluginを導入されたことがある方には不必要な説明も含まれているかと存じます。
・一部のメソッドの実行方法(ボタンと組み合わせて手動実行にするか、または void Start() で自動実行にするかなど)につきましては、具体的な実装は各自にお任せさせていただいております(つまり丸ごとコピペしただけでは広告は表示されないです)。
・実際には広告を最適に表示させる手段が存在していたり、各状況に応じた細かいカスタマイズが必要になる可能性がありますが、それらの説明は割愛させていただきます。
・プラグインのバージョンごとに記述が異なってくる可能性も孕んでいるはずなのでご注意ください(実際、流用できたりできなかったりします)。
・つきましてはご参考にするしないは各自のご判断にお任せさせていただきます。実装に関連するご質問は受け付けておりませんのでご了承ください。
リワード広告
このクラスには、4つのメソッドが存在します。それぞれの役割をおおざっぱに説明しますと、
2 RegisterEventHandlers 広告の処理を定義する
3 ShowAd 広告を表示させて、さらに一定時間視聴後に報酬を与える
4 DestroyAd 広告を破棄する
・・・となっていまして、コードを記述する際は数字の順に各メソッドが行われるようにします。
なお後述しますが、処理の順序はスマホ実機と開発環境(Unityエディタ)とでは一部が異なるので注意が必要です。
まず考慮するべき事は、スマホ実機においては、広告を読み込む処理に時間がかかるという点です。
(Unityエディタ上ではテスト広告がすぐに表示されるため、初学者は見落としやすいかもです)
通信環境にもよりますが、最低でも数秒はかかると見込んでおいたほうがいいでしょう。
よって、LoadAdメソッドが終了しないうちにShowAdメソッドが実行された場合に、プレイする側からしたら不自然な挙動にならないような工夫が必要になります。
たとえば、リワード報酬をもらうボタンがゲームシーン上にはじめから表示されていると、「アレ押しても何ももらえないじゃん?」という期間がわずかながらも発生します。
Updateメソッドを活用してローディングメッセージを表示させたり、LaodAdメソッドが終了するまではShowAdメソッドを行うボタンは表示させない、みたいな処理を追加したほうがよいでしょうか?
また、通信環境は常に安定しているとは限りません。
ユーザーが電車に乗ってプレイしている最中に通信が遮断された間は、リワード報酬はもらえないようにするべきなのか?またその旨や原因を通知する仕組みも必要なのかどうかも判断が必要になるでしょう(ここは記述やルールが甘すぎると、抜け道を見つけたとばかりにわざとスマホ本体の通信機能を常時オフにしてプレイするユーザーも出現するかもしれませんね)。
そもそもリワード広告を、ゲームにおけるどの位置に配置するべきか?またどういう報酬をどのように与えるのか?という課題もありますが、公式による定義を熟読することもおすすめします。
...
using GoogleMobileAds.Api;
using UnityEngine.SceneManagement;
...
public class Rewarded : MonoBehaviour
{
// These ad units are configured to always serve test ads.
#if UNITY_ANDROID
private const string _adUnitId = "ca-app-pub-3940256099942544/5224354917";//テスト用のユニットID
#elif UNITY_IPHONE
private const string _adUnitId = "ca-app-pub-3940256099942544/1712485313";//テスト用のユニットID
#else
private const string _adUnitId = "unused";
#endif
private RewardedAd _rewardedAd;
private bool IsEarnReward;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// Loads the ad.
/// </summary>
public void LoadAd()
{
// Clean up the old ad before loading a new one.
if (_rewardedAd != null)
{
DestroyAd();
}
Debug.Log("Loading rewarded ad.");
// Create our request used to load the ad.
var adRequest = new AdRequest();
// Send the request to load the ad.
RewardedAd.Load(_adUnitId, adRequest, (RewardedAd ad, LoadAdError error) =>
{
// If the operation failed with a reason.
// 何らかの理由で操作が失敗した場合。通信エラーなど。---------------※①
if (error != null)
{
Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
return;
}
// If the operation failed for unknown reasons.
// 不明な理由で操作が失敗した場合。
// This is an unexpected error, please report this bug if it happens.
// これは予期しないエラーです。発生した場合はこのバグを報告してください。----------※②
if (ad == null)
{
Debug.LogError("Unexpected error: Rewarded load event fired with null ad and null error.");
return;
}
// The operation completed successfully.
// 操作は正常に完了しました。
Debug.Log("Rewarded ad loaded with response : " + ad.GetResponseInfo());
_rewardedAd = ad;
// Register to ad events to extend functionality.
// 機能を拡張するには広告イベントに登録します。
RegisterEventHandlers(ad);//---------------※③
});
}
/// <summary>
/// Shows the ad.
/// </summary>
public void ShowAd()
{
if (_rewardedAd != null && _rewardedAd.CanShowAd())
{
Debug.Log("Showing rewarded ad.");//---------------※④
_rewardedAd.Show((Reward reward) =>
{
Debug.Log(String.Format("Rewarded ad granted a reward: {0} {1}",
reward.Amount,
reward.Type));
this.IsEarnReward = true;//---------------※⑤
});
}
else
{
Debug.LogError("Rewarded ad is not ready yet.");
}
}
/// <summary>
/// Destroys the ad.
/// </summary>
public void DestroyAd()
{
if (_rewardedAd != null)
{
Debug.Log("Destroying rewarded ad.");
_rewardedAd.Destroy();
_rewardedAd = null;
}
}
private void RegisterEventHandlers(RewardedAd ad)
{
// Raised when the ad is estimated to have earned money.
// 広告が収益を上げたと推定される場合に発生します。
ad.OnAdPaid += (AdValue adValue) =>
{
Debug.Log(String.Format("Rewarded ad paid {0} {1}.",
adValue.Value,
adValue.CurrencyCode));
};
// Raised when an impression is recorded for an ad.
// 広告のインプレッションが記録されるときに発生します。
ad.OnAdImpressionRecorded += () =>
{
Debug.Log("Rewarded ad recorded an impression.");
};
// Raised when a click is recorded for an ad.
// 広告のクリックが記録されたときに発生します。
ad.OnAdClicked += () =>
{
Debug.Log("Rewarded ad was clicked.");
};
// Raised when the ad opened full screen content.
// 広告が全画面コンテンツを開いたときに発生します。
ad.OnAdFullScreenContentOpened += () =>
{
Debug.Log("Rewarded ad full screen content opened.");
};
// Raised when the ad closed full screen content.
// 広告が全画面コンテンツを閉じたときに発生します。
// Unityエディター上では、_rewardedAd.Showよりも先に行われるので注意
//---------------※⑥
ad.OnAdFullScreenContentClosed += () =>
{
Debug.Log("Rewarded ad full screen content closed.");
ChkEarnReward();
};
// Raised when the ad failed to open full screen content.
// 広告が全画面コンテンツを開けなかった場合に発生します。
ad.OnAdFullScreenContentFailed += (AdError error) =>
{
Debug.LogError("Rewarded ad failed to open full screen content with error : "
+ error);
};
}
/// <summary>
/// 説明用のメソッドです
/// </summary>
public void ChkEarnReward()
{
DestroyAd();//この実行もお忘れなく
Debug.Log($"IsEarnReward:{this.IsEarnReward}");
if (this.IsEarnReward)
{
SceneManager.LoadScene("OK");
}
else
{
SceneManager.LoadScene("NG");
}
}
}
ShowAdメソッドもここに追加したら動きそうだけど・・・?
だけど、あたし達はそのメソッドは別に取り扱うことにしましょう。
_rewardedAd != null && _rewardedAd.CanShowAd() になるまで 繰り返し監視を続けるみたいな 処理を別途自作すればいいかな・・・。
もう少しだけ付き合ってね♥
例えば、ユーザーがリワード広告を閉じた直後に、ShowAdメソッドに指定した、※⑤の報酬を受け取ったかどうか(広告を最後まで視聴したかどうか)を確認したいとします。
IsEarnReward変数がtrueになったかどうかをチェックして、内容に応じてシーンの遷移先を分岐するChkEarnRewardメソッドを用意してみました(リワード広告の本来の性質や実装ルールをここでは考えずに、あくまでも説明のためのコードとします)。
このChkEarnRewardメソッドは、OnAdFullScreenContentClosedに追加しています。つまり広告が閉じられたら実行されるわけですね。
そして、Unityエディタおよびスマホ実機(ビルド済み)にて、それぞれ挙動を確認してみたところ・・・。
検証してみたところ、スマホ実機では広告を最後まで視聴した時点でShowAdメソッド内の報酬をもらえる処理(※⑤におけるIsEarnReward変数がtrueになる処理)が実施されるのですが、Unityエディタでは、テスト広告上の【Close Ad】ボタンを押せる段階になった後、押した際にOnAdFullScreenContentClosedが実施され、その直後に報酬をもらえる処理が動く仕様になっていました。
よって、今回の例でいえばUnityエディタ上ではChkEarnRewardメソッドによるチェックが行われる際にはIsEarnReward変数がfalseのままになっているため、結果としてこのような違いが生じてしまうのでした(以前のAdsでは見られなかった現象のはずですが・・・)。
【スマホ実機】 【エディタ】
広告の表示 広告の表示
↓ ↓
広告の視聴 広告の視聴
↓ ↓
報酬の獲得 広告を閉じる
↓ ↓
広告を閉じる 報酬の獲得
実際にコードを記述される際は留意しておいていただければと存じます。
ビルドの注意点
Android
あ~ん!!エラーが出ちゃったわよ~~!!
手順
Unityエディターの上部メニューより【Edit】→【Project Settings】→【Player】→【Publishing Settings】→「Minify」より以下を選択します。
・「Use R8」
・「Release」
以上
バナー広告もリワード広告も表示されないわ!!
パソコンにスマホを接続したら、Unityエディタ上で「Android Logcat」を起動してみて。
手順
1)【Edit】→【Project Settings】→【Player】→【Publishing Settings】→「Build」より以下を選択します。
・Custom Proguard File
2) 以下のテキストファイルを編集します。
Assets\Plugins\Android\proguard-user.txt
-keep class com.google.unity.** {
*;
}
-keep public class com.google.android.gms.ads.** {
public *;
}
-keep public class com.google.ads.** {
public *;
}
以上
▼Logcatで取得したスマホ実機のキャプチャーです。これ、こういう時便利ですよね。
iOS
広告もきちんと表示することができました。
この記事では詳細は割愛させていただきます。
実は、今回の記事はもともとはGoogle Mobile Ads Unity プラグインの一つ前のバージョンであるv8.5.3を取り扱いながら執筆をしておりました。当方のアプリ「レモちゃんのスライドランド」はv8.5.3を導入して最新版をリリースいたしまして、その際の経験に基づいて執筆していたのですが、ちょうど下書き原稿の終わりかけの頃にv8.6.0がリリースされまして、あわてて調査からやり直した次第です。
プラグイン付属のサンプルのコードをDiffで比較したかぎり、広告表示関連のものにつきましてはv8.5.3と同じでしたので、加工前のコードはそのまま流用しております。また、ここで指摘した注意点など、取り扱い方法についても同じままのようです。
なお、Androidのビルドにつきましては、v8.5.3はjava.lang.UnsupportedOperationExceptionや、java.lang.ClassNotFoundExceptionといった上記のエラーがまったく出なかったため、すんなりと通った印象です。iOSのビルドも同様にスムーズにできました。
環境の違いにも寄るかと思いますが、もしもv8.6.0を導入された方で他のエラーが出てビルドに失敗したり不具合がみられるようであれば、v8.5.3の使用をご検討されてみることもよいのではと思います。
まとめにかえて
個人的なお話ですが、この記事を公開した背景としまして、前回の記事が思っていた以上にアクセス数があり、しかも内容に心残りがあったため、再びプラグインの導入に挑むことにいたしました。
その途上におきまして、とても驚くような出来事が起こりました。新しいバージョンが出ていた、というのもありますが・・・。深い悲しみに包まれ作業に力が入らなくなったものですが、今一度自分に何ができるのかを問い、あきらめることなく記事の完成に取り組んでみました。ITスクールの恩師からいただいた『一番大切なことは完成させること』という言葉も思い出しながら。
前編・後編併せまして、ようやく最低限必要な情報をお伝えするという使命は果たせたのではと思います。不十分と思われる方がいらっしゃるかもしれませんが、ご容赦くださいね。
皆さまの導入作業も無事に成功できますように!!
最後まで読んでいただきまして、どうもありがとうございました。