Advertising - VirtueSky/sunflower GitHub Wiki

Advertising for Unity

(Tested with AppLovin > v5.11.3 and Google AdMob > v8.5.2)

Overview

Assets/sunflower/VirtueSky/Advertising no longer follows the old "single ad client" flow. Advertising can initialize:

  • AppLovin
  • Admob
  • LevelPlay

through AdSetting.MediationLoadMode:

  • Single: load only the mediation selected in CurrentMediation
  • Multiple: load all enabled mediations in parallel (UseAppLovin, UseAdmob, UseLevelPlay)

After successful initialization, Advertising starts the auto-load loop using:

  • AdCheckingInterval: polling interval
  • AdLoadingInterval: minimum time between load calls for the same ad type

Currently supported ad types:

  • AppLovin: Banner, Interstitial, Rewarded, AppOpen
  • AdMob: Banner, Interstitial, Rewarded, RewardedInterstitial, AppOpen, NativeOverlay
  • LevelPlay: Banner, Interstitial, Rewarded

Setup

Open tab Advertising in Magic Panel

Unity_r1v2Ygg9sY

  • Create AdSetting and the required AdUnitVariable assets
  • Choose the mediation mode based on the new logic:
    • MediationLoadMode = Single if you want to use only one network
    • MediationLoadMode = Multiple if you want to preload multiple networks at the same time
  • Enable the relevant flags: UseAppLovin, UseAdmob, UseLevelPlay
  • Install the SDK if the project does not have it yet
  • Add the correct Scripting Define Symbols so the matching mediation code is compiled

Set id for AdUnitVariable

AdVariable

  • Each AdUnitVariable uses androidId / iOSId
  • Some AdMob units provide useTestId to inject a runtime test id automatically
  • SetIdRuntime(...) can still be used if you want to override the id from code

(For Admob, you can get the test Id by going to Context Menu and selecting Get Id Test)

idtest

Attach Advertising to the object in the scene

Adsvertising

Advertising is now responsible for:

  • initializing the SDK according to InitType:
    • InitOnAwake
    • InitOnEnable
    • InitOnStart
    • Manual
  • initializing the correct ad clients from AdSetting
  • starting the auto-load loop for interstitial / rewarded / rewarded interstitial / app open
  • updating isInitAdClientVariable if it is assigned
  • listening to initializeAdEvent for manual initialization
  • listening to showAdDebugerEvent to open the mediation debugger / test suite
  • listening to changePreventDisplayAppOpenEvent to block app open display through AdStatic.IsShowingAd

Runtime behavior

Loading

  • Each mediation calls Init() on its own ad units when SDK initialization starts
  • After the SDK is initialized, the client immediately preloads the supported units
  • Advertising keeps calling auto-load periodically to keep inventory available
  • For banners, Load() may also create the banner instance if needed

Show rules

Show() only runs when the unit passes its runtime checks:

  • running on a mobile platform
  • the ad unit has a valid id
  • AdStatic.IsRemoveAd == false for units that support remove-ads behavior
  • IsReady() == true

AppOpen in AdMob and AppLovin also checks the cooldown from the most recently closed full-screen ad through timeBetweenFullScreenAd.

App open

  • AppLovin and AdMob have AppOpenVariable.autoShow
  • app open only auto-shows from the second resume onward because the client uses statusAppOpenFirstIgnore
  • an active banner is hidden when app open is displayed and shown again after app open is closed
  • LevelPlay does not currently support AppOpen

Script show ads

The example below matches the current flow because Show() returns the same AdUnitVariable for callback chaining:

using TMPro;
using UnityEngine;
using VirtueSky.Ads;

public class AdsManager : MonoBehaviour
{
    public TextMeshProUGUI textNoti;
    public AdUnitVariable banner;
    public AdUnitVariable inter;
    public AdUnitVariable reward;
    public AdUnitVariable rewardInter;

    public void ShowBanner()
    {
        if (!banner.IsReady())
        {
            LogMessage("banner is not ready");
            return;
        }

        banner.Show("main_banner")
            .OnDisplayed(_ => LogMessage("banner displayed"))
            .OnClosed(_ => LogMessage("banner closed"));
    }

    public void HideBanner()
    {
        banner.HideBanner();
        LogMessage("banner hidden");
    }

    public void ShowInter()
    {
        if (!inter.IsReady())
        {
            LogMessage("inter is not ready");
            return;
        }

        inter.Show("game_over")
            .OnDisplayed(_ => LogMessage("inter displayed"))
            .OnCompleted(() => LogMessage("inter completed"))
            .OnClosed(_ => LogMessage("inter closed"))
            .OnFailedToDisplay(error => LogMessage($"inter failed: {error.ErrorMessage}"));
    }

    public void ShowReward()
    {
        if (!reward.IsReady())
        {
            LogMessage("reward is not ready");
            return;
        }

        reward.Show("revive")
            .OnDisplayed(_ => LogMessage("reward displayed"))
            .OnReceivedReward(() => LogMessage("reward granted"))
            .OnCompleted(() => LogMessage("reward completed"))
            .OnSkipped(() => LogMessage("reward skipped"))
            .OnClosed(_ => LogMessage("reward closed"));
    }

    public void ShowRewardInter()
    {
        if (rewardInter == null || !rewardInter.IsReady())
        {
            LogMessage("reward inter is not ready");
            return;
        }

        rewardInter.Show("bonus")
            .OnReceivedReward(() => LogMessage("reward inter granted"))
            .OnCompleted(() => LogMessage("reward inter completed"))
            .OnSkipped(() => LogMessage("reward inter skipped"));
    }

    void LogMessage(string message)
    {
        textNoti.text = message;
        Debug.Log(message);
    }
}

Callback notes

Available callback extensions:

  • OnLoaded(Action<AdsInfo>)
  • OnFailedToLoad(Action<AdsError>)
  • OnDisplayed(Action<AdsInfo>)
  • OnFailedToDisplay(Action<AdsError>)
  • OnClosed(Action<AdsInfo>)
  • OnClicked(Action<AdsInfo>)
  • OnCompleted(Action)
  • OnSkipped(Action)
  • OnReceivedReward(Action)

Notes:

  • OnCompleted() for interstitial usually runs when the ad is closed
  • OnCompleted() for rewarded / rewarded interstitial only runs after the reward is granted
  • OnSkipped() is only meaningful for rewarded flows
  • besides the callback chain, each unit also exposes event fields such as OnLoadAdEvent, OnDisplayedAdEvent, OnClosedAdEvent
  • on non-mobile platforms, OnCompleted() is invoked immediately

GDPR note

If Enable GDPR

  • Android: if GDPR is enabled and the build includes AdMob UMP, Advertising only initializes ad clients after the consent flow finishes
  • iOS: the GDPR flow only runs when ATT status is AUTHORIZED; otherwise GDPR is skipped and ads are initialized directly
  • if consent update or consent form fails, the current code still falls back to InitAdClient()
  • when using GDPR, make sure you have created the GDPR message in Admob

Debug note

  • showAdDebugerEvent:
    • AppLovin -> opens Mediation Debugger
    • Admob -> opens Ad Inspector
    • LevelPlay -> opens Test Suite
  • You can use the Use: ...AdClient log to confirm which mediation was initialized at runtime
⚠️ **GitHub.com Fallback** ⚠️