LevelPlay Backfill Example
This flow uses the 2.x line of the AscendX Unity SDK, which is independent from the 1.x line documented in Unity SDK Integration — switch to v1.x in the Versions menu. Do not mix the two in one project. See the Unity SDK 2.x release notes.
A minimal guide for apps that already monetize with LevelPlay and want to add AscendX as extra demand.
LevelPlay stays your primary source. An AscendX ad is loaded in parallel and shown whenever the mediation ad cannot be — so you fill ad breaks you would otherwise skip. Nothing about your existing LevelPlay setup has to change.
1. Requirements
- Min Android SDK version: 24
- Min iOS version: 13
- A LevelPlay integration already up and running
- Formats covered in this guide: Interstitial, Rewarded
2. Install the SDK
2.1. Install via UPM
-
Add the scoped registry and the dependency to your
Packages/manifest.json:{"scopedRegistries": [{"name": "AscendX Unity","url": "https://asia-southeast1-npm.pkg.dev/ascendx-463208/asx-upm-prod/","scopes": ["com.ascendxnow"]}],"dependencies": {"com.ascendxnow.unity-sdk": "2.1.0"}} -
Back in Unity, the package is resolved automatically. Verify it under Window → Package Manager → My Registries → AscendX Unity.
-
To update later, change the
com.ascendxnow.unity-sdkversion inPackages/manifest.jsonto a newer 2.x release, or open Window → Package Manager, click "Refresh", findAscendX Unity SDKand click Update.
2.2. External Dependency Manager (Play Services Resolver)
Pulls in the native Android / iOS dependencies of the SDK.
Android
- Enable
Custom Main Gradle Template(Build Settings → Player Settings → Publishing settings). - Enable
Patch mainTemplate.gradleandUse Jetifier(Assets → External Dependency Manager → Android Resolver → Settings). - Run
Assets → External Dependency Manager → Android Resolver → Resolve(or Force Resolve).
iOS
- Run
Assets → External Dependency Manager → iOS Resolver → Install Cocoapods. - Enable
Allow the same pod to be in multiple targets(Assets → External Dependency Manager → iOS Resolver → Settings).
3. LevelPlay integration
Keep loading and showing your LevelPlay ad exactly as you do today. The AscendX ad keeps itself loaded — it fetches demand once the SDK is initialized and again whenever its ad is used up — so one is always standing by. AscendX then steps in in exactly two situations.
Retry backoff. When an auction returns no bid, OnRequestFailedEvent waits before fetching demand again instead of retrying immediately. The wait doubles after each consecutive failure and is capped at 32 seconds; the first successful auction resets it back to the start.
| Consecutive failures | 1 | 2 | 3 | 4 | 5 and beyond |
|---|---|---|---|---|---|
| Wait before the next fetch | 2s | 4s | 8s | 16s | 32s |
3.1. Scenario 1: Mediation is not ready
At the ad break, LevelPlay has no ad to show. Instead of skipping the break, show the AscendX ad.
void ShowInterstitialAd()
{
if (interstitialAd.IsAdReady()) {
interstitialAd.ShowAd();
} else if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
3.2. Scenario 2: Mediation fails to display
LevelPlay had an ad and you called ShowAd(), but it failed to appear. OnAdDisplayFailed fires and the user sees nothing — show the AscendX ad to recover the impression.
interstitialAd.OnAdDisplayFailed += InterstitialOnAdDisplayFailedEvent;
void InterstitialOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError error)
{
if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
4. Full implementation example
The LevelPlay sample classes with the AscendX code added. Everything marked AscendX is new; the LevelPlay parts are unchanged. Call Initialize() on each sample at app launch.
4.1. Interstitial Ads
public class InterstitialAdSample : MonoBehaviour
{
private LevelPlayInterstitialAd interstitialAd;
private const string AscendXInterstitialAdUnitId = "YOUR_ASCENDX_INTERSTITIAL_AD_UNIT_ID";
private int retryAttempt;
void InitializeAsxSDK() {
AsxSdkCallbacks.OnSdkInitializedEvent += AscendXOnSdkInitializedEvent;
AsxSdkCallbacks.OnRequestSuccessEvent += AscendXOnRequestSuccessEvent;
AsxSdkCallbacks.OnRequestFailedEvent += AscendXOnRequestFailedEvent;
AsxSdk.SetAccountId("«ACCOUNT_ID»");
AsxSdk.InitializeSdk();
}
void CreateInterstitialAd() {
// your current ad unit initialization logic go here
interstitialAd = new LevelPlayInterstitialAd("YOUR_INTERSTITIAL_AD_UNIT_ID");
interstitialAd.OnAdLoaded += (adInfo) => { };
interstitialAd.OnAdLoadFailed += (error) => { };
interstitialAd.OnAdDisplayed += (adInfo) => { };
interstitialAd.OnAdClicked += (adInfo) => { };
interstitialAd.OnAdInfoChanged += (adInfo) => { };
interstitialAd.OnAdClosed += InterstitialOnAdClosedEvent;
interstitialAd.OnAdDisplayFailed += InterstitialOnAdDisplayFailedEvent;
}
void LoadInterstitialAd() {
interstitialAd.LoadAd();
}
void ShowInterstitialAd() {
if (interstitialAd.IsAdReady()) {
interstitialAd.ShowAd();
}
else if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
}
else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void InterstitialOnAdClosedEvent(LevelPlayAdInfo adInfo) {
interstitialAd.LoadAd();
}
void InterstitialOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError error) {
if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void AscendXOnSdkInitializedEvent() {
AsxSdk.CreateInterstitial(AscendXInterstitialAdUnitId);
AsxSdkCallbacks.Interstitial.OnAdLoadedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += (errorInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdDisplayedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdClickedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdImpressionEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdClosedEvent += AscendXOnAdClosedEvent;
AsxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += AscendXOnAdDisplayFailedEvent;
AsxSdkCallbacks.Interstitial.OnAdExpiredEvent += AscendXOnAdExpiredEvent;
// fetch your first bid response
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
void AscendXOnRequestSuccessEvent(string adUnitId, AsxSdkBase.AuctionResult auctionResult) {
if (adUnitId == AscendXInterstitialAdUnitId) {
// a bid came back - reset the retry backoff
retryAttempt = 0;
// pre-cache the ads
AsxSdk.FetchInterstitialAd(adUnitId);
}
}
void AscendXOnRequestFailedEvent(string adUnitId, AsxSdkBase.ErrorInfo errorInfo) {
if (adUnitId != AscendXInterstitialAdUnitId) {
return;
}
// no bid this time - retry with exponentially higher delays, up to 32 seconds
retryAttempt++;
float retryDelay = (float) Math.Pow(2, Math.Min(5, retryAttempt));
Invoke(nameof(FetchAscendXDemand), retryDelay);
}
void FetchAscendXDemand() {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
void AscendXOnAdClosedEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
void AscendXOnAdDisplayFailedEvent(AsxSdkBase.ErrorInfo errorInfo) {
if (errorInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
void AscendXOnAdExpiredEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
}
4.2. Rewarded Ads
Same structure. The only addition is the reward callback — grant the reward from whichever SDK actually showed the ad.
public class RewardedAdSample : MonoBehaviour
{
private LevelPlayRewardedAd rewardedAd;
private const string AscendXRewardedAdUnitId = "YOUR_ASCENDX_REWARDED_AD_UNIT_ID";
private int retryAttempt;
void InitializeAsxSDK() {
AsxSdkCallbacks.OnSdkInitializedEvent += AscendXOnSdkInitializedEvent;
AsxSdkCallbacks.OnRequestSuccessEvent += AscendXOnRequestSuccessEvent;
AsxSdkCallbacks.OnRequestFailedEvent += AscendXOnRequestFailedEvent;
AsxSdk.SetAccountId("«ACCOUNT_ID»");
AsxSdk.InitializeSdk();
}
void CreateRewardedAd() {
// your current ad unit initialization logic go here
rewardedAd = new LevelPlayRewardedAd("YOUR_REWARDED_AD_UNIT_ID");
rewardedAd.OnAdLoaded += (adInfo) => { };
rewardedAd.OnAdLoadFailed += (error) => { };
rewardedAd.OnAdDisplayed += (adInfo) => { };
rewardedAd.OnAdClicked += (adInfo) => { };
rewardedAd.OnAdInfoChanged += (adInfo) => { };
rewardedAd.OnAdRewarded += (adInfo, reward) => { };
rewardedAd.OnAdClosed += RewardedOnAdClosedEvent;
rewardedAd.OnAdDisplayFailed += RewardedOnAdDisplayFailedEvent;
}
void LoadRewardedAd() {
rewardedAd.LoadAd();
}
void ShowRewardedAd() {
if (rewardedAd.IsAdReady()) {
rewardedAd.ShowAd();
}
else if (AsxSdk.IsRewardedAdReady(AscendXRewardedAdUnitId)) {
AsxSdk.ShowRewardedAd(AscendXRewardedAdUnitId);
}
else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void RewardedOnAdClosedEvent(LevelPlayAdInfo adInfo) {
rewardedAd.LoadAd();
}
void RewardedOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError error) {
if (AsxSdk.IsRewardedAdReady(AscendXRewardedAdUnitId)) {
AsxSdk.ShowRewardedAd(AscendXRewardedAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void AscendXOnSdkInitializedEvent() {
AsxSdk.CreateRewardedAd(AscendXRewardedAdUnitId);
AsxSdkCallbacks.Rewarded.OnAdLoadedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += (errorInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdDisplayedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdClickedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdImpressionEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += (adInfo, reward) => { };
AsxSdkCallbacks.Rewarded.OnAdClosedEvent += AscendXOnAdClosedEvent;
AsxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += AscendXOnAdDisplayFailedEvent;
AsxSdkCallbacks.Rewarded.OnAdExpiredEvent += AscendXOnAdExpiredEvent;
// fetch your first bid response
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
void AscendXOnRequestSuccessEvent(string adUnitId, AsxSdkBase.AuctionResult auctionResult) {
if (adUnitId == AscendXRewardedAdUnitId) {
// a bid came back - reset the retry backoff
retryAttempt = 0;
// pre-cache the ads
AsxSdk.FetchRewardedAd(adUnitId);
}
}
void AscendXOnRequestFailedEvent(string adUnitId, AsxSdkBase.ErrorInfo errorInfo) {
if (adUnitId != AscendXRewardedAdUnitId) {
return;
}
// no bid this time - retry with exponentially higher delays, up to 32 seconds
retryAttempt++;
float retryDelay = (float) Math.Pow(2, Math.Min(5, retryAttempt));
Invoke(nameof(FetchAscendXDemand), retryDelay);
}
void FetchAscendXDemand() {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
void AscendXOnAdClosedEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
void AscendXOnAdDisplayFailedEvent(AsxSdkBase.ErrorInfo errorInfo) {
if (errorInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
void AscendXOnAdExpiredEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
}
4.3. Interstitial and Rewarded Ads
Both formats handled by one class. The AscendX SDK is initialized once, the three global callbacks are registered once and routed by adUnitId, and each format keeps its own retry counter.
public class AdsSample : MonoBehaviour
{
private LevelPlayInterstitialAd interstitialAd;
private LevelPlayRewardedAd rewardedAd;
private const string AscendXInterstitialAdUnitId = "YOUR_ASCENDX_INTERSTITIAL_AD_UNIT_ID";
private const string AscendXRewardedAdUnitId = "YOUR_ASCENDX_REWARDED_AD_UNIT_ID";
private int interstitialRetryAttempt;
private int rewardedRetryAttempt;
void InitializeAsxSDK() {
AsxSdkCallbacks.OnSdkInitializedEvent += AscendXOnSdkInitializedEvent;
AsxSdkCallbacks.OnRequestSuccessEvent += AscendXOnRequestSuccessEvent;
AsxSdkCallbacks.OnRequestFailedEvent += AscendXOnRequestFailedEvent;
AsxSdk.SetAccountId("«ACCOUNT_ID»");
AsxSdk.InitializeSdk();
}
void CreateInterstitialAd() {
// your current ad unit initialization logic go here
interstitialAd = new LevelPlayInterstitialAd("YOUR_INTERSTITIAL_AD_UNIT_ID");
interstitialAd.OnAdLoaded += (adInfo) => { };
interstitialAd.OnAdLoadFailed += (error) => { };
interstitialAd.OnAdDisplayed += (adInfo) => { };
interstitialAd.OnAdClicked += (adInfo) => { };
interstitialAd.OnAdInfoChanged += (adInfo) => { };
interstitialAd.OnAdClosed += InterstitialOnAdClosedEvent;
interstitialAd.OnAdDisplayFailed += InterstitialOnAdDisplayFailedEvent;
}
void CreateRewardedAd() {
// your current ad unit initialization logic go here
rewardedAd = new LevelPlayRewardedAd("YOUR_REWARDED_AD_UNIT_ID");
rewardedAd.OnAdLoaded += (adInfo) => { };
rewardedAd.OnAdLoadFailed += (error) => { };
rewardedAd.OnAdDisplayed += (adInfo) => { };
rewardedAd.OnAdClicked += (adInfo) => { };
rewardedAd.OnAdInfoChanged += (adInfo) => { };
rewardedAd.OnAdRewarded += (adInfo, reward) => { };
rewardedAd.OnAdClosed += RewardedOnAdClosedEvent;
rewardedAd.OnAdDisplayFailed += RewardedOnAdDisplayFailedEvent;
}
void LoadInterstitialAd() {
interstitialAd.LoadAd();
}
void LoadRewardedAd() {
rewardedAd.LoadAd();
}
void ShowInterstitialAd() {
if (interstitialAd.IsAdReady()) {
interstitialAd.ShowAd();
}
else if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
}
else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void ShowRewardedAd() {
if (rewardedAd.IsAdReady()) {
rewardedAd.ShowAd();
}
else if (AsxSdk.IsRewardedAdReady(AscendXRewardedAdUnitId)) {
AsxSdk.ShowRewardedAd(AscendXRewardedAdUnitId);
}
else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void InterstitialOnAdClosedEvent(LevelPlayAdInfo adInfo) {
interstitialAd.LoadAd();
}
void InterstitialOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError error) {
if (AsxSdk.IsInterstitialReady(AscendXInterstitialAdUnitId)) {
AsxSdk.ShowInterstitial(AscendXInterstitialAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void RewardedOnAdClosedEvent(LevelPlayAdInfo adInfo) {
rewardedAd.LoadAd();
}
void RewardedOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError error) {
if (AsxSdk.IsRewardedAdReady(AscendXRewardedAdUnitId)) {
AsxSdk.ShowRewardedAd(AscendXRewardedAdUnitId);
} else {
// Existing "no ad" path - unchanged (skip / continue / retry)
}
}
void AscendXOnSdkInitializedEvent() {
AsxSdk.CreateInterstitial(AscendXInterstitialAdUnitId);
AsxSdkCallbacks.Interstitial.OnAdLoadedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += (errorInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdDisplayedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdClickedEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdImpressionEvent += (adInfo) => { };
AsxSdkCallbacks.Interstitial.OnAdClosedEvent += AscendXInterstitialOnAdClosedEvent;
AsxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += AscendXInterstitialOnAdDisplayFailedEvent;
AsxSdkCallbacks.Interstitial.OnAdExpiredEvent += AscendXInterstitialOnAdExpiredEvent;
AsxSdk.CreateRewardedAd(AscendXRewardedAdUnitId);
AsxSdkCallbacks.Rewarded.OnAdLoadedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += (errorInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdDisplayedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdClickedEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdImpressionEvent += (adInfo) => { };
AsxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += (adInfo, reward) => { };
AsxSdkCallbacks.Rewarded.OnAdClosedEvent += AscendXRewardedOnAdClosedEvent;
AsxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += AscendXRewardedOnAdDisplayFailedEvent;
AsxSdkCallbacks.Rewarded.OnAdExpiredEvent += AscendXRewardedOnAdExpiredEvent;
// fetch your first bid response
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
void AscendXOnRequestSuccessEvent(string adUnitId, AsxSdkBase.AuctionResult auctionResult) {
if (adUnitId == AscendXInterstitialAdUnitId) {
// a bid came back - reset the retry backoff
interstitialRetryAttempt = 0;
// pre-cache the ads
AsxSdk.FetchInterstitialAd(adUnitId);
}
else if (adUnitId == AscendXRewardedAdUnitId) {
rewardedRetryAttempt = 0;
AsxSdk.FetchRewardedAd(adUnitId);
}
}
void AscendXOnRequestFailedEvent(string adUnitId, AsxSdkBase.ErrorInfo errorInfo) {
// no bid this time - retry with exponentially higher delays, up to 32 seconds
if (adUnitId == AscendXInterstitialAdUnitId) {
interstitialRetryAttempt++;
Invoke(nameof(FetchAscendXInterstitialDemand), (float) Math.Pow(2, Math.Min(5, interstitialRetryAttempt)));
}
else if (adUnitId == AscendXRewardedAdUnitId) {
rewardedRetryAttempt++;
Invoke(nameof(FetchAscendXRewardedDemand), (float) Math.Pow(2, Math.Min(5, rewardedRetryAttempt)));
}
}
void FetchAscendXInterstitialDemand() {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
void FetchAscendXRewardedDemand() {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
void AscendXInterstitialOnAdClosedEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
void AscendXInterstitialOnAdDisplayFailedEvent(AsxSdkBase.ErrorInfo errorInfo) {
if (errorInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
void AscendXInterstitialOnAdExpiredEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXInterstitialAdUnitId) {
AsxSdk.FetchInterstitialDemand(AscendXInterstitialAdUnitId);
}
}
void AscendXRewardedOnAdClosedEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
void AscendXRewardedOnAdDisplayFailedEvent(AsxSdkBase.ErrorInfo errorInfo) {
if (errorInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
void AscendXRewardedOnAdExpiredEvent(AsxSdkBase.AdInfo adInfo) {
if (adInfo.AdUnitId == AscendXRewardedAdUnitId) {
AsxSdk.FetchRewardedDemand(AscendXRewardedAdUnitId);
}
}
}
5. SDK specification
- Full integration guide, including Banner / MREC, floor pricing and win/loss notifications: Unity SDK Integration
- Release notes: Unity SDK 2.x
6. Demo app
A runnable Unity project with both samples wired up is available from the AscendX team.