Unity SDK

Overview

The Metica Unity SDK offers a straightforward interface for interacting with the Metica backend API from your Unity game directly. The SDK provides simplified methods to fetch offers and smart configs (abbreviated as configs), and to log user actions within your app or game as events. The Metica Unity SDK takes care of networking nuance, caching and much more so you don't have to.

Terminology

  • Application Your application or game that uses the Metica services.

  • User A user or player of the application. Player and user are used interchangeably.

  • Event An event refers to a user's action within the context of the application. For example, clicking a button, logging in, clicking an ad, etc. The properties (or attributes) associated with these events vary based on the type of the event; e.g., the property totalAmount must be set when logging an in-app offer purchase event.

  • User Properties Sometimes (and formerly) called User Attributes, user properties consist of general information about the user that best characterises them in your application. For example you could define your user properties to include game progression, demographics, user preferences, player's level, etc.

What you need

To use the Metica Unity SDK you need:

  • An API key, obtainable in the Metica platform

  • An appId, obtainable in the Metica platform.

Installation

In Unity, open the Package Manager (Window > Package Manager) and click on the '+' button in the top left corner. Select "Add package from git URL..." and enter the following:

https://github.com/meticalabs/metica-unity-sdk.git

How to use the Metica SDK

The recommended way to use the SDK is through MeticaSdk which offers async/await methods alongside coroutine ones.

For backwards compatibility the old deprecated mechanism is still available which has only the coroutine approach: MeticaAPI

You can retrieve an instance of IMeticaSdk with

private IMeticaSdk _sdk = MeticaSdk.SDK;

If you do so, you can now take advantage of the asynchronous calls. Example:

configResult = await _sdk.GetConfigsAsync(new List<string> { "dynamic_difficulty" });

Setup

  1. In Unity's Hierarchy View, right click (on an empty area) and select Metica > Add SDK. This will add a prefab with the MeticaUnitySdk component attached (if not already in scene). Alternatively you can manually drag and drop the prefab from Packages/Metica Unity Sdk/Runtime/Unity/Prefabs/.

  2. Select the prefab and click the Create Configuration button to create and save it in the folder you select.*

  3. Select the configuration file and fill the fields (see SDK Configuration)

  4. If needed, add the file to the MeticaSdk prefab. When you use the Create Configuration button the configuration should be automatically linked.

*: Alternatively, create a configuration asset by right-clicking a folder in the Project View and selecting Create > Metica > SDK > New SDK Configuration. This can also be found in the main menu under Assets > Create > Metica but it will create the asset in the Assets' root.

SDK Configuration

The SDK configuration exposes the following parameters:

Property
Description

apiKey

Your API key

appId

The application identifier accessible from Metica's dashboard.

initialUserId

A string that identifies a user. This can change during the lifetime of your app/game so, for example and depending on your needs, this could be a temporary id like "guest" that later becomes a specific userId, or it can be the current user's id if it's already identified.

Base Endpoint

Base metica endpoint : https://api-gateway.prod-eu.metica.com

Events Log Dispatch Cadence

The cadence, in seconds, that triggers an events dispatch.

displayLogFlushCadence

The number of stored events above which they are dispecthed (actually sent to Metica). Events in fact don't get always sent immediately, they accumulate and get sent in bulks.

Events Log Dispatch Cadence

The cadence, in seconds, by which the logged events will be sent to the ingestion service.

Events Log Dispatch Max Queue Size

The maximum number of pending logged events before they are sent to the ingestion service. When this value is reached, oldest accumulated events will be dropped to accommodate most recent ones.

Http Cache TTL Seconds

The time-to-live, in seconds, for the http-level cache.

Http Request Timeout

The network timeout, in seconds, for the calls to any Metica endpoint.

Log Level

The level of the SDK's logs. Do not confuse this with the meaning Log in the context of event logging. The valid values are provided by the enumeration Metica.SDK.LogLevel and determine the verbosity of the logs with Info being the most verbose and Debug being the least verbose. Offsuppresses all logging.

Offers

Asynchronously fetches offers for specified (or all) placements from the Metica API. The result consists of a dictionary of placements with their respective offers.

A dictionary of user attributes can be passed to the method to personalize the offers. If not, then the last known user attributes are used.

Also, a DeviceInfo object (see Device Info) can be passed to the method to provide device information. If not, then the device information is automatically collected.

Signature

Task<OfferResult> GetOffersAsync(string[] placements, Dictionary<string, object> userData = null, DeviceInfo deviceInfo = null);

Example

var offers = await _sdk.GetOffersAsync(
    configKeys: new string[] { "placement1", "placement2" },
    userProperties: {
        { "someAttribute", true },
        { "another", "someValue" }
    }, 
    deviceInfo: new DeviceInfo()
        {
            appVersion = "1.0.0",
            timezone = "UTC",
            locale = "en_US",
            store = "AppStore"
        });
);

Log.Debug(() => $"Offers: {offersResult}");

Smart Configs

The GetConfig method can be used to retrieve the Smart Configs.

Similar to the GetOffers method, the operation is performed asynchronously. Because the result is specific to each application, the SDK represents it in a generic manner, through an Dictionary<string, object> instance. Each entry in the dictionary represents a configuration key and its value. The latter is expected to be valid json.

Also, again similar to the GetOffers method, the operation can be passed a dictionary of user properties and the device details, as a DeviceInfo instance (see Device Info), in order to personalise the returned configuration. The personalisation happens on the server side, based on the configured variations and experimentation setup.

Signature

Task<ConfigResult> GetConfigsAsync(List<string> configKeys = null, Dictionary<string, object> userData = null, DeviceInfo deviceInfo = null);

Example

var configResult = await _sdk.GetConfig(
    configKeys: new string[] { "key1", "key2" },
    responseCallback: result => { 
        if (result.Error == null) 
        {             
            Debug.Log(result.Result.Configs["key1"]);             
        } else 
        { 
            Debug.Log("Failed to get offers: " + result.Error); 
        } 
    },
    userProperties: {
        { "someAttribute", true },
        { "another", "someValue" }
    }, 
    deviceInfo: new DeviceInfo()
        {
            appVersion = "1.0.0",
            timezone = "UTC",
            locale = "en_US",
            store = "AppStore"
        });
        
Log.Debug(() => $"Configs: {configResult}");

If the configKeys argument is null or empty, then all the configured keys will be returned.

Note that the server side response for this call is going to be cached according to the cache control directives returned by the server.

Device Info

The DeviceInfo stores information regarding the host device, and it can differ across different devices of the same user.

Note that leaving this variable to null is perfectly fine as it will trigger a device detector that is internal to the SDK

An overview of the role of each DeviceInfo property:

Property
Type
Description
Example

store

string

Identifies the app store related to the in-game offers. Possible values: - GooglePlayStore, the Google store - AppStore, the Apple store

GooglePlayStore

timezone

string

Device timezone expressed with IANA tz identifier format

+01:00

appVersion

string

The game/app version, in SemVer format

0.1.5

locale

string

Locale expressed as a combination of language (ISO 639) and country (ISO 3166) JDK 8 standard reference.

en-US

Events Ingestion

This section explains how to ingest events from the game into Metica. Here is a list of methods that deliver these events to Metica, and following their signatures should be sufficient for an SDK developer. Additional details about them can also be found here.

Offer Lifecycle Events

Logs offer related events like offer display, offer purchase, and offer interaction.

All these events have two signatures: one with and one without productId. If the offer linked to the event was generated by Metica, the method without ProductId should be used. Conversely, if the offer is a game offer created internally within the game, the method with ProductId must be used, passing the relevant productId (any internal string representing it) in the method.

Examples:

void LogOfferImpressionEvent(string placementId, string offerId, Dictionary<string, object> customPayload = null);
void LogOfferImpressionEventWithProductId(string productId, Dictionary<string, object> customPayload = null);
void LogOfferInteractionEvent(string placementId, string offerId, string interactionType, Dictionary<string, object> customPayload = null);
void LogOfferInteractionEventWithProductId(string productId, string interactionType, Dictionary<string, object> customPayload = null);
void LogOfferPurchaseEvent(string placementId, string offerId, string currencyCode, double totalAmount, Dictionary<string, object> customPayload = null);
void LogOfferPurchaseEventWithProductId(string productId, string currencyCode, double totalAmount, Dictionary<string, object> customPayload = null);

Full State Update

LogFullStateUpdate sends a complete snapshot of the user's state to the server, replacing any previously stored data. Please note that this method fully resets the user's state on the server and expects all relevant state information to be included in the request. Any user attributes that are currently stored in the server with the given userId but are not sent with this update, will be erased.

Dictionary<string, object> userAttributes = new Dictionary<string, object> { { "level", 25 }, { "favoriteItem", "shield" } };
_sdk.LogFullStateUserUpdateEvent(userAttributes); 

Partial State Update

LogPartialStateUpdate sends a partial update of the user's state to the server, modifying or adding only the provided fields while preserving those that are currently stored on the server. This method cannot erase existing fields (like LogFullStateUpdate does); it can only overwrite values or introduce new ones.

Dictionary<string, object> userAttributes = new Dictionary<string, object> { { "level", 26 } };
_sdk.LogPartialStateUserUpdateEvent(userAttributes)

Custom Event Logging

LogCustomEvent logs custom application events. The only required field in this dictionary is eventType , which Metica uses to classify the different types of events your application sends.

Dictionary<string, object> customUserEvent = new Dictionary<string, object> { { "eventType", "completed_level" }, { "eventDetails", "level 5" } };
_sdk.LogCustomEvent(userEvent);

Note: The final event that is submitted to the Metica backend is enriched with additional information that's gathered by the SDK. See Base Fields for further information. In a scenario where you weren't using Metica SDK you'd have to code your game/app so these fields are always included.

// this will avoid the extra allocation but will mutate the passed userEvent
_sdk.LogCustomEvent(customEventType, true);

Custom Payloads

All events, except fullStateUpdate, partialStateUpdate and CustomEvent, support a custom payload that can include any information. It's passed as a Dictionary<string, object> to the logging methods. To avoid confusion with other fields, it is recommended to pass the parameter by name, i.g. LogInstall(customPayload: playerExtraParams) rather than just LogInstall(playerExtraParams).

Example

Dictionary<string, object> myCustomPayload {
    { "set", "Intense Stare Magic Mastery" },
    { "cosmetic", "flamingo shades" },
};
_sdk.LogOfferImpressionEvent(somePlacementId, someOfferId, myCustomPayload);
_sdk.LogOfferPurchaseEvent(somePlacementId, someOfferId, "USD", 2.49, customPayload: myCustomPayload);

Event Dispatch (Flushing)

When you log an event (both with static calling or using async), it isn't guaranteed to be sent immediately as events are sent in bulks.

If you need to make sure events are immediately sent to the ingestion endpoint, you can use the RequestDispatchEvents call but don't overuse it as the default behaviour helps aggregating events for lower network load.

Ads Integration

This section explains how to integrate Metica's interstitial ads into your Unity project. Metica's streamlined API simplifies ad integration by automatically handling ad unit IDs and optimizing ad selection.

This guide assumes that the AppLovin SDK has already been configured. Otherwise please follow this guide. MeticaAds requires AppLovin SDK version ≥ 13.

Initialization

The IsMeticaAdsEnabled flag is a boolean variable that determines whether a specific user will receive ads optimized by Metica's AI or will be part of a holdout group receiving ads directly through the standard AppLovin MAX SDK. This flag is set during the SDK initialization and is used to A/B test the revenue performance of Metica's ad optimization against the baseline performance of the MAX SDK.

Important: MeticaAds.InitializeAsync() must be called BEFORE initializing the AppLovin MAX SDK to ensure proper integration.

/// <summary>
/// Complete initialization flow for MeticaAds and MAX SDK integration
/// This method demonstrates the proper order and setup required for ad functionality
/// Call this method early in your app lifecycle, typically in Start() or Awake()
/// </summary>
public async void InitializeAds()
{
    // Step 1: Set User Identification (Critical Requirement)
    // Important: UserId must be set for the current user before any MeticaAds initialization
    // This unique identifier is used for:
    // - User analytics and behavior tracking
    // - A/B testing and holdout group management
    // - Revenue attribution and reporting
    // - Personalized ad targeting and optimization
    MeticaSdk.CurrentUserId = "your_unique_user_id";

    // Step 2: Configure MeticaAds initialization
    // Create the configuration object that will be passed to the MeticaAds SDK
    var meticaConfiguration = new MeticaConfiguration();
    
    // Step 3: Initialize MeticaAds SDK (Asynchronous Operation)
    // This call performs the core MeticaAds setup including:
    // - SDK authentication and configuration validation
    // - Network connectivity and endpoint verification  
    // - Platform-specific delegate initialization
    // - Ad callback registration and event system setup
    // Returns true if Metica should be used, false otherwise
    IsMeticaAdsEnabled = await MeticaAds.InitializeAsync(meticaConfiguration);

    // Step 4: Initialize AppLovin MAX SDK (Traditional Synchronous Setup)
    // Set the SDK key that identifies your app in the AppLovin dashboard
    // This key is essential for ad serving, reporting, and revenue tracking
    MaxSdk.SetSdkKey("YOUR_MAX_SDK_KEY"); // Replace with your actual MAX SDK key
    
    // Set MAX initialization callback
    MaxSdkCallbacks.OnSdkInitializedEvent += sdkConfiguration =>
    {
        // AppLovin SDK is initialized, now start loading ads
        Debug.Log("MAX SDK Initialized");

        InitializeInterstitialAds();
        InitializeRewardedAds();
    };
    
    // Initialize the MAX SDK - this sets up the underlying ad infrastructure
    // Must be called after MeticaAds initialization to ensure proper integration
    MaxSdk.InitializeSdk();
}

MeticaAdExtensions

The MeticaAdExtensions class provides extension methods to convert MeticaAd objects to MaxSdkBase.AdInfo objects, enabling seamless integration between MeticaAds callbacks and existing MAX SDK callback signatures. This allows you to use the same callback methods for both MeticaAds and standard MAX SDK implementations.

Purpose: Converts MeticaAd objects to MaxSdkBase.AdInfo format so existing MAX SDK callback handlers can work with MeticaAds without modification.

Usage: The extension method ToAdInfo() is automatically available on MeticaAd objects and is used in callback registrations as shown in the interstitial ads example.

// MeticaAdExtensions.cs
using System.Collections.Generic;

namespace Metica.ADS
{
    public static class MeticaAdExtensions
    {
        /// <summary>
        /// Converts a MeticaAd object to MaxSdkBase.AdInfo format for compatibility with MAX SDK callbacks
        /// </summary>
        /// <param name="meticaAd">The MeticaAd object to convert</param>
        /// <returns>MaxSdkBase.AdInfo object with mapped values</returns>
        public static MaxSdkBase.AdInfo ToAdInfo(this MeticaAd meticaAd)
        {
            // Create a dictionary with available MeticaAd values
            var adInfoDictionary = new Dictionary<string, object>
            {
                ["adUnitId"] = meticaAd.adUnitId ?? string.Empty,
                ["adFormat"] = meticaAd.adFormat ?? string.Empty,
                ["networkName"] = meticaAd.networkName ?? string.Empty,
                ["placement"] = meticaAd.placementTag ?? string.Empty,
                ["revenue"] = meticaAd.revenue,
                    
                // Set defaults for unavailable fields
                ["networkPlacement"] = string.Empty,
                ["creativeId"] = string.Empty,
                ["revenuePrecision"] = string.Empty,
                ["waterfallInfo"] = new Dictionary<string, object>(),
                ["latencyMillis"] = 0L,
                ["dspName"] = string.Empty
            };

            return new MaxSdkBase.AdInfo(adInfoDictionary);
        }
    }
}

Integration Notes:

  • Create this file in your project's Scripts folder alongside other MeticaAds integration files

  • The extension method enables callback compatibility as seen in: meticaAd.ToAdInfo()

  • This allows unified callback handling regardless of whether MeticaAds or standard MAX SDK is active

Interstitial ads

Interstitial ads are full-screen or full-page ads that temporarily cover an app’s interface. They’re typically shown at natural pauses or transition points, such as after completing a level in a game or when navigating between major views.

The following sections show you how to load and then show an interstitial ad.

Loading an Interstitial Ad

The following code shows you how to attach listeners and load the first interstitial ad:

private void InitializeInterstitialAds()
{
    // Attach callbacks based on whether MeticaAds is enabled
    if (IsMeticaAdsEnabled)
    {
        // Use MeticaAds callbacks with extension methods to convert to MAX callback signatures
        MeticaAdsCallbacks.Interstitial.OnAdLoadSuccess += (meticaAd) => OnInterstitialLoadedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Interstitial.OnAdLoadFailed += (error) => OnInterstitialFailedEvent("", error);
        MeticaAdsCallbacks.Interstitial.OnAdShowSuccess += (meticaAd) => OnInterstitialDisplayedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Interstitial.OnAdShowFailed += (meticaAd, error) => InterstitialFailedToDisplayEvent(meticaAd.adUnitId, error);
        MeticaAdsCallbacks.Interstitial.OnAdClicked += (meticaAd) => OnInterstitialClickedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Interstitial.OnAdHidden += (meticaAd) => OnInterstitialDismissedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Interstitial.OnAdRevenuePaid += (meticaAd) => OnInterstitialRevenuePaidEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
    }
    else
    {
        // Use standard MAX SDK callbacks
        MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent;
        MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += (adUnitId, adInfo) => OnInterstitialFailedEvent(adUnitId, adInfo.Message);
        MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayedEvent;
        MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += (adUnitId, errorInfo, adInfo) => InterstitialFailedToDisplayEvent(adUnitId, errorInfo.Message);
        MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterstitialClickedEvent;
        MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialDismissedEvent;
        MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnInterstitialRevenuePaidEvent;
    }

    // Load the first interstitial
    LoadInterstitial();
}

void LoadInterstitial()
{
    interstitialStatusText.text = "Loading...";
    if (IsMeticaAdsEnabled)
    {
        MeticaAds.LoadInterstitial();
    }
    else
    {
        MaxSdk.LoadInterstitial(InterstitialAdUnitId);
    }
}

private void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad is ready to be shown
    interstitialStatusText.text = $"Loaded {adUnitId}";
    Debug.Log("Interstitial loaded");
    
    // Reset retry attempt
    interstitialRetryAttempt = 0;
}

private void OnInterstitialFailedEvent(string adUnitId, string error)
{
    // Interstitial ad failed to load. Retry with exponentially higher delays up to a maximum delay
    interstitialRetryAttempt++;
    double retryDelay = Math.Pow(2, Math.Min(6, interstitialRetryAttempt));
    
    interstitialStatusText.text = $"Load failed {adUnitId}: " + error + "\nRetrying in " + retryDelay + "s...";
    Debug.Log("Interstitial failed to load with error: " + error);
    
    Invoke("LoadInterstitial", (float)retryDelay);
}

private void OnInterstitialDisplayedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    Debug.Log($"Interstitial shown {adUnitId}");
}

private void InterstitialFailedToDisplayEvent(string adUnitId, string error)
{
    // Interstitial ad failed to display. Load the next ad
    Debug.Log("Interstitial failed to display: " + error);
    LoadInterstitial();
}

private void OnInterstitialClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    Debug.Log($"Interstitial clicked {adUnitId}");
}

private void OnInterstitialDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad is hidden. Pre-load the next ad
    Debug.Log("Interstitial dismissed");
    LoadInterstitial();
}

private void OnInterstitialRevenuePaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad revenue paid. Use this callback to track user revenue
    double revenue = adInfo.Revenue;
    string countryCode = MaxSdk.GetSdkConfiguration().CountryCode;
    string networkName = adInfo.NetworkName;
    string adUnitIdentifier = adInfo.AdUnitIdentifier;
    string placement = adInfo.Placement;
    
    Debug.Log($"Interstitial revenue paid - AdUnit: {adUnitIdentifier}, Revenue: {revenue}, Network: {networkName}, Country: {countryCode}, Placement: {placement}");
}

Showing an Interstitial Ad

To show an interstitial ad, call ShowInterstitial():

void ShowInterstitial()
{
    if (IsMeticaAdsEnabled) {
        if (MeticaAds.IsInterstitialReady())
        {
            MeticaAds.ShowInterstitial();
        }
        else
        {
            interstitialStatusText.text = "Ad not ready";
        }
    }
    else
    {
        if (MaxSdk.IsInterstitialReady(InterstitialAdUnitId)) 
        {
            intersitialStatusText.text = "Showing";
            MaxSdk.ShowInterstitial(InterstitiaAdUnitId);
        }
        else 
        {
            interstitialStatusText.text = "Ad not ready";
        }
    }
}

Rewarded Ads

Rewarded ads are video ads that users can choose to watch in exchange for in-app rewards.

Loading a Rewarded Ad

The InitializeRewardedAds method attaches listeners for both Metica and MAX SDK rewarded ad callbacks and then loads the first rewarded ad.

private void InitializeRewardedAds()
{
    // Attach callbacks based on whether MeticaAds is enabled
    if (IsMeticaAdsEnabled)
    {
        // Use MeticaAds callbacks with extension methods to convert to MAX callback signatures
        MeticaAdsCallbacks.Rewarded.OnAdLoadSuccess += (meticaAd) => OnRewardedAdLoadedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Rewarded.OnAdLoadFailed += (error) => OnRewardedAdFailedEvent("", error);
        MeticaAdsCallbacks.Rewarded.OnAdShowSuccess += (meticaAd) => OnRewardedAdDisplayedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Rewarded.OnAdShowFailed += (meticaAd, error) => OnRewardedAdFailedToDisplayEvent(meticaAd.adUnitId, error);
        MeticaAdsCallbacks.Rewarded.OnAdClicked += (meticaAd) => OnRewardedAdClickedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Rewarded.OnAdHidden += (meticaAd) => OnRewardedAdDismissedEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
        MeticaAdsCallbacks.Rewarded.OnAdRewarded += (meticaAd) => OnRewardedAdReceivedRewardEvent
        (
            meticaAd.adUnitId,
            new MaxSdkBase.Reward { Label = "Coin", Amount = 1 },
            meticaAd.ToAdInfo()
        );
        MeticaAdsCallbacks.Rewarded.OnAdRevenuePaid += (meticaAd) => OnRewardedAdRevenuePaidEvent(meticaAd.adUnitId, meticaAd.ToAdInfo());
    }
    else
    {
        // Use standard MAX SDK callbacks
        MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent;
        MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += (adUnitId, errorInfo) => OnRewardedAdFailedEvent(adUnitId, errorInfo.Message);
        MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent;
        MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += (adUnitId, errorInfo, adInfo) => OnRewardedAdFailedToDisplayEvent(adUnitId, errorInfo.Message);
        MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent;
        MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdDismissedEvent;
        MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent;
        MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdRevenuePaidEvent;
    }

    // Load the first rewarded ad
    LoadRewardedAd();
}

private void LoadRewardedAd()
{
    rewardedStatusText.text = "Loading...";
    if (IsMeticaAdsEnabled)
    {
        MeticaAds.LoadRewarded();
    }
    else
    {
        MaxSdk.LoadRewardedAd(RewardedAdUnitId);
    }
}

private void OnRewardedAdLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad is ready to be shown
    rewardedStatusText.text = $"Loaded {adUnitId}";
    Debug.Log("Rewarded ad loaded");

    // Reset retry attempt
    rewardedRetryAttempt = 0;
}

private void OnRewardedAdFailedEvent(string adUnitId, string error)
{
    // Rewarded ad failed to load. Retry with exponentially higher delays up to a maximum delay
    rewardedRetryAttempt++;
    double retryDelay = Math.Pow(2, Math.Min(6, rewardedRetryAttempt));

    rewardedStatusText.text = $"Load failed {adUnitId}: " + error + "\nRetrying in " + retryDelay + "s...";
    Debug.Log("Rewarded ad failed to load with error: " + error);

    Invoke("LoadRewardedAd", (float)retryDelay);
}

private void OnRewardedAdDisplayedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    Debug.Log($"Rewarded ad displayed {adUnitId}");
}

private void OnRewardedAdFailedToDisplayEvent(string adUnitId, string error)
{
    // Rewarded ad failed to display. Load the next ad
    Debug.Log("Rewarded ad failed to display: " + error);
    LoadRewardedAd();
}

private void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    Debug.Log($"Rewarded ad clicked {adUnitId}");
}

private void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad is hidden. Pre-load the next ad
    Debug.Log("Rewarded ad dismissed");
    LoadRewardedAd();
}

private void OnRewardedAdReceivedRewardEvent(string adUnitId, MaxSdk.Reward reward, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad was displayed and user should receive the reward
    Debug.Log("Rewarded ad received reward");
    // Implement your reward logic here
}

private void OnRewardedAdRevenuePaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad revenue paid. Use this callback to track user revenue
    double revenue = adInfo.Revenue;
    string countryCode = MaxSdk.GetSdkConfiguration().CountryCode;
    string networkName = adInfo.NetworkName;
    string adUnitIdentifier = adInfo.AdUnitIdentifier;
    string placement = adInfo.Placement;

    Debug.Log($"Rewarded ad revenue paid - AdUnit: {adUnitIdentifier}, Revenue: {revenue}, Network: {networkName}, Country: {countryCode}, Placement: {placement}");
}

Showing a Rewarded Ad

To show an interstitial ad, call ShowRewarded():

void ShowRewardedAd()
{
	if (IsMeticaAdsEnabled)
	{
		if (MeticaAds.IsRewardedReady())
		{
			rewardedStatusText.text = "Showing";
			MeticaAds.ShowRewarded();
		}
		else
		{
			rewardedStatusText.text = "Ad not ready";
		}
	}
	else 
	{
		if (MaxSdk.IsRewardedAdReady(RewardedAdUnitId))
		{
			rewardedStatusText.text = "Showing";
			MaxSdk.ShowRewardedAd(RewardedAdUnitId);
		}
		else
		{
			rewardedStatusText.text = "Ad not ready";
		}
	}
}

Metica SDK vs AppLovin Interstitial API

API Calls

Metica's API in contrast to AppLovin does not require an adUnitId to be passed. The reason is that Metica's AI technology automatically selects and manages the optimal ad unit IDs behind the scenes, eliminating the need for developers to manually specify these IDs in each function call and simplifying integration.

Metica Ads API

MeticaAds.LoadInterstitial();        
MeticaAds.IsInterstitialReady();                
MeticaAds.ShowInterstitial();

MeticaAds.LoadRewarded();        
MeticaAds.IsRewardedAdReady();                
MeticaAds.ShowRewarded();

Max SDK API

MaxSdk.LoadInterstitialAd(InterstitialAdUnitId);  
MaxSdk.IsInterstitialAdReady(InterstitialAdUnitId); 
MaxSdk.ShowInterstitialAd(InterstitialAdUnitId);

MaxSdk.LoadRewardedAd(RewardedAdUnitId);  
MaxSdk.IsRewardedAdReady(RewardedAdUnitId); 
MaxSdk.ShowRewardedAd(RewardedAdUnitId);

Callbacks

Metica Ad Callbacks

MeticaAdsCallbacks.Interstitial.OnAdLoadSuccess
MeticaAdsCallbacks.Interstitial.OnAdLoadFailed
MeticaAdsCallbacks.Interstitial.OnAdShowSuccess
MeticaAdsCallbacks.Interstitial.OnAdShowFailed
MeticaAdsCallbacks.Interstitial.OnAdClicked
MeticaAdsCallbacks.Interstitial.OnAdHidden

MeticaAdsCallbacks.Rewarded.OnAdLoadSuccess
MeticaAdsCallbacks.Rewarded.OnAdLoadFailed
MeticaAdsCallbacks.Rewarded.OnAdShowSuccess
MeticaAdsCallbacks.Rewarded.OnAdShowFailed
MeticaAdsCallbacks.Rewarded.OnAdClicked
MeticaAdsCallbacks.Rewarded.OnAdHidden

Max Ad Callback

MaxSdkCallbacks.Interstitial.OnAdLoadedEvent
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent
MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent
MaxSdkCallbacks.Interstitial.OnAdClickedEvent
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent

MaxSdkCallbacks.Rewarded.OnAdLoadedEvent
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent
MaxSdkCallbacks.Rewarded.OnAdClickedEvent
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent

Code Samples

The Metica Unity SDK package includes examples that can be easily imported into your Assets by selecting the package in the Package Manager and clicking the Import button next to one of the examples.

Currently available examples:

  • MeticaExample01 shows how to use the SDK via MeticaAPI(deprecated). This uses the static calls code style.

  • MeticaExample02 shows how to use the SDK by retrieving the instance and using asynchronous code style.

Privacy Manifest

For iOS, iPadOS, tvOS and watchOS apps, we provide a privacy manifest at Assets/Plugins/PrivacyInfo.xcprivacy that describes the data collected by the Metica SDK.

Last updated

Was this helpful?