Введение

Политика монетизации - одна из самых важных составляющих мобильного приложения. Доходит и до того, что разработчики сначала продумывают маркетинговую схему, а уже потом натягивают на нее геймплей. Сейчас существует множество проверенных способов повысить ARPU (Average Revenue Per User) своей игры: лутбоксы, батлпасы, прямые продажы апгрейдов или косметических предметов. В этой статье мы рассмотрим самый простой, старый, но от этого не менее эффективный способ получения прибыли с мобильного приложения - внутриигровая реклама, а так же поговорим о том, как внедрить ее в Unity на примере Google AdMob.

Что выбрать новичку? Unity Ads vs AdMob

Unity Ads и AdMob (Google) - два самых известных рекламных сервиса на рынке. По каждому существует тонна ресурсов в интернете, но с кем же из них сделать первый шаг?

Если вы читали название статьи (а вы его читали), вам должно быть очевидно, на кого пал мой выбор. Есть несколько причин, по которым я советую вам выбрать решение от Google:

  1. У данного сервиса выше показатели CPC(cost-per-click);

  2. Опыт работы со сторонним API;

  3. Думаю, что вам все равно придется заводить AdMob-аккаунт, так почему бы не сделать это сейчас;

Подключаем AdMob

На YouTube есть много материала по теме подключения сервиса. Лично я использовал этот ролик, а также официальную документацию. Ниже приведу пошаговую инструкцию в формате текста с картинками.

Пошагово установка выглядит так:

  1. Регистрируемся в AdMob

  2. Создаем проект и настраиваем блоки (речь о каждом из блоков пойдет ниже)

  3. Скачиваем с гитхаба официальный плагин

  4. Импортируем в Unity (Assets - Import Package)

  5. Прописываем в Assets - Google Mobile Ads - Settings ID своего приложения. Оно лежит на странице приложения в AdMob: Настройки приложения - Идентификатор приложения

  1. Инициализируем рекламу

using GoogleMobileAds.Api;
public class AdInitialize : MonoBehaviour
{
    private void Awake()
    {
        MobileAds.Initialize(initStatus => { });
    }
}
  1. Создаем классы необходимых рекламных блоков (рассмотрим ниже).

Рекламные блоки

Теперь обсудим, какие виды рекламных блоков нам предоставляет AdMob. Для того, чтобы создать блок, первым делом вы должны выбрать его тип. На сайте нас встречает вот такой список:

Ниже мы рассмотрим каждый вариант, в документации также есть необходимый код (меню Ad Formats слева).

ВАЖНО: В скриптах ниже вместо ID своих блоков на этапе тестирования нужно использовать тестовые ID, лежащие тут. Иначе ваше приложение забанят. В моем коде везде используются тестовые ID.

Баннер

Прямоугольный блок, который будет постоянно висеть на экране. Может обновляться автоматически. Частоту обновлений можно прописать вручную (Расширенные настройки - Автоматическое обновление - Специальный). Для вызова используем класс BannerView. В конструкторе указывается размер и положение на экране (в формате перечисления). Отрисовывается автоматически при загрузке сцены.

using GoogleMobileAds.Api;
public class BannerAd : MonoBehaviour
{
    private BannerView _bannerAd;
    private const string _bannerId = "ca-app-pub-3940256099942544/6300978111";
    private void OnEnable()
    {
        _bannerAd = new BannerView(_bannerId, AdSize.Banner, AdPosition.Top);
        AdRequest adRequest = new AdRequest.Builder().Build();
        _bannerAd.LoadAd(adRequest);
    }
    public void ShowAd()
    {
        _bannerAd.Show();
    }
}
Баннер
Баннер

Межстраничное объявление

Стандартная реклама полноэкранного формата. Вызывается методом Show(). Во время показа ставит игру на паузу.

using GoogleMobileAds.Api;
public class InterAd : MonoBehaviour
{
    private InterstitialAd _interAd;
    private const string _interstitialId = "ca-app-pub-3940256099942544/8691691433";
    private void OnEnable()
    {
        _interAd = new InterstitialAd(_interstitialId);
        AdRequest adRequest = new AdRequest.Builder().Build();
        _interAd.LoadAd(adRequest);
    }
    public void ShowAd()
    {
        if (_interAd.IsLoaded())
        {
            _interAd.Show();
        }
    }
}

Межстраничное объявление с вознаграждением

Реклама предыдущего формата с единственным отличием - после просмотра обратным вызовом можно начислить пользователю награду.

public class RewardedInterAD : MonoBehaviour
{
    private RewardedInterstitialAd _rewardAd;
    private const string _interstitialId = "ca-app-pub-3940256099942544/5354046379";
    private void OnEnable()
    { 
        AdRequest adRequest = new AdRequest.Builder().Build();
        RewardedInterstitialAd.LoadAd(_interstitialId, adRequest, adLoadCallback);
    }
    private void adLoadCallback(RewardedInterstitialAd ad, AdFailedToLoadEventArgs error)
    {
        if (error == null)
        {
            _rewardAd = ad;
        }
    }
    public void ShowAd()
    {
        if (_rewardAd != null)
        {
            _rewardAd.Show(userEarnedRewardCallback);
        }
    }

    private void userEarnedRewardCallback(Reward reward)
    {
        // Начисляем награду
    }
}
Реклама с вознаграждением
Реклама с вознаграждением

С вознаграждением

Практически предыдущий вариант, только пользователи должны согласиться на просмотр.

public class RewardAd : MonoBehaviour
{
    private RewardedAd _rewardAd;
    private const string _adId = "ca-app-pub-3940256099942544/5224354917";
    private void OnEnable()
    {
        _rewardAd = new RewardedAd(_adId);
        AdRequest adRequest = new AdRequest.Builder().Build();
        _rewardAd.LoadAd(adRequest);
        _rewardAd.OnUserEarnedReward += HandleUserEarnedReward;
    }
    public void ShowAd()
    {
        if (_rewardAd.IsLoaded())
        {
            _rewardAd.Show();
        }
    }
    private void HandleUserEarnedReward(object sender, Reward args)
    {
        // Начисляем награду
    }
}

Расширенное нативное

Реклама, встраиваемая прямо в игровую сцену. По кусочкам собираем рекламу используя различные Get-методы. Советую посмотреть гайд на YouTube для быстрого понимания процесса встраивания и прочитать API здесь.

Итоговый результат выглядит так:

Нативная реклама
Нативная реклама

Запуск приложения

Реклама, появляющаяся перед запуском приложения. Приложение будет запускаться параллельно, так что этот формат можно использовать вместо экрана загрузки тяжеловесной сцены. Весь код берем с этой страницы.

Итоговый результат выглядит так:

public class AppOpenAdManager
{
    private const string AD_UNIT_ID = "ca-app-pub-3940256099942544/3419835294";

    private static AppOpenAdManager instance;

    private AppOpenAd ad;

    private bool isShowingAd = false;

    public static AppOpenAdManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new AppOpenAdManager();
            }

            return instance;
        }
    }

    private bool IsAdAvailable
    {
        get
        {
            return ad != null;
        }
    }

    public void LoadAd()
    {
        AdRequest request = new AdRequest.Builder().Build();

        // Load an app open ad for portrait orientation
        AppOpenAd.LoadAd(AD_UNIT_ID, ScreenOrientation.Portrait, request, ((appOpenAd, error) =>
        {
            if (error != null)
            {
                // Handle the error.
                Debug.LogFormat("Failed to load the ad. (reason: {0})", error.LoadAdError.GetMessage());
                return;
            }

            // App open ad is loaded.
            ad = appOpenAd;
        }));
    }
    public void ShowAdIfAvailable()
    {
        if (!IsAdAvailable || isShowingAd)
        {
            return;
        }

        ad.OnAdDidDismissFullScreenContent += HandleAdDidDismissFullScreenContent;
        ad.OnAdFailedToPresentFullScreenContent += HandleAdFailedToPresentFullScreenContent;
        ad.OnAdDidPresentFullScreenContent += HandleAdDidPresentFullScreenContent;
        ad.OnAdDidRecordImpression += HandleAdDidRecordImpression;
        ad.OnPaidEvent += HandlePaidEvent;

        ad.Show();
    }

    private void HandleAdDidDismissFullScreenContent(object sender, EventArgs args)
    {
        Debug.Log("Closed app open ad");
        // Set the ad to null to indicate that AppOpenAdManager no longer has another ad to show.
        ad = null;
        isShowingAd = false;
        LoadAd();
    }

    private void HandleAdFailedToPresentFullScreenContent(object sender, AdErrorEventArgs args)
    {
        Debug.LogFormat("Failed to present the ad (reason: {0})", args.AdError.GetMessage());
        // Set the ad to null to indicate that AppOpenAdManager no longer has another ad to show.
        ad = null;
        LoadAd();
    }

    private void HandleAdDidPresentFullScreenContent(object sender, EventArgs args)
    {
        Debug.Log("Displayed app open ad");
        isShowingAd = true;
    }

    private void HandleAdDidRecordImpression(object sender, EventArgs args)
    {
        Debug.Log("Recorded ad impression");
    }

    private void HandlePaidEvent(object sender, AdValueEventArgs args)
    {
        Debug.LogFormat("Received paid event. (currency: {0}, value: {1}",
                args.AdValue.CurrencyCode, args.AdValue.Value);
    }
}
Open App Ad
Open App Ad

Послесловие

В реальности мир не делится на Unity Ads и AdMob, а подходить к выбору сети нужно, исходя из вашей целевой аудитории. Расширить кругозор по поводу существующих вариантов можно, прочитав эту статью. Также обычно никто не использует один сервис, когда существует медиация - автоматический выбор решения, которое заплатит больше в конкретной ситуации. По традиции отсылаю вас на отдельный источник, если заинтересовала данная тема.

Желаю творческих и коммерческих успехов!

Комментарии (2)


  1. mopsicus
    15.01.2022 21:14
    +2

    Это перевод документации оф. сайта?


  1. virtusha
    16.01.2022 02:54

    Во-первых, вы сами вначале сказали, что в нете полно информации, так зачем создавать 100501ю сиатью, которая ничем не отличается от подобных?

    Во-вторых,

    >>Также обычно никто не использует один сервис, когда существует медиация - автоматический выбор решения, которое заплатит больше в конкретной ситуации. По традиции отсылаю вас на отдельный источник, если заинтересовала данная тема.

    Почему бы не дополнить эту статью этой информацией, для полноты картины так сказать?