chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
393
Assets/Scripts/Shop/IAPManager.cs
Normal file
393
Assets/Scripts/Shop/IAPManager.cs
Normal file
@@ -0,0 +1,393 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Purchasing;
|
||||
using System.Collections;
|
||||
using UnityEngine.Purchasing.Extension;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class IAPManager : MonoBehaviour, IDetailedStoreListener
|
||||
{
|
||||
private static IStoreController storeController;
|
||||
private static IExtensionProvider storeExtensionProvider;
|
||||
|
||||
public const string PRODUCT_1000_COINS = "1000_coins";
|
||||
public const string PRODUCT_2150_COINS = "2150_coins";
|
||||
public const string PRODUCT_4350_COINS = "4350_coins";
|
||||
public const string PRODUCT_6700_COINS = "6700_coins";
|
||||
public const string PRODUCT_11500_COINS = "11500_coins";
|
||||
|
||||
private CoinWalletSystem coinWalletSystem;
|
||||
private StoreUIManager storeUIManager;
|
||||
|
||||
[SerializeField] private GameObject purchaseLoadingUI;
|
||||
[SerializeField] private GameObject purchaseSuccessUI;
|
||||
[SerializeField] private GameObject purchaseFailedUI;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
coinWalletSystem = CoinWalletSystem.Instance;
|
||||
storeUIManager = FindAnyObjectByType<StoreUIManager>();
|
||||
|
||||
|
||||
// Deactivate both UIs initially
|
||||
if (storeUIManager != null) storeUIManager.gameObject.SetActive(false);
|
||||
|
||||
|
||||
// Check if in development (Editor) or production (build)
|
||||
#if UNITY_EDITOR
|
||||
// In development, show fake store UI
|
||||
|
||||
#else
|
||||
// In production, show real store UI
|
||||
//if (storeUIManager != null) storeUIManager.gameObject.SetActive(true);
|
||||
#endif
|
||||
|
||||
if (storeController == null)
|
||||
{
|
||||
InitializePurchasing();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePurchasing()
|
||||
{
|
||||
if (IsInitialized()) return;
|
||||
|
||||
try
|
||||
{
|
||||
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
|
||||
builder.AddProduct(PRODUCT_1000_COINS, ProductType.Consumable);
|
||||
builder.AddProduct(PRODUCT_2150_COINS, ProductType.Consumable);
|
||||
builder.AddProduct(PRODUCT_4350_COINS, ProductType.Consumable);
|
||||
builder.AddProduct(PRODUCT_6700_COINS, ProductType.Consumable);
|
||||
builder.AddProduct(PRODUCT_11500_COINS, ProductType.Consumable);
|
||||
|
||||
Debug.Log("Initializing Unity Purchasing...");
|
||||
UnityPurchasing.Initialize(this, builder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Error initializing IAP: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInitialized() => storeController != null && storeExtensionProvider != null;
|
||||
|
||||
public void Buy1000Coins() { BuyProduct(PRODUCT_1000_COINS, 1000); }
|
||||
public void Buy2150Coins() { BuyProduct(PRODUCT_2150_COINS, 2150); }
|
||||
public void Buy4350Coins() { BuyProduct(PRODUCT_4350_COINS, 4350); }
|
||||
public void Buy6700Coins() { BuyProduct(PRODUCT_6700_COINS, 6700); }
|
||||
public void Buy11500Coins() { BuyProduct(PRODUCT_11500_COINS, 11500); }
|
||||
|
||||
public void BuyProduct(string productID, int coinsToAdd)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowPurchaseLoading(true);
|
||||
Debug.Log($"Attempting to purchase {coinsToAdd} coins");
|
||||
|
||||
if (coinWalletSystem == null)
|
||||
{
|
||||
coinWalletSystem = CoinWalletSystem.Instance;
|
||||
if (coinWalletSystem == null)
|
||||
{
|
||||
Debug.LogError("CoinWalletSystem is not initialized!");
|
||||
ShowPurchaseResult(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if in development (Editor) or production (build)
|
||||
#if UNITY_EDITOR
|
||||
// In development mode, simulate the purchase
|
||||
Debug.Log($"Development mode: Simulating purchase of {coinsToAdd} coins");
|
||||
SimulatePurchase(coinsToAdd);
|
||||
#else
|
||||
// In production mode, trigger the real IAP purchase flow
|
||||
if (IsInitialized())
|
||||
{
|
||||
Product product = storeController.products.WithID(productID);
|
||||
if (product != null && product.availableToPurchase)
|
||||
{
|
||||
storeController.InitiatePurchase(product);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Product not available for purchase: " + productID);
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("In-App Purchasing is not initialized.");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"Error purchasing coins: {e.Message}");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ShowPurchaseLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Add this new method to handle development mode purchases
|
||||
private async void SimulatePurchase(int coinsToAdd)
|
||||
{
|
||||
if (!gameObject.activeInHierarchy)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Log($"Simulating purchase of {coinsToAdd} coins");
|
||||
ShowPurchaseLoading(true);
|
||||
|
||||
// Simulate a short delay to mimic network request
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Add coins directly in development mode
|
||||
await coinWalletSystem.AddCoins(coinsToAdd);
|
||||
|
||||
// Ensure the game object is still active before showing the result
|
||||
if (gameObject != null && gameObject.activeInHierarchy)
|
||||
{
|
||||
ShowPurchaseResult(true);
|
||||
}
|
||||
|
||||
Debug.Log($"Development mode: Successfully added {coinsToAdd} coins");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Development mode: Failed to simulate purchase: {e.Message}");
|
||||
if (gameObject != null && gameObject.activeInHierarchy)
|
||||
{
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (gameObject != null && gameObject.activeInHierarchy)
|
||||
{
|
||||
ShowPurchaseLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a purchase completes
|
||||
public void OnPurchaseComplete(Product product)
|
||||
{
|
||||
try
|
||||
{
|
||||
int coinsToAdd = 0;
|
||||
|
||||
// Determine the number of coins based on the product ID
|
||||
if (product.definition.id == PRODUCT_1000_COINS)
|
||||
{
|
||||
coinsToAdd = 1000;
|
||||
}
|
||||
else if (product.definition.id == PRODUCT_2150_COINS)
|
||||
{
|
||||
coinsToAdd = 2150;
|
||||
}
|
||||
else if (product.definition.id == PRODUCT_4350_COINS)
|
||||
{
|
||||
coinsToAdd = 4350;
|
||||
}
|
||||
else if (product.definition.id == PRODUCT_6700_COINS)
|
||||
{
|
||||
coinsToAdd = 6700;
|
||||
}
|
||||
else if (product.definition.id == PRODUCT_11500_COINS)
|
||||
{
|
||||
coinsToAdd = 11500;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Unrecognized product ID.");
|
||||
ShowPurchaseResult(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the coins to the wallet and show the result
|
||||
if (coinWalletSystem != null)
|
||||
{
|
||||
coinWalletSystem.AddCoins(coinsToAdd);
|
||||
ShowPurchaseResult(true); // Show success UI
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("CoinWalletSystem not found!");
|
||||
ShowPurchaseResult(false); // Show failure UI
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Error processing purchase: {e.Message}");
|
||||
ShowPurchaseResult(false); // Show failure UI
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called when a purchase fails
|
||||
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
|
||||
{
|
||||
Debug.LogError($"Purchase failed for {product.definition.id}: {failureDescription.message}");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
|
||||
// This method simulates showing the store UI in the development build
|
||||
private void ShowFakeStore(string productID, int coinsToAdd)
|
||||
{
|
||||
// Show the fake store UI or simulate the purchase process
|
||||
Debug.Log($"Fake store: ready to purchase {coinsToAdd} coins.");
|
||||
// You may trigger the "Buy" button in the fake store UI to simulate a successful purchase
|
||||
}
|
||||
|
||||
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
|
||||
{
|
||||
if (!gameObject.activeInHierarchy)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
Debug.Log($"Processing purchase for product: {args.purchasedProduct.definition.id}");
|
||||
|
||||
if (coinWalletSystem == null)
|
||||
{
|
||||
coinWalletSystem = CoinWalletSystem.Instance;
|
||||
}
|
||||
|
||||
string productId = args.purchasedProduct.definition.id;
|
||||
int coinsToAdd = 0;
|
||||
|
||||
switch (productId)
|
||||
{
|
||||
case PRODUCT_1000_COINS: coinsToAdd = 1000; break;
|
||||
case PRODUCT_2150_COINS: coinsToAdd = 2150; break;
|
||||
case PRODUCT_4350_COINS: coinsToAdd = 4350; break;
|
||||
case PRODUCT_6700_COINS: coinsToAdd = 6700; break;
|
||||
case PRODUCT_11500_COINS: coinsToAdd = 11500; break;
|
||||
default:
|
||||
Debug.LogError($"Unrecognized product ID {productId}");
|
||||
return PurchaseProcessingResult.Complete;
|
||||
}
|
||||
|
||||
if (coinsToAdd > 0 && coinWalletSystem != null)
|
||||
{
|
||||
// Use async/await pattern with Task.Run to handle the async operation
|
||||
StartCoroutine(ProcessPurchaseAsync(coinsToAdd));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Failed to add coins: CoinWalletSystem not found or coins amount is 0");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
|
||||
return PurchaseProcessingResult.Complete;
|
||||
}
|
||||
|
||||
private IEnumerator ProcessPurchaseAsync(int coinsToAdd)
|
||||
{
|
||||
ShowPurchaseLoading(true);
|
||||
|
||||
var task = coinWalletSystem.AddCoins(coinsToAdd);
|
||||
while (!task.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (task.Exception == null)
|
||||
{
|
||||
ShowPurchaseResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to process purchase: {task.Exception}");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
|
||||
ShowPurchaseLoading(false);
|
||||
}
|
||||
|
||||
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
|
||||
{
|
||||
storeController = controller;
|
||||
storeExtensionProvider = extensions;
|
||||
}
|
||||
|
||||
public void OnInitializeFailed(InitializationFailureReason error)
|
||||
{
|
||||
Debug.LogError($"IAP initialization failed: {error.ToString()}");
|
||||
}
|
||||
|
||||
public void OnInitializeFailed(InitializationFailureReason error, string message)
|
||||
{
|
||||
Debug.LogError($"IAP initialization failed: {error.ToString()}, Message: {message}");
|
||||
}
|
||||
|
||||
// Add the BuyProductID method here
|
||||
public void BuyProductID(string productID)
|
||||
{
|
||||
if (IsInitialized())
|
||||
{
|
||||
Product product = storeController.products.WithID(productID);
|
||||
if (product != null && product.availableToPurchase)
|
||||
{
|
||||
storeController.InitiatePurchase(product);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Product not available for purchase: " + productID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("In-App Purchasing is not initialized.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowPurchaseLoading(bool show)
|
||||
{
|
||||
if (purchaseLoadingUI != null)
|
||||
purchaseLoadingUI.SetActive(show);
|
||||
}
|
||||
|
||||
private void ShowPurchaseResult(bool success)
|
||||
{
|
||||
if (!gameObject.activeInHierarchy) return;
|
||||
|
||||
if (success && purchaseSuccessUI != null)
|
||||
{
|
||||
purchaseSuccessUI.SetActive(true);
|
||||
StartCoroutine(HideUI(purchaseSuccessUI, 2f));
|
||||
}
|
||||
else if (!success && purchaseFailedUI != null)
|
||||
{
|
||||
purchaseFailedUI.SetActive(true);
|
||||
StartCoroutine(HideUI(purchaseFailedUI, 2f));
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator HideUI(GameObject ui, float duration)
|
||||
{
|
||||
if (ui == null) yield break;
|
||||
|
||||
yield return new WaitForSeconds(duration);
|
||||
|
||||
if (ui != null && gameObject != null && gameObject.activeInHierarchy)
|
||||
{
|
||||
ui.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
|
||||
{
|
||||
Debug.LogError($"Purchase failed - Product: {product.definition.id}, Reason: {failureReason}");
|
||||
ShowPurchaseResult(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user