Why a Complete Store System Matters

A robust in-game store is essential for monetization, progression systems, and player engagement. Creating a store system that supports real-world monetization, multiple currencies, dynamic pricing, user authentication, inventory integration, and more is no small feat. This guide takes you through building a comprehensive, reusable store system in Unity.

This system includes:

  • Dynamic loading of store data.
  • Multiple payment options (in-game and real-world currency).
  • Discount and promotion systems.
  • Purchase validation and inventory integration.
  • Modular design for easy adaptation across projects.

What You’ll Learn

By following this guide, you will create a store system that can:

  1. Fetch product details dynamically.
  2. Support multiple in-game currencies and real-money purchases.
  3. Manage player inventory and validate purchases.
  4. Implement time-limited offers and discounts.
  5. Provide modular, reusable components for any Unity project.

Comprehensive Code Implementation

Below is a detailed implementation of a Unity store system.


Step 1: Define Core Data Structures

We’ll use ScriptableObject for predefined store items and external data for dynamic updates.

using System;
using UnityEngine;

[Serializable]
public class StoreItem
{
    public string ItemID; // Unique identifier
    public string Name;
    public string Description;
    public int Price;
    public string CurrencyType; // "Coins", "Gems", or "USD"
    public bool IsLimitedTimeOffer; 
    public DateTime OfferExpiryDate;
    public Sprite Icon;
}

Step 2: Create a ScriptableObject for Store Configuration

[CreateAssetMenu(fileName = "StoreData", menuName = "Store/StoreData")]
public class StoreData : ScriptableObject
{
    public StoreItem[] Items;
}

Step 3: Store Manager with Dynamic Data Loading

This class manages the store’s lifecycle, fetching data, updating UI, and handling purchases.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class StoreManager : MonoBehaviour
{
    public StoreData LocalStoreData; // For offline fallback
    public Transform StoreUIParent;
    public GameObject StoreItemPrefab;
    public string StoreDataURL = "https://example.com/storedata.json"; // Replace with your server URL

    private List<GameObject> activeItems = new List<GameObject>();

    void Start()
    {
        LoadStore();
    }

    public void LoadStore()
    {
        StartCoroutine(FetchStoreData());
    }

    private IEnumerator FetchStoreData()
    {
        UnityWebRequest request = UnityWebRequest.Get(StoreDataURL);
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            var fetchedItems = JsonUtility.FromJson<StoreItem[]>(request.downloadHandler.text);
            PopulateStore(fetchedItems);
        }
        else
        {
            Debug.LogError("Failed to fetch store data. Loading local data.");
            PopulateStore(LocalStoreData.Items);
        }
    }

    private void PopulateStore(StoreItem[] items)
    {
        foreach (Transform child in StoreUIParent) Destroy(child.gameObject);
        activeItems.Clear();

        foreach (var item in items)
        {
            GameObject itemUI = Instantiate(StoreItemPrefab, StoreUIParent);
            StoreItemUI uiComponent = itemUI.GetComponent<StoreItemUI>();
            uiComponent.Setup(item);
            activeItems.Add(itemUI);
        }
    }
}

Step 4: UI for Store Items

using UnityEngine;
using UnityEngine.UI;

public class StoreItemUI : MonoBehaviour
{
    public Text NameText;
    public Text PriceText;
    public Image IconImage;
    public Button PurchaseButton;

    private StoreItem currentItem;

    public void Setup(StoreItem item)
    {
        currentItem = item;
        NameText.text = item.Name;
        PriceText.text = $"{item.Price} {item.CurrencyType}";
        IconImage.sprite = item.Icon;

        if (item.IsLimitedTimeOffer && item.OfferExpiryDate < System.DateTime.Now)
        {
            PurchaseButton.interactable = false;
            PriceText.text = "Expired";
        }
        else
        {
            PurchaseButton.interactable = true;
        }
    }

    public void OnPurchaseButtonClicked()
    {
        Debug.Log($"Attempting to purchase: {currentItem.Name}");
        // Trigger purchase logic
        StorePurchaseHandler.Instance.PurchaseItem(currentItem);
    }
}

Step 5: Handle Purchases

This singleton handles purchase validations and inventory updates.

using UnityEngine;

public class StorePurchaseHandler : MonoBehaviour
{
    public static StorePurchaseHandler Instance;

    private void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
    }

    public void PurchaseItem(StoreItem item)
    {
        if (item.CurrencyType == "Coins" && PlayerData.Coins >= item.Price)
        {
            PlayerData.Coins -= item.Price;
            AddToInventory(item);
            Debug.Log($"{item.Name} purchased successfully!");
        }
        else if (item.CurrencyType == "Gems" && PlayerData.Gems >= item.Price)
        {
            PlayerData.Gems -= item.Price;
            AddToInventory(item);
            Debug.Log($"{item.Name} purchased successfully!");
        }
        else
        {
            Debug.LogError("Insufficient currency!");
        }
    }

    private void AddToInventory(StoreItem item)
    {
        PlayerData.Inventory.Add(item);
    }
}

Step 6: Player Data Structure

using System.Collections.Generic;

public static class PlayerData
{
    public static int Coins = 1000;
    public static int Gems = 50;
    public static List<StoreItem> Inventory = new List<StoreItem>();
}

Step 7: Implement Discounts and Promotions

Add a utility class to manage discounts dynamically.

using UnityEngine;

public class PromotionManager
{
    public static int GetDiscountedPrice(StoreItem item)
    {
        if (item.IsLimitedTimeOffer && item.OfferExpiryDate > System.DateTime.Now)
        {
            return Mathf.RoundToInt(item.Price * 0.8f); // 20% discount
        }
        return item.Price;
    }
}

Update the PriceText in StoreItemUI to reflect discounted prices.


Features of This System

  1. Dynamic Data Fetching: Store items update dynamically from the server.
  2. Multiple Currencies: Supports coins, gems, and more.
  3. Discounts & Promotions: Easily integrate limited-time offers.
  4. Purchase Validation: Ensures fair transactions and inventory updates.
  5. Reusability: Modular and scalable for various projects.

Closing Thoughts

A modular and feature-rich store system is a must-have for modern game developers. The provided implementation ensures you can integrate a flexible and scalable system into any Unity project while maintaining player satisfaction and engagement.

Feel free to enhance it with payment gateway integration (e.g., Google Play or Apple Store) for real-money transactions!

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다