Saturday, January 3, 2015

Tutorial on how to integrate Soomla IAP into Unity3D for your IOS app

Okay, so below is what I learned on how to integrate Soomla In app purchase into my Unity3D project, I try to write it as detail as possible

below link is the official tutorial, you should definitely go through it first
https://github.com/soomla/unity3d-store

Itune Connect Setup:
---------------------------
step1: login to https://itunesconnect.apple.com

step2: Create your app in itune connect, remember the bundle id

step3: Create in app purchase Item
-> say you have 3 in app purchase item, you need to create 3 product ID

step4: Create a sandbox test user in itune connect for you to test the in app purchase



Unity Integration:
------------------------
step1: create a new unity empty project

step2: download and import the Soomla IAP plugin from the unity asset store, its free

step3: drag 2 soomla prefab into your project, "CoreEvents", "StoreEvents"

step4: create 2 c# file
-> one is mymain.cs
-> one is myasset.cs

before we create the file, let me explain as detail as possible


myasset.cs
----------------
here you define your item type,
Soomla prepared 4 type for you to use (you can see the example at MuffinRushAsset.cs)
1.VirtualCurrency
2. VirtualCurrencyPack
3. SingleUseVG
4. LifetimeVG


So for my example,  I am trying to sell 10 gem for $0.99 , 100 gem for $4.99, 1000 gem for $9.99, therefore I am using the "VirtualCurrencyPack"

lets build the code step by step
//////////////////////////////////////////////////////////

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

namespace Soomla.Store.Example 
{
    public class myasset : IStoreAssets
    {
        public int GetVersion() 
        {
            return 0;
        }
        

        public VirtualCurrency[] GetCurrencies() 
        {
            return new VirtualCurrency[]{};
        }
        

        public VirtualGood[] GetGoods() 
        {
            return new VirtualGood[] {};
        }
        

        public VirtualCurrencyPack[] GetCurrencyPacks() 
        {
            return new VirtualCurrencyPack[] {};
        }
        

        public VirtualCategory[] GetCategories() 
        {
            return new VirtualCategory[]{};
        }
    }

}

///////////////////////////////////////////////////////////

above is the asset framework for you to start, you cannot reduce or remove anything or it will return error for you


then below is where you define your virtual currency and your virtual currency pack



///////////////////////////////////////////////////////////////////

/*Static Final Members **/

        public const string CURRENCY_ITEM_ID      = "gems";
        
        public const string TEN_PACK_PRODUCT_ID      = "zombiespitgem1";
        
        public const string HUNDRED_PACK_PRODUCT_ID    = "zombiespitgem5";
        
        public const string THOUSAND_PACK_PRODUCT_ID = "zombiespitgem10";
        
        /*Virtual Currencies **/
        
        public static VirtualCurrency GEMS_CURRENCY = new VirtualCurrency(
            "Gems",                                        // name
            "",                                            // description
            CURRENCY_ITEM_ID                            // item id
            );


        /*Virtual Currency Packs **/
        
        public static VirtualCurrencyPack TENGEM_PACK = new VirtualCurrencyPack(
            "10 Gems",                                   // name
            "Buy 10 gems",                       // description
            "zombie_10",                                   // item id
            10,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(TEN_PACK_PRODUCT_ID0.99)
            );

        public static VirtualCurrencyPack HUNDREDGEM_PACK = new VirtualCurrencyPack(
            "100 Gems",                                   // name
            "Buy 100 gems",                       // description
            "zombie_100",                                   // item id
            100,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(HUNDRED_PACK_PRODUCT_ID4.99)
            );

        public static VirtualCurrencyPack THOUSANDGEM_PACK = new VirtualCurrencyPack(
            "1000 Gems",                                   // name
            "Buy 1000 gems",                       // description
            "zombie_1000",                                   // item id
            1000,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(THOUSAND_PACK_PRODUCT_ID9.99)
            );



///////////////////////////////////////////////////////
So you can see my "VirtualCurrency" is GEMS_CURRENCY, and my 3 "VirtualCurrencyPack" is TENGEM_PACK, HUNDREDGEM_PACK and THOUSANDGEM_PACK

so make sure you put those on top of the "RETURN".....

one thing to notice is your PRODUCT_ID must match 100% in your itune connect PRODUCT_ID

 FINAL myasset.cs look like below
////////////////////////////////////////////////////////////////

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

namespace Soomla.Store.Example 
{
    public class myasset : IStoreAssets
    {
        public int GetVersion() 
        {
            return 0;
        }
        

        public VirtualCurrency[] GetCurrencies() 
        {
            return new VirtualCurrency[]{GEMS_CURRENCY};
        }
        

        public VirtualGood[] GetGoods() 
        {
            return new VirtualGood[] {};
        }
        

        public VirtualCurrencyPack[] GetCurrencyPacks() 
        {
            return new VirtualCurrencyPack[] {TENGEM_PACK,HUNDREDGEM_PACK,THOUSANDGEM_PACK};
        }
        

        public VirtualCategory[] GetCategories() 
        {
            return new VirtualCategory[]{};
        }

        /*Static Final Members **/

        public const string CURRENCY_ITEM_ID      = "gems";
        
        public const string TEN_PACK_PRODUCT_ID      = "zombiespitgem1";
        
        public const string HUNDRED_PACK_PRODUCT_ID    = "zombiespitgem5";
        
        public const string THOUSAND_PACK_PRODUCT_ID = "zombiespitgem10";
        
        /*Virtual Currencies **/
        
        public static VirtualCurrency GEMS_CURRENCY = new VirtualCurrency(
            "Gems",                                        // name
            "",                                            // description
            CURRENCY_ITEM_ID                            // item id
            );


        /*Virtual Currency Packs **/
        
        public static VirtualCurrencyPack TENGEM_PACK = new VirtualCurrencyPack(
            "10 Gems",                                   // name
            "Buy 10 gems",                       // description
            "zombie_10",                                   // item id
            10,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(TEN_PACK_PRODUCT_ID0.99)
            );

        public static VirtualCurrencyPack HUNDREDGEM_PACK = new VirtualCurrencyPack(
            "100 Gems",                                   // name
            "Buy 100 gems",                       // description
            "zombie_100",                                   // item id
            100,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(HUNDRED_PACK_PRODUCT_ID4.99)
            );

        public static VirtualCurrencyPack THOUSANDGEM_PACK = new VirtualCurrencyPack(
            "1000 Gems",                                   // name
            "Buy 1000 gems",                       // description
            "zombie_1000",                                   // item id
            1000,                                                // number of currencies in the pack
            CURRENCY_ITEM_ID,                        // the currency associated with this pack
            new PurchaseWithMarket(THOUSAND_PACK_PRODUCT_ID9.99)
            );

    }

}



mymain.cs
-----------------
here you will try create 3 variable and few button let you click buy

example


Gems = this variable we use to retrieve how many gems in appstore every 2 seconds
Origem = how many gems in appstore when you first start the app
playergem = how many gems in the iphone now

this is because, let say user buy the "10 gem" pack 5 times, the appstore will record "50 gems", so let say the user spend 48 gems in the game, the user left with only 2 gems

so the variable will display
gems= 50
origems = 50
playergem = 2

then let say the user buy another 10 gems

so variable will display
gems = 60
origems = 50
playergem = 2

then our update function must perform the task

if(gems > origems)
playergems + 10

(meaning the user buy another extra 10 gems and so we need to add it into the playergem)




so lets start with the framework
////////////////////////////
using UnityEngine;
using System.Collections;

public class mymain : MonoBehaviour 
{

    // Use this for initialization
    void Start () 
    {
    
    }
    
    // Update is called once per frame
    void Update () 
    {
    
    }
}

////////////////////
When you first create your mymain.cs, it will look like the above, so lets first add a few button so it look like the screenshot

float originalWidth = 800.0f;  // define here the original resolution
float originalHeight = 600.0f// you used to create the GUI contents
private Vector3 scale;
int gems;


void OnGUI() 
    {
        scale.x = Screen.width/originalWidth// calculate hor scale
        scale.y = Screen.height/originalHeight// calculate vert scale
        scale.z = 1;

        GUI.matrix = Matrix4x4.TRS(Vector3.zeroQuaternion.identityscale);  


        GUI.Box(new Rect(280f40f500f,300f), "");

        GUI.Label(new Rect(300f,50f,1000f,40f),"<size=20>Gems: "+gems+"</size>");

        if(GUI.Button (new Rect (300f,150f,250f,30f), "<color=white><size=20>Buy 10 gem $0.99</size></color>"))
        {

        }
        if(GUI.Button (new Rect (300f,200f,250f,30f), "<color=white><size=20>Buy 100 gem  $4.99</size></color>"))
        {
            
        }
        if(GUI.Button (new Rect (300f,250f,250f,30f), "<color=white><size=20>Buy  1000 gem $9.99</size></color>"))
        {
            
        }

    }


////////////////////
Okay, let me explain the GUI.matrix, because the iphone and ipad has many different kinds of screen resolution, you want your game screen to fit into all kinds of resolution, therefore you must define your original width and original height, and then scale accordingly to different screen sizes

then what i did is to create one variable "gems"
one GUI.box
one GUI.label
three GUI.button

next, we start to add in the soomla integration, so you initialize your asset using the SoomlaStore.Initialize( new myasset());

you can also initialize a few stuff, origems is to get how many gems inside appstore, because maybe the user already bought like 5 times previously so he have a lot of gems in the appstore, but not in his phone because he already used it inside his phone

/////////////////////////////////////
// Use this for initialization
        void Start () 
        {
                    //Handle the initialization of store events

            StoreEvents.OnSoomlaStoreInitialized += onSoomlaStoreInitialized;

            SoomlaStore.Initialize (new myasset());                            //Intialize the store

            origemsStoreInventory.GetItemBalance("gems");   //you want to know how many gems you got in the appstore
            playergemsPlayerPrefs.GetInt("gems");   //you want to know how many gems you got in the playerpref


        }




        public void onSoomlaStoreInitialized() 
        {
           
        }


////////////////////
Then, inside each of your buy button, you just use the function
StoreInventory.BuyItem(item_id);

because my item_id is "zombie_10" (check your item id in myasset.cs)

try and catch function is for you to capture log if error occured

//////////////////////////////////////////
try 
                {
                    StoreInventory.BuyItem ("zombie_10");                            // if success
                    gemsStoreInventory.GetItemBalance("gems");
                } 
                
                catch (Exception e)                                                 // if fail
                {
                    Debug.Log ("SOOMLA/UNITY" + e.Message);                            
                }


////////////////////
Then, inside your update function , you need to check every 2 second if the purchase is successful by using the function StoreInventory.GetItemBalance(itemid)

because my currency is "gems" so i keep checking if the gem inside appstore is larger than the one original in appstore, so i will then add up my playergem and save it into the playerpref

////////////////////
void Update () 
        {
        
            second+=Time.deltaTime;

            //check every 2 second
            if(second>2)
            {
                second=0;
                gemsStoreInventory.GetItemBalance("gems");   //you want to know how many gems you got

                if(gems>origems)
                {
                    origems=gems;
                    if(playerchoice==1)
                    {
                        playergems +=10;
                    }
                    else if(playerchoice==2)
                    {
                        playergems +=100;
                    }
                    else if(playerchoice==3)
                    {
                        playergems +=1000;
                    }



                    PlayerPrefs.SetInt ("gems",playergems);
                }
            }

        }

///////////////////
FULL CODE BELOW
///////////////////



using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using Soomla;

namespace Soomla.Store.Example                                                         //Allows for access to Soomla API                                        
{
    public class mymain : MonoBehaviour 
    {
        float originalWidth = 800.0f;  // define here the original resolution
        float originalHeight = 600.0f// you used to create the GUI contents
        private Vector3 scale;
        int gems;
        int origems//say inside the store have 100 gemyou buy another 10become 110you want to know
        int playergems;  //say you have 2 gems left and you wanna buy another 10

        int playerchoice;  //we want to know player click item 1,2 or 3

        float second;

        private static mymain instance = null;
        //public static List<VirtualCurrencyPackVirtualCurrencyPacks = null;



        /// <summary>
        /// Initializes the game state before the game starts.
        /// </summary>
        void Awake()
        {
            if(instance == null){     //making sure we only initialize one instance.
                instance = this;
                GameObject.DontDestroyOnLoad(this.gameObject);
            } else {                    //Destroying unused instances.
                GameObject.Destroy(this);
            }
            

        }



        // Use this for initialization
        void Start () 
        {
                    //Handle the initialization of store events

            StoreEvents.OnSoomlaStoreInitialized += onSoomlaStoreInitialized;

            SoomlaStore.Initialize (new myasset());                            //Intialize the store

            origemsStoreInventory.GetItemBalance("gems");   //you want to know how many gems you got in the appstore
            playergemsPlayerPrefs.GetInt("gems");   //you want to know how many gems you got in the playerpref


        }




        public void onSoomlaStoreInitialized() 
        {
            //VirtualCurrencyPacks          = StoreInfo.CurrencyPacks;        


        
        }
    



        // Update is called once per frame
        void Update () 
        {
        
            second+=Time.deltaTime;

            //check every 2 second
            if(second>2)
            {
                second=0;
                gemsStoreInventory.GetItemBalance("gems");   //you want to know how many gems you got

                if(gems>origems)
                {
                    origems=gems;
                    if(playerchoice==1)
                    {
                        playergems +=10;
                    }
                    else if(playerchoice==2)
                    {
                        playergems +=100;
                    }
                    else if(playerchoice==3)
                    {
                        playergems +=1000;
                    }



                    PlayerPrefs.SetInt ("gems",playergems);
                }
            }

        }

        void OnGUI() 
        {
            scale.x = Screen.width/originalWidth// calculate hor scale
            scale.y = Screen.height/originalHeight// calculate vert scale
            scale.z = 1;

            GUI.matrix = Matrix4x4.TRS(Vector3.zeroQuaternion.identityscale);  


            GUI.Box(new Rect(280f40f500f,300f), "");

            GUI.Label(new Rect(300f,40f,1000f,40f),"<size=20>Gems: "+gems+"</size>");
            GUI.Label(new Rect(300f,70f,1000f,40f),"<size=20>OriGems: "+origems+"</size>");
            GUI.Label(new Rect(300f,100f,1000f,40f),"<size=20>PlayerGems: "+playergems+"</size>");

            if(GUI.Button (new Rect (300f,150f,250f,30f), "<color=white><size=20>Buy 10 gem $0.99</size></color>"))
            {
                //VirtualCurrencyPack pack1 = VirtualCurrencyPacks[0];                    //define pack1 so that we know user buy this
                playerchoice=1;
                try 
                {
                    StoreInventory.BuyItem ("zombie_10");                            // if success
                    gemsStoreInventory.GetItemBalance("gems");
                } 
                
                catch (Exception e)                                                 // if fail
                {
                    Debug.Log ("SOOMLA/UNITY" + e.Message);                            
                }
            }
            if(GUI.Button (new Rect (300f,200f,250f,30f), "<color=white><size=20>Buy 100 gem  $4.99</size></color>"))
            {
                playerchoice=2;
                try 
                {
                    StoreInventory.BuyItem ("zombie_100");                            // if success
                    gemsStoreInventory.GetItemBalance("gems");
                } 
                
                catch (Exception e)                                                 // if fail
                {
                    Debug.Log ("SOOMLA/UNITY" + e.Message);                            
                }
            }
            if(GUI.Button (new Rect (300f,250f,250f,30f), "<color=white><size=20>Buy  1000 gem $9.99</size></color>"))
            {
                playerchoice=3;
                try 
                {
                    StoreInventory.BuyItem ("zombie_1000");                            // if success
                    gemsStoreInventory.GetItemBalance("gems");
                } 
                
                catch (Exception e)                                                 // if fail
                {
                    Debug.Log ("SOOMLA/UNITY" + e.Message);                            
                }
            }
            if(GUI.Button (new Rect (300f,300f,400f,30f), "<color=white><size=20>DELETE ALL GEMS FROM APPSTORE</size></color>"))
            {
                StoreInventory.TakeItem ("gems",10000);
                origems=StoreInventory.GetItemBalance("gems");
            }

        }





    }
}



step5: drag the mymain.cs into any object in your scene, then you save your scene in unity
-> I drag it into my camera object, but is up to you

step6: build setting -> build
-> make sure app name 100% match ituneconnect
-> make sure bundle id 100% match ituneconnect

step7: open xcode , plug in your iphone, run

step8: in your iphone, setting -> itune & app store -> LOG OUT apple_ID

step9: go to your games, press the buy button

step10: key in the "TEST USER" apple id you created in itune connect
-> make sure there is a word "SANDBOX ENVIRONMENT" so that you dont get charged

step11: make sure everything working as intended

There you go, hopefully this will help you in your project and good luck =)

3 comments:

  1. Wow..... impressive tutorial..... because of explanation.... Thanks.

    ReplyDelete
  2. awesome!u r the greatest!

    ReplyDelete
  3. I still would not quit my job though, as there isn't quite as much money in this as people think. Do it on the side until you are earning enough to quit your job. Or if you are still living with your parents and they support you, you could risk it for a few months and see how it goes.

    ReplyDelete