Search code examples
androidunity-game-engineadmobadmob-rewardedvideoad

Google Admob Reward Video callback called mutiple times after reloading scene in Unity


I'm developing an android game apk in Unity. I've integrated Google Admob SDK in my project and succeeded to show the Google admob reward video ads in my android apk by referring https://developers.google.com/admob/unity/rewarded-video.

However when scene is reloaded to restart game callback functions called multiple times.

callback function example:

// Called when an ad request has successfully loaded.
rewardBasedVideo.OnAdLoaded += HandleRewardBasedVideoLoaded;
// Called when an ad request failed to load.
rewardBasedVideo.OnAdFailedToLoad += HandleRewardBasedVideoFailedToLoad;
// Called when an ad is shown.
rewardBasedVideo.OnAdOpening += HandleRewardBasedVideoOpened;
// Called when the ad starts to play.
rewardBasedVideo.OnAdStarted += HandleRewardBasedVideoStarted;
// Called when the user should be rewarded for watching a video.
rewardBasedVideo.OnAdRewarded += HandleRewardBasedVideoRewarded;

scene loaded first time: callback functions are called 1 time.

scene loaded again: callback functions are called 2 times.

scene loaded again: callback functions are called 3 times.

...

I think the root cause is that callback functions are accumulated even reloading Unity scenes.

How can I make such callback functions be called only 1 time even changing scene?


Solution

  • So I finally figured out the solution to get the ads working properly

    First install the google ad SDK as shown by Google

    Follow the Google's admonb unity integration tutotrial here

    Then use the following script for Banner,Interstitial as well as Rewarded Video Ads

    SaveManager and related are some scripts used in my function

    Ignore them

    In the update function you have to check a boolean continuously to see if the ads are loaded or not. We will change this boolean's value using callbacks provided by Google Admob, as ads are being loaded paralelly on some other thread. I am not an expert in multithreading or parallel processing, but what I have found is you cannot call Unity functions from those callbacks, instead you can set some bool there, and then keep on checking the bool's value in update function

    See the onAdFailedToLoad event handlers of interstitial and rewarded ads

        using System;
        using UnityEngine;
        using UnityEngine.UI;
        using GoogleMobileAds.Api;
    
        // Example script showing how to invoke the Google Mobile Ads Unity plugin.
    
        public class adMob : MonoBehaviour
        {
        private BannerView bannerView;
        private InterstitialAd interstitial;
        private RewardedAd rewardedAd;
        private float deltaTime = 0.0f;
        private static string outputMessage = string.Empty;
    
        public Text coinValueHolder;
        int rewardAmount;
    
        private bool reqInterstitial = true;
        private bool reqRewardedAdVideo = true;
    
        public static adMob ins;
    
        public static string OutputMessage
        {
            set { outputMessage = value; }
        }
    
        public void Start()
        {
    
            #if UNITY_ANDROID
            string appId = "ca-app-pub-3940256099942544~3347511713";
            #elif UNITY_IPHONE
            string appId = "ca-app-pub-3940256099942544~1458002511";
            #else
            string appId = "unexpected_platform";
            #endif
    
            MobileAds.SetiOSAppPauseOnBackground(true);
    
            // Initialize the Google Mobile Ads SDK.
            MobileAds.Initialize(appId);
    
            this.CreateAndLoadRewardedAd();
        }
    
        // making the current object a singleton
        public void Awake()
        {
            if (ins == null)
            {
                ins = this;
                DontDestroyOnLoad(gameObject);
            }
            else if (ins != null)
            {
                Destroy(gameObject);
            }
        }
    
        public void Update()
        {
            // Calculate simple moving average for time to render screen. 0.1 factor used as 
            smoothing
            // value.
            this.deltaTime += (Time.deltaTime - this.deltaTime) * 0.1f;
    
            if (rewardAmount > 0)
            {
                coinValueHolder = 
                GameObject.FindGameObjectWithTag("coinText").GetComponent<Text>();
    
                SaveManager.coins += 100;
                SaveManager.ins.SaveDataFromDataObjects();
                coinValueHolder.text = SaveManager.coins.ToString();
                rewardAmount = 0;
            }
    
            if(reqInterstitial)
            {
                this.RequestInterstitial();
                reqInterstitial = false;
            }
    
            if(reqRewardedAdVideo)
            {
                this.CreateAndLoadRewardedAd();
                reqRewardedAdVideo = false;
            }
        }
    
        // Returns an ad request with custom ad targeting.
        public AdRequest CreateAdRequest()
        {
            return new AdRequest.Builder().Build();
        }
    
        public void RequestBanner()
        {
            // These ad units are configured to always serve test ads.
            #if UNITY_EDITOR
            string adUnitId = "unused";
            #elif UNITY_ANDROID
            string adUnitId = "ca-app-pub-3940256099942544/6300978111";
            #elif UNITY_IPHONE
            string adUnitId = "ca-app-pub-3940256099942544/2934735716";
            #else
            string adUnitId = "unexpected_platform";
            #endif
    
            // Clean up banner ad before creating a new one.
            if (this.bannerView != null)
            {
                this.bannerView.Destroy();
            }
    
            // Create a 320x50 banner at the top of the screen.
            this.bannerView = new BannerView(adUnitId, AdSize.Banner, AdPosition.Bottom);
    
            // Register for ad events.
            this.bannerView.OnAdLoaded += this.HandleAdLoaded;
            this.bannerView.OnAdFailedToLoad += this.HandleAdFailedToLoad;
            this.bannerView.OnAdOpening += this.HandleAdOpened;
            this.bannerView.OnAdClosed += this.HandleAdClosed;
            this.bannerView.OnAdLeavingApplication += this.HandleAdLeftApplication;
    
            // Load a banner ad.
            this.bannerView.LoadAd(this.CreateAdRequest());
        }
    
        public void DestroyBanner()
        {
            this.bannerView.Destroy();
        }
    
        public void RequestInterstitial()
        {
            // These ad units are configured to always serve test ads.
            #if UNITY_EDITOR
            string adUnitId = "unused";
            #elif UNITY_ANDROID
            string adUnitId = "ca-app-pub-3940256099942544/1033173712";
            #elif UNITY_IPHONE
            string adUnitId = "ca-app-pub-3940256099942544/4411468910";
            #else
            string adUnitId = "unexpected_platform";
            #endif
    
            // Clean up interstitial ad before creating a new one.
            if (this.interstitial != null)
            {
                this.interstitial.Destroy();
            }
    
            // Create an interstitial.
            this.interstitial = new InterstitialAd(adUnitId);
    
            // Register for ad events.
            this.interstitial.OnAdLoaded += this.HandleInterstitialLoaded;
            this.interstitial.OnAdFailedToLoad += this.HandleInterstitialFailedToLoad;
            this.interstitial.OnAdOpening += this.HandleInterstitialOpened;
            this.interstitial.OnAdClosed += this.HandleInterstitialClosed;
            this.interstitial.OnAdLeavingApplication += 
            this.HandleInterstitialLeftApplication;
    
            // Load an interstitial ad.
            this.interstitial.LoadAd(this.CreateAdRequest());
        }
    
        public void CreateAndLoadRewardedAd()
        {
            #if UNITY_EDITOR
            string adUnitId = "unused";
            #elif UNITY_ANDROID
            string adUnitId = "ca-app-pub-3940256099942544/5224354917";
            #elif UNITY_IPHONE
            string adUnitId = "ca-app-pub-3940256099942544/1712485313";
            #else
            string adUnitId = "unexpected_platform";
            #endif
            // Create new rewarded ad instance.
            this.rewardedAd = new RewardedAd(adUnitId);
    
            // Called when an ad request has successfully loaded.
            this.rewardedAd.OnAdLoaded += HandleRewardedAdLoaded;
            // Called when an ad request failed to load.
            this.rewardedAd.OnAdFailedToLoad += HandleRewardedAdFailedToLoad;
            // Called when an ad is shown.
            this.rewardedAd.OnAdOpening += HandleRewardedAdOpening;
            // Called when an ad request failed to show.
            this.rewardedAd.OnAdFailedToShow += HandleRewardedAdFailedToShow;
            // Called when the user should be rewarded for interacting with the ad.
            this.rewardedAd.OnUserEarnedReward += HandleUserEarnedReward;
            // Called when the ad is closed.
            this.rewardedAd.OnAdClosed += HandleRewardedAdClosed;
    
            // Create an empty ad request.
            AdRequest request = this.CreateAdRequest();
            // Load the rewarded ad with the request.
            this.rewardedAd.LoadAd(request);
        }
    
        public void ShowInterstitial()
        {
            if (this.interstitial.IsLoaded())
            {
                this.interstitial.Show();
            }
            else
            {
                MonoBehaviour.print("Interstitial is not ready yet");
            }
        }
    
        public void ShowRewardedAd()
        {
            if (this.rewardedAd.IsLoaded())
            {
                this.rewardedAd.Show();
            }
            else
            {
                MonoBehaviour.print("Rewarded ad is not ready yet");
            }
        }
    
        #region Banner callback handlers
    
        public void HandleAdLoaded(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleAdLoaded event received");
        }
    
        public void HandleAdFailedToLoad(object sender, AdFailedToLoadEventArgs args)
        {
            MonoBehaviour.print("HandleFailedToReceiveAd event received with message: " + 
            args.Message);
        }
    
        public void HandleAdOpened(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleAdOpened event received");
        }
    
        public void HandleAdClosed(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleAdClosed event received");
        }
    
        public void HandleAdLeftApplication(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleAdLeftApplication event received");
        }
    
        #endregion
    
        #region Interstitial callback handlers
    
        public void HandleInterstitialLoaded(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleInterstitialLoaded event received");
        }
    
        public void HandleInterstitialFailedToLoad(object sender, AdFailedToLoadEventArgs args)
        {
            MonoBehaviour.print(
                "HandleInterstitialFailedToLoad event received with message: " + args.Message);
            this.reqInterstitial = true;
        }
    
        public void HandleInterstitialOpened(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleInterstitialOpened event received");
        }
    
        public void HandleInterstitialClosed(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleInterstitialClosed event received");
            this.reqInterstitial = true;
        }
    
        public void HandleInterstitialLeftApplication(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleInterstitialLeftApplication event received");
        }
    
        #endregion
    
        #region RewardedAd callback handlers
    
        public void HandleRewardedAdLoaded(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleRewardedAdLoaded event received");
        }
    
        public void HandleRewardedAdFailedToLoad(object sender, AdErrorEventArgs args)
        {
            MonoBehaviour.print(
                "HandleRewardedAdFailedToLoad event received with message: " + args.Message);
            this.reqRewardedAdVideo = true;
        }
    
        public void HandleRewardedAdOpening(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleRewardedAdOpening event received");
        }
    
        public void HandleRewardedAdFailedToShow(object sender, AdErrorEventArgs args)
        {
            MonoBehaviour.print(
                "HandleRewardedAdFailedToShow event received with message: " + args.Message);
        }
    
        public void HandleRewardedAdClosed(object sender, EventArgs args)
        {
            MonoBehaviour.print("HandleRewardedAdClosed event received");
            this.reqRewardedAdVideo = true;
        }
    
        public void HandleUserEarnedReward(object sender, Reward args)
        {
            string type = args.Type;
            double amount = args.Amount;
            rewardAmount = (int)amount;
    
            MonoBehaviour.print(
                "HandleRewardedAdRewarded event received for "
                            + amount.ToString() + " " + type);
        }
    
        #endregion
    }
    

    Place the above script on a GameObject in Hierachy

    To call an ad use

        adMob.ins.DestroyBanner();
    

    or any other function ( declaring a static ins and referring the gameobject is known as singleton pattern, which is used in Managers like sound manager, Scene Manager, Add Managers )

        adMob.ins._Your-Function-Name_();
    

    You need to unsubscribe handles from events after using it or you will get unexpected behaviours:

    void OnDestroy()
    {
            // Called when an ad request has successfully loaded.
            rewardBasedVideo.OnAdLoaded -= HandleRewardBasedVideoLoaded;
            // Called when an ad request failed to load.
            rewardBasedVideo.OnAdFailedToLoad -= HandleRewardBasedVideoFailedToLoad;
            // Called when an ad is shown.
            rewardBasedVideo.OnAdOpening -= HandleRewardBasedVideoOpened;
            // Called when the ad starts to play.
            rewardBasedVideo.OnAdStarted -= HandleRewardBasedVideoStarted;
            // Called when the user should be rewarded for watching a video.
            rewardBasedVideo.OnAdRewarded -= HandleRewardBasedVideoRewarded;
    }
    

    Do it in OnDestroy method, for example.