How to put upgradeable items in scene manually?



  • I need to show button for upgradeable item (with automatic replacing next level upgrades etc) in menu and in the
    shop.

    Is there simple way on how to do this?
    Thanks a lot!



  • The simple way is to use the ShopManager, which instantiates and handles the IAPItem.

    The manual way is to make use of our scripting reference and get/set the upgrade product identifiers manually on your button.

    static string GetCurrentUpgrade (string productId)
    Returns the last purchased upgrade id of a product, or the main product itself if it hasn't been purchased yet.

    static string GetNextUpgrade (string productId)
    Returns the next unpurchased upgrade id of a product.



  • Hi Baroni,
    thank you for your answer. Yes the ShopManager worked flawlessly for list of items, which are configured in groups with prefabs and Content in IAP settings.
    But I would like to know if there is easy way (like creating prefab which I could only drag and drop to desired multiple position(s) in scene) to do this. Or I just have to do it manual way.



  • So I tried to use your suggested methods with IAPManager.PurchaseProduct(). The visuals are not refreshing automatically as like it's happening in the linked container (I removed group container and prefab link from IAP Settings just to be sure).

    My upgradeable ids:
    "id_scanner_0" -> next upgrade set to "id_scanner_1"
    "id_scanner_1" -> next upgrade set to "id_scanner_2"
    "id_scanner_2" -> next upgrade set to "id_scanner_3"
    "id_scanner_3" - > empty next upgrade

    When it's already upgraded to max, the GetNextUpgrade method is returning "id_scanner_3" and it's not activating "sold" gameobject (I think).

    Here is the code:

    public class CustomIAPButton : MonoBehaviour
    {
    
        public IAPItem iapItem { get; set; }
        // Start is called before the first frame update
    
        void Start() {
            iapItem = GetComponent<IAPItem>();
            handleUpgrades();
            IAPManager.purchaseSucceededEvent += HandleSuccessfulPurchase;
        }
        
        //calling from button's OnClick()
        public void purchaseNext() {
            string nextUpgrade = IAPManager.GetNextUpgrade("id_scanner_0");
            Debug.Log($"+++++++++++Purchasing next id: {nextUpgrade}");
            IAPManager.PurchaseProduct(nextUpgrade);
        }
    
        public void HandleSuccessfulPurchase(string id) {
            if (IAPManager.isDebug) Debug.Log("CustomIAPButton reports: HandleSuccessfulPurchase: " + id);
    
            switch (id) {
                case "id_scanner_1":
                case "id_scanner_2":
                case "id_scanner_3":
                    handleUpgrades();
                    break;
            }
        }
    
        private void handleUpgrades() {
            List<string> upgrades = IAPManager.GetIAPUpgrades(iapItem.productId);
            if (upgrades != null && upgrades.Count > 0) {
                string currentUpgrade = IAPManager.GetNextUpgrade(iapItem.productId);
    
                if (!string.IsNullOrEmpty(currentUpgrade)) {
                    iapItem.Init(IAPManager.GetIAPObject(currentUpgrade));
                }
            }
        }
    }
    

    This code is working partially. Like I said above, it should be replacing IAPItem and updating visuals automatically right?
    Thanks a lot for your help.



  • Thanks for posting your code, it is now clearer what you are trying to do. Calling IAPItem.Init with the next upgrade identifier does not update the "sold" state on the IAPItem, since it is not accessed in that method. IAPItem.Init is just called for setting text labels and so on. What you are looking for is ShopManager.Refresh() - which actually does a few other things as well.

    I was in the process of clearing up your code, but noticed that when using an IAPItem in the scene, it should already update its visuals automatically. If it doesn't, it could be caused by your code. Please check:

    • that you entered "id_scanner_0" as the productId value on the IAPItem in the scene
    • have a ShopManager prefab instance in the scene (even if you don't assign containers for instantiation)

    With that, if you would like to still have your own button floating around somewhere, all you need would be:

    public class CustomIAPButton : MonoBehaviour
    {
        public IAPItem iapItem { get; set; }
        // Start is called before the first frame update
    
        void Start() {
            iapItem = GetComponent<IAPItem>();
        }
        
        //calling from button's OnClick()
        public void purchaseNext() {
            IAPManager.PurchaseProduct(iapItem.productId);
        }
    }
    

    The callback to ShopManager.Refresh which updates the IAPItem happens automatically on a successful purchase, so you don't need to subscribe to the HandleSuccessfulPurchase event anywhere.



  • Thanks for your answer.

    I tried that simple code you provided at first, but after purchase, it stays on same upgrade level, it's not changing to next product (which is set in requirement window) and it's throwing "PurchaseFailed. Error: Product already owned.".

    Also I noticed that in ShopManager.Init() when it's searching for manually placed items, it's not handling upgrade levels like it's done on line 176. It is a bug?



  • Just tested it out. The issue is with declaring the base product identifier "id_scanner_0" on the IAPItem manually. Our ShopManager only checks the next iteration, not the last upgrade, which in your case would be "id_scanner_1". If you own that product already, it then throws the "Product already owned" message.

    If the IAPItem copies are instantiated by the ShopManager (like in the example scenes), it obviously gets the last upgrade for displaying it in the shop. But not when dragging IAPItems into the scene manually. I have thought about it for a moment, and this is actually the designed behavior. A manual IAPItem should not be overridden in any case. This is also why IAPItem.Init is not called, because it would defeat the purpose of custom visuals in the scene - it would just be overridden with what is entered in the IAP Settings editor.

    To mimic what the ShopManager does, we have to go back to your first code sample.

            public IAPItem iapItem { get; set; }
            // Start is called before the first frame update
    
            void Start()
            {
                iapItem = GetComponent<IAPItem>();
                Update();
    
                IAPManager.purchaseSucceededEvent += HandleSuccessfulPurchase;
            }
    
            public void HandleSuccessfulPurchase(string id)
            {
                if (id == iapItem.productId)
                    Update();
            }
    
            void Update()
            {
                string nextUpgrade = IAPManager.GetNextUpgrade(iapItem.productId);
    
                iapItem.Init(IAPManager.GetIAPObject(nextUpgrade));
                if (DBManager.isPurchased(nextUpgrade))
                    iapItem.Purchased(true);
            }
    
            //calling from button's OnClick()
            public void purchaseNext()
            {
                IAPManager.PurchaseProduct(iapItem.productId);
            }
    

    This forces the IAPItem to get the product details from the IAP Settings editor on the beginning and also updates on each purchase. To repeat, since it replicates exactly what the ShopManager already does when instantiating IAPItems, I would recommend just using that. If you would like to have your own purchase button, you can still keep it, but let the IAPItem untouched.



  • Perfect. Worked like a charm and exactly what I needed :)
    Thank you a lot Baroni, you can close the thread as I don't have any other questions regarding this topic.


Log in to reply