본문 바로가기

[unity] 모듈 제작 : 광고 시스템(Admob, UnityAds) 만들기(2)

앤디가이 2022. 7. 8.

Unity에서 사용하는 수익 구조 중 광고(Admob, UnityAds) 모듈을 제작하는 방법 및 사용하는 방법에 대해 알아보자. (2편)

 

이전 편에 이어서 스크립트 작성 및 테스트 코드를 만들어 보자.

2022.07.08 - [unity3d/Modules] - [unity] 모듈 제작 : 광고 시스템(Admob, UnityAds) 만들기(1)

 

[unity] 모듈 제작 : 광고 시스템(Admob, UnityAds) 만들기(1)

Unity에서 사용하는 수익 구조 중 광고(Admob, UnityAds) 모듈을 제작하는 방법 및 사용하는 방법에 대해 알아보자. (1편) 게임 앱 개발에 있어 수익화는 무엇보다 중요하다. 대부분의 게임은 인앱 결제

wonjuri.tistory.com

 

 

5. Script 작성 : AdAdmob.cs

이전 편에서 작성했던 Adbase를 상속받는 AdAdmob.cs 스크립트를 만들어 준다.

AdAdmob 클래스는 애드몹의 배너, 동영상 광고, 전면광고의 제어를 담당한다.

 

Admob 사이트에서 배너, 전면, 보상형 광고 단위를 생성 후 ID를  코드에 입력해준다.

광고 단위 ID는 ca-app-pub- 형태로 시작한다.

 

스크립트에 아래와 같이 작성해보자.

using System;
using UnityEngine;
using GoogleMobileAds.Api;

namespace Modules.Ads
{
    public class AdAdmob : Adbase
    {

        [SerializeField]
        protected string bannerID = "ca-app-pub-**********"; //사용자가 바꿀 부분
        [SerializeField]
        protected string interstitialID = "ca-app-pub-**********"; //사용자가 바꿀 부분
        [SerializeField]
        protected string rewardID = "ca-app-pub-**********"; //사용자가 바꿀 부분

        protected BannerView bannerAd = null;

        protected InterstitialAd interstitialAd = null;

        protected RewardedAd rewardAd = null;

        protected bool IsRewared { get; set; }

        public override void OnInitialize()
        {
            MobileAds.Initialize((initStatus) =>
            {
                if (isBannerUse)
                {
                    LoadBanner();
                    bannerAd.OnAdFailedToLoad += BannerAd_OnAdFailedToLoad;
                    ShowBanner();
                }

                if (isInterstitialUse)
                {
                    interstitialAd = new InterstitialAd(interstitialID);
                    LoadInterstitial();
                    interstitialAd.OnAdClosed += InterstitialAd_OnAdClosed;
                }

                if (isRewardUse)
                {
                    rewardAd = new RewardedAd(rewardID);
                    LoadRewardBasedVideo();
                    rewardAd.OnAdClosed += HandleRewardBasedVideoClosed;
                    rewardAd.OnUserEarnedReward += HandleRewardBasedVideoRewarded;
                    rewardAd.OnAdFailedToLoad += RewardAd_OnAdFailedToLoad;
                }
            });
        }

        /// <summary>
        /// Destory Event
        /// </summary>
        public void OnDestroy()
        {
            if (isBannerUse && bannerAd != null)
            {

                bannerAd.OnAdFailedToLoad -= BannerAd_OnAdFailedToLoad;
            }

            if (isInterstitialUse && interstitialAd != null)
            {
                interstitialAd.OnAdClosed -= InterstitialAd_OnAdClosed;
            }

            if (isRewardUse && rewardAd != null)
            {
                rewardAd.OnAdClosed -= HandleRewardBasedVideoClosed;
                rewardAd.OnUserEarnedReward -= HandleRewardBasedVideoRewarded;
                rewardAd.OnAdFailedToLoad -= RewardAd_OnAdFailedToLoad;
            }
        }

        #region Banner

        protected void LoadBanner()
        {
            bannerAd = new BannerView(bannerID, AdSize.Banner, AdPosition.Bottom);
            AdRequest request = new AdRequest.Builder().Build();
            bannerAd.LoadAd(request);
        }

        public void ShowBanner()
        {
            if (bannerAd != null)
                bannerAd.Show();
        }

        public void HideBanner()
        {
            if (bannerAd != null)
                bannerAd.Hide();
        }
        private void BannerAd_OnAdFailedToLoad(object sender, AdFailedToLoadEventArgs e)
        {
            Debug.Log(e.ToString()) ;
        }

        #endregion


        #region Interstitial

        protected void LoadInterstitial()
        {
            if (!interstitialAd.IsLoaded() && !string.IsNullOrEmpty(interstitialID))
            {
                AdRequest request = new AdRequest.Builder().Build();
                this.interstitialAd.LoadAd(request);
            }
        }

        public override void ShowInterstitial()
        {
            if (interstitialAd.IsLoaded())
                interstitialAd.Show();
        }

        void InterstitialAd_OnAdClosed(object sender, System.EventArgs e)
        {
            LoadInterstitial();
        }

        #endregion

        #region RewardVideoAds


        public override void ShowRewardVideo(System.Action<AdResultType> result)
        {
            OnRewardResult = result;
            ShowRewardVideo();
        }

        public void ShowRewardVideo()
        {

            if (rewardAd.IsLoaded())
            {
                IsRewared = false;
                rewardAd.Show();
            }
            else
            {

                if (OnRewardResult != null)
                {
                    OnRewardResult(AdResultType.Fail);
                    OnRewardResult = null;
                }

                LoadRewardBasedVideo();
            }
        }

        protected void LoadRewardBasedVideo()
        {

            AdRequest request = new AdRequest.Builder().Build();

            rewardAd.LoadAd(request);

        }

        protected virtual void HandleRewardBasedVideoRewarded(object sender, Reward e)
        {
            Debug.Log("[동영상광고 보상지급]" + "보상타입 :" + e.Type + " / " + "보상개수 :" + e.Amount);
            IsRewared = true;
        }

        protected void HandleRewardBasedVideoClosed(object sender, EventArgs e)
        {
            Debug.Log("비디오 종료 : " + e.ToString());
            if (OnRewardResult != null)
            {
                if (IsRewared)
                {
                    OnRewardResult(AdResultType.Sucess);
                }
                else
                {
#if UNITY_EDITOR
                    OnRewardResult(AdResultType.Sucess);
#else
                    OnRewardResult(AdResultType.Fail);
#endif
                }

                OnRewardResult = null;
            }
            IsRewared = false;
            LoadRewardBasedVideo();
        }

        protected void RewardAd_OnAdFailedToLoad(object sender, AdFailedToLoadEventArgs e)
        {
            Debug.Log(e.ToString());
        }



        #endregion
    }
}

 

6. Script 작성 : AdUnity.cs

이전편에서 작성했던 Adbase를 상속받는 AdUnity.cs 스크립트도 만들어 주자.

AdUnity 클래스는 UnityAds의 배너, 동영상 광고, 전면광고의 제어를 담당한다.

 

Service에서 확인할 수 있는 Unity Ads의 gameID 값을 넣어준다.

using System;
using UnityEngine;
using UnityEngine.Advertisements;

namespace Modules.Ads
{
    public class AdUnity : Adbase, IUnityAdsInitializationListener, IUnityAdsLoadListener, IUnityAdsShowListener
    {
        protected string placement_reward_Id = "rewardedVideo";

        [SerializeField]
        protected string android_game_id = "*******"; //사용자가 바꿀 부분
        [SerializeField]
        protected string ios_game_id = "********"; //사용자가 바꿀 부분

        private const string BANNER_PLACEMENT = "banner";
        private const string VIDEO_PLACEMENT = "video";
        private const string REWARDED_VIDEO_PLACEMENT = "rewardedVideo";

        private BannerPosition bannerPosition = BannerPosition.BOTTOM_CENTER;
        private bool testMode = true;
        
        public override void OnInitialize()
        {
#if UNITY_IOS
        Advertisement.Initialize(ios_game_id, testMode, this);
#else
            Advertisement.Initialize(android_game_id, testMode, this);
#endif
            if (isBannerUse)
            {
                Advertisement.Banner.SetPosition(bannerPosition);
                Advertisement.Banner.Show(BANNER_PLACEMENT);
            }

            if (isInterstitialUse)
            {
                Advertisement.Load(VIDEO_PLACEMENT, this);
            }

            if (isRewardUse)
            {
                Advertisement.Load(REWARDED_VIDEO_PLACEMENT, this);
            }
        }


        #region Interface Implementations
        public void OnInitializationComplete()
        {
            Debug.Log("Init Success");
        }

        public void OnInitializationFailed(UnityAdsInitializationError error, string message)
        {
            Debug.Log($"Init Failed: [{error}]: {message}");
        }

        public void OnUnityAdsAdLoaded(string placementId)
        {
            Debug.Log($"Load Success: {placementId}");
        }

        public void OnUnityAdsFailedToLoad(string placementId, UnityAdsLoadError error, string message)
        {
            Debug.Log($"Load Failed: [{error}:{placementId}] {message}");
        }

        public void OnUnityAdsShowFailure(string placementId, UnityAdsShowError error, string message)
        {
            Debug.Log($"OnUnityAdsShowFailure: [{error}]: {message}");
        }

        public void OnUnityAdsShowStart(string placementId)
        {
            Debug.Log($"OnUnityAdsShowStart: {placementId}");
        }

        public void OnUnityAdsShowClick(string placementId)
        {
            Debug.Log($"OnUnityAdsShowClick: {placementId}");
        }

        /// <summary>
        /// 보상형 광고 콜백함수.
        /// </summary>
        /// <param name="placementId"></param>
        /// <param name="showCompletionState"></param>
        public void OnUnityAdsShowComplete(string placementId, UnityAdsShowCompletionState showCompletionState)
        {
            switch(showCompletionState)
            {
                case UnityAdsShowCompletionState.COMPLETED:
                    {
                        if(OnRewardResult != null)
                            OnRewardResult(AdResultType.Sucess);
                    }
                    break;
                case UnityAdsShowCompletionState.SKIPPED:
                case UnityAdsShowCompletionState.UNKNOWN:
                    {
                        if (OnRewardResult != null)
                            OnRewardResult(AdResultType.Fail);
                    }
                    break;
            }
            OnRewardResult = null;
        }

        /// <summary>
        /// 보상형 동영상 광고 Show
        /// </summary>
        /// <param name="result"></param>
        public override void ShowRewardVideo(Action<AdResultType> result)
        {
            OnRewardResult = result;
            Advertisement.Show(REWARDED_VIDEO_PLACEMENT, this);
        }

        public override void ShowInterstitial()
        {
            Advertisement.Show(VIDEO_PLACEMENT, this);
        }
        #endregion
    }
}

 

 

7. Script 작성 : AdManager.cs

우리가 만든 AdBase 항목을 호출할 컨트롤 클래스인 AdManager 클래스를 제작해보자.

AdManager.cs 클래스를 만든 후 아래와 같이 작성해주자.

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

namespace Modules.Ads
{
    /// <summary>
    /// 구글 애드몹/UnityAds 사용을 위한 매니저 클래스
    /// </summary>
    public class AdManager : MonoBehaviour
    {
        //싱글톤
        public static AdManager Instance { get; private set; }

        private Dictionary<Advertiser, Adbase> dic_advertiser = new Dictionary<Advertiser, Adbase>();

        private void Awake()
        {
            Instance = this;

            Adbase[] ads = FindObjectsOfType<Adbase>();

            foreach(Adbase ad in ads)
            {
                if(!dic_advertiser.ContainsKey(ad.Type))
                {
                    dic_advertiser.Add(ad.Type, ad);
                }
            }
        }

        private void Start()
        {
            OnInitialize();
        }

        private void OnDestroy()
        {
            Instance = null;
        }

        /// <summary>
        /// 모든 광고 모듈 초기화
        /// </summary>
        public void OnInitialize()
        {
            foreach(KeyValuePair<Advertiser, Adbase> advertiser in dic_advertiser)
            {
                advertiser.Value.OnInitialize();
            }
        }

        /// <summary>
        /// 보상형 동영상 광고 호출
        /// </summary>
        /// <param name="advertiser"></param>
        /// <param name="result"></param>
        public void ShowRewardVideo(Advertiser advertiser, System.Action<AdResultType> result)
        {
            if(dic_advertiser.ContainsKey(advertiser))
                dic_advertiser[advertiser].ShowRewardVideo(result);
        }

        /// <summary>
        /// 전면 광고 호출
        /// </summary>
        /// <param name="advertiser"></param>
        public void ShowInterstitialAds(Advertiser advertiser)
        {
            if (dic_advertiser.ContainsKey(advertiser))
                dic_advertiser[advertiser].ShowInterstitial();
        }
    }
}

 

매니저 클래스는 ShowRewardVideo() 함수를 통해 보상형 광고를 호출할 수 있는 인터페이스를 제공해주며, ShowInterstitialAds() 함수를 통해 전면광고를 호출할 수 있는 인터페이스를 제공한다.

배너 광고는 각 광고 클래스(AdUnity, AdAdmob)에서 사용 여부에 따라 바로 Show 하기 때문에 매니저 클래스에서 따로 컨트롤하지는 않는다.

 

8. 호출 테스트

Scene에 GameObject를 만든 후 AdSystem 이라고 이름을 변경 후 AdManager 스크립트를 붙여준다.

AdSystem GameObject
AdSystem GameObject

AdSystem 밑에 자식으로 AdUnity라고 GameObject를 만든 후 AdUnity 스크립트를 붙여준다.

배너, 전면광고, 리워드 광고 중 사용하려는 광고를 On, Off 해준다.

Game ID를 넣어준다.

AdUnity GameObject
AdUnity GameObject

AdSystem 밑에 자식으로 AdAdmob이라고 GameObject를 만든 후 AdAdmob 스크립트를 붙여준다.

배너, 전면광고, 리워드 광고 중 사용하려는 광고를 On, Off 해준다.

광고 ID를 넣어준다.

AdAdmob GameObject
AdAdmob GameObject

이제 Scene에 TestCall을 위한 GameObject를 하나 만든 후 정상적으로 호출이 되는지 확인해 보자.

using UnityEngine;

namespace Modules.Ads
{
    public class AdTester : MonoBehaviour
    {
        //유니티 전면광고
        public void OnClicked_UnityInterstitialAds()
        {
            AdManager.Instance.ShowInterstitialAds(Advertiser.Unity);
        }
        //애드몹 전면광고
        public void OnClicked_AdmobInterstitialAds()
        {
            AdManager.Instance.ShowInterstitialAds(Advertiser.Admob);
        }
        //유니티 보상형 광고
        public void OnClicked_UnityReward()
        {
            AdManager.Instance.ShowRewardVideo(Advertiser.Unity, OnRewardAction);
        }
        //애드몹 보상형 광고
        public void OnClicked_AdmobReward()
        {
            AdManager.Instance.ShowRewardVideo(Advertiser.Admob, OnRewardAction);
        }
        //보상 콜백
        private void OnRewardAction(AdResultType result)
        {
            Debug.Log(result);
        }
    }
}

 

9. 결과 화면

간단히 UI로 버튼을 만들어 준 후, AdTester 클래스의 함수에 연결해 준다.

 

 

Unity 에디터에서 테스트 결과 잘 출력되는 걸 확인할 수 있다.

위 모듈을 사용하면 사용자는 원하는 시점에 유니티 or 구글 애드몹을 선택적으로 호출하여 컨트롤할 수 있다.

참고로 두 개 광고 모듈 사용 시 배너 광고는 둘 중 하나만 사용해야 한다. (하나는 Enable을 꺼주도록 하자 )

 

유니티 보상형 광고가 하루 제한 개수가 있는 만큼 유니티 보상형 광고 호출 -> 광고 없음 -> 애드몹 보상형 광고 호출 식으로 부족한 광고를 채우는 방법도 고려 가능하다.

 

 

댓글