1 简介

将已完成的 Unity 游戏接入亚马逊商店,主要需要注意内购和广告系统的接入。游戏亚马逊平台的特殊性,原 Google Play 相关的 SDK 都无法使用。考虑到版本迭代的便捷性,可以与原项目共用同一个代码库,而在涉及到不同平台的 IAP 和广告请求时使用宏来区分。接下来主要从内购和广告两个方面来详细介绍一下 Unity 游戏如何接入亚马逊 Appstore。

2 亚马逊内购接入

亚马逊内购接入有两种方式,一种是使用 Appstore SDK Plugin for Unity ,另一种则是直接使用 UnityIAP 来接入 Amazon Appstore。使用 Appstore SDK 相对于 UnityIAP 的功能更全面,可以使用 DRM 等内容。但 UnityIAP 使用起来更便捷,并且原项目有使用过 UnityIAP,从效率层面出发,更推荐使用 UnityIAP。下面将详细介绍使用 UnityIAP 接入 Amazon Appstore 的步骤。

2.1 项目安装并配置 UnityIAP 插件

2.1.1 安装插件

  1. 打开 Unity Hub 并启动你的项目,确保 Build Target 是 Android。
  2. 进入 Unity 编辑器,点击顶部菜单栏的 Window Package Manager
  3. 在 Package Manager 窗口中,选择 Unity Registry,然后在搜索框中输入“Unity IAP”或“In-App Purchasing”。
  4. 找到“Unity IAP”并点击 Install 按钮进行安装。

2.1.2 配置插件

  1. 点击顶部菜单栏 Services In-App Purchasing configure,然后把 Simplify cross-platform IAP 选项切换成 ON
  2. 点击顶部菜单栏 Services In-App Purchasing Switch Store,把 Current Target Store 切换为 Amazon Appstore

2.2 代码实现

代码需要在原项目的基础上实现一个继承自 IStoreListenerAmazonPurchaser 类。

2.2.1 初始化 AmazonPurchaser 类

初始化 AmazonPurchaser 类:

public void InitializePurchasing()  
{  
	ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance(AppStore.AmazonAppStore));  
	
	//给builder里添加商品
	if( initializeWithIAPCatalog)
		IAPConfigurationHelper.PopulateConfigurationBuilder(ref builder, ProductCatalog.LoadDefaultCatalog());
	else if(products != null)
		builder.AddProducts(products);
 
	//做沙盒测试时使用,需要有手机写入权限。生产环境应当删除最后的WriteSandboxJSON(builder.products)
	builder.Configure<IAmazonConfiguration>().WriteSandboxJSON(builder.products);
	
	UnityPurchasing.Initialize(this, builder);  
}

初始化完成和失败的回调:

//初始化完成,获取IStoreController和IExtensionProvider
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
	string amazonUserId =
	extensions.GetExtension<IAmazonExtensions>().amazonUserId;
	Debug.Log("In-App Purchasing successfully initialized. UserID: " + amazonUserId);
	m_StoreController = controller;
	m_StoreExtensionProvider = extensions;
	GooglePlayPurchaser.instance.OnInitialized();
}
 
//初始化失败,获取失败信息
public void OnInitializeFailed(InitializationFailureReason error)
{
	Debug.LogError("OnInitializeFailed InitializationFailureReason:" + error);
}
 
//初始化失败,获取失败信息
public void OnInitializeFailed(InitializationFailureReason error, string message)
{
	Debug.LogError("OnInitializeFailed InitializationFailureReason:" + error+ " message: "+message);
}
 

2.2.2 实现购买功能

 
//购买Product
public void BuyProduct(string productId)  
{  
	m_StoreController.InitiatePurchase(GetProductInfo(productId));  
}
 
//购买成功后游戏内逻辑处理
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)  
{  
	try  
	{  
		string productId = purchaseEvent.purchasedProduct.definition.id;  
		GooglePlayPurchaser.instance.ProcessPurchase(productId);  
	}  
	catch (Exception e)  
	{  
		UnityUtils.LogBold($"ProcessPurchase:{e.Message}");  
	}  
	  
	UnityUtils.Log("Complete");  
	  
	return PurchaseProcessingResult.Complete;  
}
 
//购买失败处理
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)  
{  
	Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product?.definition?.storeSpecificId, failureReason));  
	GooglePlayPurchaser.instance.OnPurchaseFailed(failureReason == PurchaseFailureReason.UserCancelled);  
	RecordAmazonFailedMessage(product, failureReason);  
}
 
//存储收据
public void RestorePurchases()  
{  
	//TODO
}
 
//获取ProductInfo
public Product GetProductInfo(string productID)  
{  
	return m_StoreController?.products.WithID(productID);  
}

上述两部分构成了 AmazonPurchaser 的主要功能。需要注意的是,为了避免和其他渠道混淆,不建议直接调用 AmazonPurchaser 的函数。更好的做法是和其他的购买渠道一起再包一层类,用于统一管理和调度,根据不同的平台做区分。上述的示例代码中,GooglePlayPurchaser 就是一个管理的类,只是名称沿用了旧的管理类名。

另外,Amazon Appstore 的内购匹配是通过应用签名、包名、商品 SKU 三者来匹配的,只有三者都符合的时候才能初始化商品成功。如果所有添加的商品都初始化失败,则 IAP 初始化失败。

2.3 后台添加商品

出于便捷考虑,建议 Amazon 的商品 SKU 可以与 GooglePlay 设置成相同的。如果设置成不同的,则可以在初始化 IAP 的时候用以下代码做区分:

// Define our products.
// In this case our products have the same identifier across all the App stores,
// except on the Mac App store where product IDs cannot be reused across both Mac and
// iOS stores.
// So on the Mac App store our products have different identifiers,
// and we tell Unity IAP this by using the IDs class.
builder.AddProduct("100.gold.coins", ProductType.Consumable, new IDs
{
	{"100.gold.coins.mac", MacAppStore.Name},
	{"000000596586", TizenStore.Name},
	{"com.ff", MoolahAppStore.Name},
});

2.4 测试与发包

Amazon Appstore 的测试方式可以分为本地沙盒测试和 Live App Testing。本地沙盒测试主要是利用 Amazon App Tester 来测试游戏内购买功能是否正常,是否能正常获取回调;Live App Testing 则是用来测试能否正常获取到服务器的商品。

2.4.1 本地沙盒测试

本地沙盒测试使用的是 Amazon App Tester 来给游戏本地返回一个购买响应,且可以自定义返回结果:成功、失败。下面是具体的沙盒测试步骤。

  1. 从亚马逊商城下载 App Tester
  2. 将设备连接至电脑,开启 USB 调试,输入下述命令: adb shell setprop debug.amazon.sandboxmode debug 如果需要取消,可以输入: adb shell setprop debug.amazon.sandboxmode none
  3. 从后台下载商品 JSON 并将其放置到设备 /sdcard/ 路径 $ adb push [_您的JSON文件夹_]/amazon.sdktester.json /sdcard/amazon.sdktester.json 或者可以直接使用代码中的 builder.Configure<IAmazonConfiguration>().WriteSandboxJSON(builder.products); 来写入
  4. 打开 App Tester 点击 Appstore SDK API's 9. IAP Items in JSON File 来查看是否成功写入商品。
  5. 在 App Tester 中设置用户、市场、响应等,然后打开应用进行购买测试。

下面是部分可用的响应:

API 名称可用响应
GetProductDataDefault、SUCCESSFUL、FAILED
GetPurchaseUpdatesDefault、SUCCESSFUL、FAILED
GetUserDataDefault、SUCCESSFUL、FAILED
PurchaseDefault、SUCCESSFUL、FAILED、ALREADY_PURCHASED、INVALID_SKU

AppTester 使用 Logcat 工具来捕获有关应用交易以及其他 API 交互的详细信息。使用 Logcat 查看在测试应用时生成的日志:

  1. 将包含应用和 AppTester 的 Android 设备连接到运行 Android 调试桥(ADB)的计算机。
  2. 搜索标签“AmazonAppTester”以查看 AppTester 日志条目。

2.4.2 Live App Testing

Live App Testing 是亚马逊的动态应用测试 (LAT) 服务,上线之前将要在亚马逊应用商店发布的应用快速分发给一组预设定的测试人员进行测试。

  • 下面是 LAT 的具体步骤
  1. 将应用的 APK 提交到 LAT。
  2. 让测试者通过 LAT 测试您的应用:
    1. 添加测试人员。
    2. 测试者下载并测试您的 APK。
  3. 评估测试者的反馈。
  4. 根据测试结果和反馈更改您的应用。根据需要重新提交应用进行测试。
  5. 对测试结果感到满意后,在亚马逊应用商店上发布您的应用。
  • 后台动态测试的创建和提交
  1. 转到 开发者控制台主信息页面 并使用您的开发者账户登录。选择应用程序列表,然后选择要设置动态应用测试的应用。
  2. 从应用名称下方的菜单中选择动态应用测试
  3. 您应用的动态应用测试控制面板打开。选择快速创建动态应用测试,然后通过确认对话框继续进行操作。
  4. 在“快速创建”页面上,上传二进制文件并选择支持的设备。
  5. 填写剩余的必填字段。必填字段包括应用程序标题应用程序类别语言支持内容评级应用程序图标出口合规性。要查看放大的“快速创建”页面,请单击该图像。
  6. 添加测试者分组。已有测试者分组会显示在此处,可以选择为该动态应用测试添加哪些测试者分组。如果需要添加新的测试者,请单击测试者管理,然后按照 动态应用测试的标准提交 中步骤 4 的说明操作。
  7. 选择提交

Pasted image 20230901152403

Pasted image 20230901152524

3 Applovin

Amazon Appstore 平台使用的广告平台是 Applovin,下面详细介绍一下 Applovin 的接入步骤。

3.1 安装设置 SDK

  1. 下载并安装 AppLovin-MAX-Unity-Plugin
  2. 在 Unity 点击 Applovin Integration Manager,然后选择自己需要安装的广告平台,并输入 SDK Key

3.2 代码实现

3.2.1 初始化 SDK

MaxSdkCallbacks.OnSdkInitializedEvent += (MaxSdkBase.SdkConfiguration sdkConfiguration) => {
    // AppLovin SDK is initialized, start loading ads
};
 
MaxSdk.SetSdkKey("YOUR_SDK_KEY_HERE");
MaxSdk.SetUserId("USER_ID");
MaxSdk.InitializeSdk();

3.2.2 Banner 实现

  • 初始化 banner
#if UNITY_IOS
string bannerAdUnitId = "YOUR_IOS_BANNER_AD_UNIT_ID"; // Retrieve the ID from your account
#else // UNITY_ANDROID
string bannerAdUnitId = "YOUR_ANDROID_BANNER_AD_UNIT_ID"; // Retrieve the ID from your account
#endif
//初始化banner
public void InitializeBannerAds()
{
    // 初始化Banner和设置Banner的位置
    MaxSdk.CreateBanner(bannerAdUnitId, MaxSdkBase.BannerPosition.BottomCenter);
 
    // 设置banner背景颜色
    MaxSdk.SetBannerBackgroundColor(bannerAdUnitId, <YOUR_BANNER_BACKGROUND_COLOR>);
 
	//设置回调
    MaxSdkCallbacks.Banner.OnAdLoadedEvent      += OnBannerAdLoadedEvent;
    MaxSdkCallbacks.Banner.OnAdLoadFailedEvent  += OnBannerAdLoadFailedEvent;
    MaxSdkCallbacks.Banner.OnAdClickedEvent     += OnBannerAdClickedEvent;
    MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerAdRevenuePaidEvent;
    MaxSdkCallbacks.Banner.OnAdExpandedEvent    += OnBannerAdExpandedEvent;
    MaxSdkCallbacks.Banner.OnAdCollapsedEvent   += OnBannerAdCollapsedEvent;
}
 
//回调获取
private void OnBannerAdLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnBannerAdLoadFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) {}
 
private void OnBannerAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnBannerAdRevenuePaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnBannerAdExpandedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)  {}
 
private void OnBannerAdCollapsedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
  • 展示 Banner
MaxSdk.ShowBanner(bannerAdUnitId);
  • 隐藏 Banner
MaxSdk.HideBanner(bannerAdUnitId);
  • 摧毁 Banner
MaxSdk.DestroyBanner(bannerAdUnitId);

3.2.3 RV 实现

  • 初始化 RV
#if UNITY_IOS
string adUnitId = "YOUR_IOS_AD_UNIT_ID";
#else // UNITY_ANDROID
string adUnitId = "YOUR_ANDROID_AD_UNIT_ID";
#endif
int retryAttempt;
 
public void InitializeRewardedAds()
{
    // 设置回调函数
    MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent;
    MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdLoadFailedEvent;
    MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent;
    MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent;
    MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdRevenuePaidEvent;
    MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdHiddenEvent;
    MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardedAdFailedToDisplayEvent;
    MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent;
            
    // 加载RV
    LoadRewardedAd();
}
 
private void LoadRewardedAd()
{
    MaxSdk.LoadRewardedAd(adUnitId);
}
 
private void OnRewardedAdLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad is ready for you to show. MaxSdk.IsRewardedAdReady(adUnitId) now returns 'true'.
 
    // Reset retry attempt
    retryAttempt = 0;
}
 
private void OnRewardedAdLoadFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
    // Rewarded ad failed to load 
    // AppLovin recommends that you retry with exponentially higher delays, up to a maximum delay (in this case 64 seconds).
 
    retryAttempt++;
    double retryDelay = Math.Pow(2, Math.Min(6, retryAttempt));
    
    Invoke("LoadRewardedAd", (float) retryDelay);
}
 
private void OnRewardedAdDisplayedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnRewardedAdFailedToDisplayEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad failed to display. AppLovin recommends that you load the next ad.
    LoadRewardedAd();
}
 
private void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnRewardedAdHiddenEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Rewarded ad is hidden. Pre-load the next ad
    LoadRewardedAd();
}
 
private void OnRewardedAdReceivedRewardEvent(string adUnitId, MaxSdk.Reward reward, MaxSdkBase.AdInfo adInfo)
{
    // The rewarded ad displayed and the user should receive the reward.
}
 
private void OnRewardedAdRevenuePaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Ad revenue paid. Use this callback to track user revenue.
}
  • 展示 RV
if (MaxSdk.IsRewardedAdReady(adUnitId))
{
    MaxSdk.ShowRewardedAd(adUnitId);
}

3.2.4 插屏实现

  • 初始化加载插屏
#if UNITY_IOS
string adUnitId = "YOUR_IOS_AD_UNIT_ID";
#else // UNITY_ANDROID
string adUnitId = "YOUR_ANDROID_AD_UNIT_ID";
#endif
int retryAttempt;
 
public void InitializeInterstitialAds()
{
    // Attach callback
    MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent;
    MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialLoadFailedEvent;
    MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayedEvent;
    MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterstitialClickedEvent;
    MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialHiddenEvent;
    MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnInterstitialAdFailedToDisplayEvent;
    
    // Load the first interstitial
    LoadInterstitial();
}
 
private void LoadInterstitial()
{
    MaxSdk.LoadInterstitial(adUnitId);
}
 
private void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad is ready for you to show. MaxSdk.IsInterstitialReady(adUnitId) now returns 'true'
 
    // Reset retry attempt
    retryAttempt = 0;
}
 
private void OnInterstitialLoadFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
    // Interstitial ad failed to load 
    // AppLovin recommends that you retry with exponentially higher delays, up to a maximum delay (in this case 64 seconds)
 
    retryAttempt++;
    double retryDelay = Math.Pow(2, Math.Min(6, retryAttempt));
    
    Invoke("LoadInterstitial", (float) retryDelay);
}
 
private void OnInterstitialDisplayedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnInterstitialAdFailedToDisplayEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad failed to display. AppLovin recommends that you load the next ad.
    LoadInterstitial();
}
 
private void OnInterstitialClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) {}
 
private void OnInterstitialHiddenEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    // Interstitial ad is hidden. Pre-load the next ad.
    LoadInterstitial();
}
  • 展示插屏
if ( MaxSdk.IsInterstitialReady(adUnitId) )
{
    MaxSdk.ShowInterstitial(adUnitId);
}

3.3 注意事项

上述实现方式主要讲述的是请求广告的实现方式,在实际项目应用中还需要根据在这之上套一层广告管理的类,然后对不同的平台做不同广告渠道的选择,可以通过 Unity 的宏来实现。另外,如果项目中本来就已经有 Admob,则 Admob SDK 和 Applovin 中的 admob 的 key 要保持一致。

3.4 补充 AmazonPublicService 接入 Max 部分

MaxSdk 现在不提供 APS 的 Adapter,需要手动安装 APS 的 Unity SDK,然后以 SetBannerLocalExtraParameter 的方式接入到 MaxSdk 中。

下面是一些补充示例:

3.4.1 Banner 实现

using System.Collections;
using System.Collections.Generic;
using AmazonAds;
using UnityEngine;
 
public class BannerAds_AppLovin : AdUnitBase
{
    public override void LoadAd(string[] ids=null)
    {
        if (!InitAds_AppLovin.IsInit)
            InitAds_AppLovin.Init();
        SetAdsEvents();
        int width;
        int height;
        if (MaxSdkUtils.IsTablet())
        {
            width = 728;
            height = 90;
        }
        else
        {
            width = 320;
            height = 50;
        }
#if UNITY_EDITOR
        CreateMaxBannerAd();
        return;
#endif
 
        var apsBanner = new APSBannerAdRequest(width, height, AmazonAdsId);
        apsBanner.onSuccess += (adResponse) =>
        {   
            MaxSdk.SetBannerLocalExtraParameter(CurAdsId, "amazon_ad_response", adResponse.GetResponse());
            CreateMaxBannerAd();
        };
        apsBanner.onFailedWithError += (adError) =>
        {
            MaxSdk.SetBannerLocalExtraParameter(CurAdsId, "amazon_ad_error", adError.GetAdError());
            CreateMaxBannerAd();
        };
 
        apsBanner.LoadAd();
    }
    
    private void CreateMaxBannerAd()
    {
        MaxSdk.CreateBanner(CurAdsId, MaxSdkBase.BannerPosition.BottomCenter);
        MaxSdk.SetBannerPlacement(CurAdsId, "BannerPosition.BottomCenter");
    }
 
    private void SetAdsEvents()
    {
        MaxSdkCallbacks.Banner.OnAdLoadedEvent-=HandleBannerAdLoaded;
        MaxSdkCallbacks.Banner.OnAdLoadedEvent+=HandleBannerAdLoaded;
        MaxSdkCallbacks.Banner.OnAdLoadFailedEvent-=HandleBannerAdFailedToLoad;
        MaxSdkCallbacks.Banner.OnAdLoadFailedEvent+=HandleBannerAdFailedToLoad;
        MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent-=HandleBannerAdRevenuePaidEvent;
        MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent+=HandleBannerAdRevenuePaidEvent;
    }
}
 

3.5 RV 实现

public class RewardAds_AppLovin : AdUnitBase
{
    private object userData;
    private bool isUserEarnedReward;
    private bool _isFirstLoad = true;
    private readonly string AmazonAdsId = "xxxxxxx";
    
    public override void LoadAd(string[] ids)
    {
        if (!InitAds_AppLovin.IsInit)
            InitAds_AppLovin.Init();
        SetAdsEvents();
#if UNITY_EDITOR
        MaxSdk.LoadRewardedAd(CurAdsId);
        return;
#endif
        if (_isFirstLoad)
        {
            _isFirstLoad = false;
 
            var rewardedVideoAd = new APSVideoAdRequest(320, 480, AmazonAdsId);
            rewardedVideoAd.onSuccess += (adResponse) =>
            {
                MaxSdk.SetRewardedAdLocalExtraParameter(CurAdsId, "amazon_ad_response", adResponse.GetResponse());
                Debug.Log("[AdManager] AppLovin SDK is Loading RV" + CurAdsId);
                MaxSdk.LoadRewardedAd(CurAdsId);
            };
            rewardedVideoAd.onFailedWithError += (adError) =>
            {
                MaxSdk.SetRewardedAdLocalExtraParameter(CurAdsId, "amazon_ad_error", adError.GetAdError());
                Debug.Log("[AdManager] AppLovin SDK is Loading RV" + CurAdsId);
                MaxSdk.LoadRewardedAd(CurAdsId);
            };
 
            rewardedVideoAd.LoadAd();
			
        }
        else
        {
            Debug.Log("[AdManager] AppLovin SDK is Loading RV" + CurAdsId);
            MaxSdk.LoadRewardedAd(CurAdsId);
        }
    }
 
    private void SetAdsEvents()
    {
        MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnAdLoadedEvent;
        MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnAdLoadedEvent;
        MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnAdLoadFailedEvent;
        MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnAdLoadFailedEvent;
        MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent -= OnAdDisplayFailedEvent;
        MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnAdDisplayFailedEvent;
        MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent -= OnAdRevenuePaidEvent;
        MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent;
        MaxSdkCallbacks.Rewarded.OnAdHiddenEvent -= OnAdHiddenEvent;
        MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnAdHiddenEvent;
        MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent -= OnAdReceivedRewardEvent;
        MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnAdReceivedRewardEvent;
    }
}
 

3.6 插屏实现

using System.Collections;
using System.Collections.Generic;
using AmazonAds;
using Firebase.Analytics;
using UnityEngine;
 
public class InterstitialAds_AppLovin : AdUnitBase
{
 
    private readonly string AmazonAdsId = "xxxxxxxx";
 
    public override void LoadAd(string[] ids)
    {
        if (!InitAds_AppLovin.IsInit)
            InitAds_AppLovin.Init();
        SetAdsEvents();
#if UNITY_EDITOR
        MaxSdk.LoadInterstitial(CurAdsId);
        return;
#endif
        if (IsFirstLoad)
        {
            IsFirstLoad = false;
 
            var interstitialVideoAd = new APSVideoAdRequest(320, 480, AmazonAdsId);
            interstitialVideoAd.onSuccess += (adResponse) =>
            {
                MaxSdk.SetInterstitialLocalExtraParameter(CurAdsId, "amazon_ad_response", adResponse.GetResponse());
                MaxSdk.LoadInterstitial(CurAdsId);
            };
            interstitialVideoAd.onFailedWithError += (adError) =>
            {
                MaxSdk.SetInterstitialLocalExtraParameter(CurAdsId, "amazon_ad_error", adError.GetAdError());
                MaxSdk.LoadInterstitial(CurAdsId);
            };
 
            interstitialVideoAd.LoadAd();
        }
        else
        {
            MaxSdk.LoadInterstitial(CurAdsId);
        }
    }
 
    private bool IsFirstLoad = true;
 
    public override void Dispose()
    {
        ShowAdsCanvas(false);
        IsLoad = false;
        base.Dispose();
    }
 
    private void SetAdsEvents()
    {
        MaxSdkCallbacks.Interstitial.OnAdLoadedEvent-= OnAdLoadedEvent;
        MaxSdkCallbacks.Interstitial.OnAdLoadedEvent+= OnAdLoadedEvent;
        MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent-= OnAdLoadFailedEvent;
        MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent+= OnAdLoadFailedEvent;
        MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent-= OnAdDisplayFailedEvent;
        MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent+= OnAdDisplayFailedEvent;
        MaxSdkCallbacks.Interstitial.OnAdHiddenEvent-= OnAdHiddenEvent;
        MaxSdkCallbacks.Interstitial.OnAdHiddenEvent+= OnAdHiddenEvent;
        MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent-= OnAdRevenuePaidEvent;
        MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent+= OnAdRevenuePaidEvent;
    }
}
 

4 总结

本文档详细介绍了如何将已完成的 Unity 游戏接入亚马逊 Appstore,重点关注了内购和广告系统的实现。文档首先讨论了两种内购接入方式:使用 Appstore SDK Plugin for Unity 和使用 UnityIAP。其中,UnityIAP 被推荐用于项目中,因为它更加便捷且与原项目兼容性好。

在内购部分,文档提供了详细的代码示例,包括如何初始化购买、处理购买成功和失败的回调等。同时,也讲解了如何在 Amazon 后台添加商品和进行沙盒测试与 Live App Testing。

在广告接入方面,文档介绍了如何使用 Applovin 广告平台。这包括 SDK 的安装和初始化,以及如何在 Unity 中实现 Banner、激励视频和插屏广告。每种广告类型都有对应的代码示例和实现步骤。

最后,文档强调了在实际项目中,最好是在这些具体实现之上再添加一层广告和内购的管理类,以便能够灵活地适应不同的平台和需求。

总体而言,这份文档为 Unity 开发者提供了一份全面、详细的指南,用于帮助他们顺利地将游戏接入亚马逊 Appstore,同时也提供了丰富的代码示例和最佳实践。

希望这份总结能够帮助你更好地理解文档的核心内容和实施步骤。如果你有进一步的问题或需要深入某个特定主题,随时可以提出。