Advertising - VirtueSky/sunflower GitHub Wiki
(Tested with AppLovin > v5.11.3 and Google AdMob > v8.5.2)
Assets/sunflower/VirtueSky/Advertising no longer follows the old "single ad client" flow. Advertising can initialize:
AppLovinAdmobLevelPlay
through AdSetting.MediationLoadMode:
-
Single: load only the mediation selected inCurrentMediation -
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

- Create
AdSettingand the requiredAdUnitVariableassets - Choose the mediation mode based on the new logic:
-
MediationLoadMode = Singleif you want to use only one network -
MediationLoadMode = Multipleif 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 Symbolsso the matching mediation code is compiled

- Each
AdUnitVariableusesandroidId/iOSId - Some AdMob units provide
useTestIdto 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)


Advertising is now responsible for:
- initializing the SDK according to
InitType:InitOnAwakeInitOnEnableInitOnStartManual
- initializing the correct ad clients from
AdSetting - starting the auto-load loop for interstitial / rewarded / rewarded interstitial / app open
- updating
isInitAdClientVariableif it is assigned - listening to
initializeAdEventfor manual initialization - listening to
showAdDebugerEventto open the mediation debugger / test suite - listening to
changePreventDisplayAppOpenEventto block app open display throughAdStatic.IsShowingAd
- 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
-
Advertisingkeeps calling auto-load periodically to keep inventory available - For banners,
Load()may also create the banner instance if needed
Show() only runs when the unit passes its runtime checks:
- running on a mobile platform
- the ad unit has a valid id
-
AdStatic.IsRemoveAd == falsefor 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.
-
AppLovinandAdMobhaveAppOpenVariable.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
-
LevelPlaydoes not currently supportAppOpen
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);
}
}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
- Android: if GDPR is enabled and the build includes AdMob UMP,
Advertisingonly 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
-
showAdDebugerEvent:-
AppLovin-> opens Mediation Debugger -
Admob-> opens Ad Inspector -
LevelPlay-> opens Test Suite
-
- You can use the
Use: ...AdClientlog to confirm which mediation was initialized at runtime