We require some packages to install first before we can use Retention package
You'll now have all the necessary files, including 1 example screen and 1 example script which is covering the most common topic of this package. We'll be using only prefabs for our purpose.
Drag the Retention prefab from the Prefabs folder into Hierarchy.
Note: Retention gameobject must be in the first scene of the game. It contains DontDestroyonLoad, so it'll manage all other screens itself.Retention script is attached to the gameobject which contains all the modules required. You can enable/disable any of these module and fill in the data required.
Fill in all the In App Purchase ids, to let it fetch the data when the game starts
All the code is inside FM package
using FM;
All the UI code for Retention package is insize FM.UI package
using FM.UI;
Note: Every scene must have a canvas, where you want to use this package's elements
Enable Achievements and unfold to fill in the information
Size | Count of achievements required in the game |
Name | Name of the achievement |
Achievement Id | Achievement Id (must be same as the achievement id in game services) |
Max Progress | Maximum progress required to complete the achievement (in game case, player has to die 10 and 20 times to complete the achievements) |
To manage the achievements
All achievement progress will be saved offline. And posted to the game services if internet is available. If device is offline, then all the progress will be saved offline and when device will come online, progress will be synced to the game services. To support this, you have to authenticate the user and call sync function.
If you are using google play games, you'll have to authenticate gpg before unity's code.
Example#if UNITY_ANDROID
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().Build();
PlayGamesPlatform.InitializeInstance(config);
// recommended for debugging:
PlayGamesPlatform.DebugLogEnabled = true;
// Activate the Google Play Games platform
PlayGamesPlatform.Activate();
#endif
Social.localUser.Authenticate((bool status) => {
if(status){
Achievements.SyncWithGameService();
}
});
In order to complete the achievements you'll have to call an inbuilt function with your action functions.
ExampleAchievements.IncrementProgress("CgkI2Zbnhb0UEAIQBA", 1);
Call this function everytime your player dies. Eventually when your player died 10 times, this achievement will unlock
Same goes for the other actions, for example one if achievement says to complete 5 jumping kills, you have to call the this function with corresponding achievementId and 1 in incrementBy whenever player kills an enemy while jumping.
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
public void SyncWithGameService();
Sync unsynced achievement progress, which is saved offline with game services on the device when the device comes online.
Always use after authenticating the user.
Social.localUser.Authenticate((bool status) => {
if(status){
Achievements.SyncWithGameService();
}
});
public void IncrementProgress(string achievementId, float incrementBy);
achievementId | Achievement Id used in game services and added in the inspector window |
incrementBy | Increase achievment progress by the value |
Increment progress of a given achievement using its achievementId.
Use the same achievementId used on game services (Game Center, Google Play Games).
If maximum progress is 10 for the given achievement, then increment it by 1 will increase 10% of the achievement.
Achievements.IncrementProgress("die_10_times", 1);
public Achievement GetAchievementInfo(string achievementId);
achievementId | Fetch achievement info using achievement id |
Fetch single achievement info using achievementId.
Achievement ach = Achievements.GetAchievementInfo("die_10_times");
string name = ach.name;
float progress = ach.progress;
float maxProgress = ach.maxProgress;
bool syncedWithGameService= ach.syncedWithGameService;
Enable Character Selection and unfold to fill in the information.
Drag "CharacterSelectionPanel" from Prefabs folder to the Canvas in order to use it.
Size | Count of characters required in the game |
Character Id | id for the character, which will be used for purchasing/unlocking it |
Character Image | Character Image to show on the selection panel |
Currency Id | Character can be purchased by given currency id |
Character Amount | GIven currency amount to be spend on the character |
Unlocked | Unlocked for user to be used |
Name | Name of the character |
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
public Character[] GetAllCharacters();
Fetch info of all the Characters
Character[] characters = CharacterSelection.GetAllCharacters();
Character c = characters[0];
int indexInInspector = c.index;
string characterId = c.characterId;
Sprite characterImage = c.characterImage;
string currencyId = c.currencyId;
int characterAmount = c.characterAmount;
bool unlocked = c.unlocked;
string name = c.name;
public Character GetCharacter(string characterId);
characterId | use characterId to fetch the info for a single character |
Fetch single character info using characterId.
Character c = CharacterSelection.GetCharacter("character01");
int indexInInspector = c.index;
string characterId = c.characterId;
Sprite characterImage = c.characterImage;
string currencyId = c.currencyId;
int characterAmount = c.characterAmount;
bool unlocked = c.unlocked;
string name = c.name;
public List<string> GetLockedCharacters();
Fetch ids of all locked characters.
List<string> lockedCharacters = CharacterSelection.GetLockedCharacters();
public List<string> GetUnlockedCharacters();
Fetch ids of all unlocked characters.
List<string> lockedCharacters = CharacterSelection.GetUnlockedCharacters();
public void BuyCharacter(string characterId, System.Action<bool, string> callback);
characterId | pass characterId of the character to buy using it's amount and currency |
callback | call to recieve status and message after buying the character |
Buy a character using it's currency and amount specified in inspector window.
CharacterSelection.BuyCharacter("character01", (bool status, string msg) => {
if(status){
// Do someting
}else{
Toast.Show(msg, 1.5f); // toast is included in the asset.
}
});
public bool UnlockCharacter(string characterId);
characterId | use characterId to unlock a particular character |
Unlock a character using it's characterId without spending any money on it.
bool status = CharacterSelection.UnlockCharacter("character01");
public void EquipCharacter(string characterId);
characterId | use characterId to equip an unlocked character |
Equip an unlocked character to use in game using it's characterId
CharacterSelection.EquipCharacter("character01");
public Character GetEquippedCharacter();
Get info of current equipped character in the game
Character character = CharacterSelection.GetEquippedCharacter();
string id = character.characterId;
Sprite image = character.characterImage;
string currencyId = character.currencyId;
int amount = character.characterAmount;
bool unlocked = character.unlocked;
string name = character.name;
int indexInInspector = character.index;
public bool IsCharacterUnlocked(string characterId);
characterId | use characterId to check if it's unlocked or not |
Check if a character is unlocked or not using it's characterId
bool unlocked = CharacterSelection.IsCharacterUnlocked("character01");
Enable Collectibles and unfold to fill in the information.
Drag "CollectiblesPanel" from Prefabs folder to the Canvas in order to use it.
Level Up | Unlock a collectible when user levelled up |
Achievement Unlock | Unlock a collectible when an achivement is unlocked |
Challenge Complete | Unlock a collectible when a daily challenge is completed |
Random | Unlock collectible after a random interval between given minimum and maximum values |
Interval | Unlock collectible after a constant interval. e.g. 5. When above given states occorred 5 times, a collectible will unlock. Same will go for random. |
Array Sequence | Unlock collectible in the same order as given array |
Random Sequence | Unlock collectible in random order from the given array |
Size | Count of collectible items for the game |
Id | Id of a collectible |
Name | Name of collectible |
Image | Image of collectible |
Unlocked | Unlock a collectible to provide it to user before they even start playing the game |
Add an event listener to get notified when user receives a new collectible. This has to be done when game/scene is started where you want to show user.
Collectibles.AddOnCollectibleUnlockListener((string collectibleId) => {
// this is default panel to be shown to user. you can use you custom panel too.
FM.UI.Collectibles colPanel = collectiblesPanel.GetComponent();
colPanel.selectedCollectibleId = collectibleId;
colPanel.newCollectibleUnlocked = true;
collectiblesPanel.SetActive(true);
});
To unlock a collectibe, send it's corresponding action by which it could be unlocked. e.g. put this code in every level up, achievement unlock and challenge complete. According to provided conditions which include interval and actions collectible will be unlocked. This will be taken care in the function itself, you don't have to manage the conditions again.
Collectibles.UnlockAction(Collectibles.LevelUp);
Collectibles.LevelUp | level_up |
Collectibles.AchievementUnlock | achievement_unlock |
Collectibles.DailyChallenge | daily_challenge |
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
public void AddOnCollectibleUnlockListener(System.Action<string> callback);
callback | get callback when a new collectible is unlocked. receive characterId in the callback |
Add a callback listener to get notified when a new collectible is unlocked
Collectibles.AddOnCollectibleUnlockListener((string collectibleId) => {
// use collectibleId to perform anything or to fetch collectible info
});
public Collectible[] GetAllCollectibles();
Fetch info of all the collectibles
Collectible[] collectibles = Collectibles.GetAllCollectibles();
Collectible coll = collectibles[0];
string id = coll.id;
string name = coll.name;
Sprite image = coll.image;
bool unlocked = coll.unlocked;
int indexInInspector = coll.index;
public Collectible GetCollectible(string collectibleId);
collectibleId | pass collectibleId to fetch its info |
Get info of a particular collectible using its id
Collectible coll = Collectibles.GetCollectible("collectible01");
string id = coll.id;
string name = coll.name;
Sprite image = coll.image;
bool unlocked = coll.unlocked;
int indexInInspector = coll.index;
public List<string> GetLockedCollectibles();
Fetch ids of all locked collectibles.
List<string> collectibles = Collectibles.GetLockedCollectibles();
public List<string> GetUnlockedCollectibles();
Fetch ids of all unlocked collectibles.
List<string> collectibles = Collectibles.GetUnlockedCollectibles();
public void UnlockAction(string action);
action | send action which will unlock a collectible in a given condition |
Unlock colectible using action and condition specified in inspector. Send action everytime, interval and action type will be taken care inside the function
Collectibles.UnlockAction(Collectibles.AchievementUnlock);
Collectibles.LevelUp | level_up |
Collectibles.AchievementUnlock | achievement_unlock |
Collectibles.DailyChallenge | daily_challenge |
public void UnlockCollectible(string collectibleId)
collectibleId | use collectibleId to unlock corresponding Collectible Item. |
Unlock collectible using its collectibleId
Collectibles.UnlockCollectible("collectible01");
Enable CurrencyManager and unfold to fill in the information
Size | Count of total currencies used in the game including in app purchases. |
IAP | Declare whether this currency is IAP or In game currency. |
Name | Name of the currency |
Id | Id of the currency (in case of IAP, this must match the product id in app store/ play store) |
Single Image | Image to be shown as single currency |
Bundle Image | Image to be show as bundle of this currency |
There are 3 packages, by using them user can buy currency. These packages can be other in game currency or IAP. There are constant 3 packages, because it matches the perfect number which is useful for marketing purpose. More or less than 3 can ruin the UX and UI. If you want it to be vary, you can suggest changes to the developer.
PackagesBuy Quantity | Quantity of the above currency to buy using this package |
Currency Spend ID | Currency Id by which above currency will be bought |
Currency Spend Quantity | Currency amount in the package to be spend to buy above currency |
There is a default UI provided in the package. Which you can't drag in the inspector. Rather use it through the inbuilt code.
CurrencyManager.ShowBuyCurrencyUI("igc_coin");
You can add/subtract currency using AddQuantity if required. It can be useful if you want to play video ad to reward currency
CurrencyManager.AddQuantity("igc_coin", 20);
You can use "CurrencyQuantityPanel" to show currenct currency quantity. Fill in the currency id and get currency value in the panel
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
public void AddOnCurrenciesChangeListener(System.Action<IGCurrency[]> callback);
callback | get notified and fetch all the currency info after a change (spend or buy) in any currency |
Get notified if any currency is spent or bought anywhere in the game
CurrencyManager.AddOnCurrenciesChangeListener((IGCurrency[] changedCurrencies) => {
IGCurrency curr = changedCurrencies[0];
bool isIAP = curr.iap; // is in app purchase or local currency
string name = curr.name;
string currencyId = curr.id;
Sprite bundleImage = curr.bundleImage; // image of currency shown for bundle
Sprite singleImage = curr.singleImage; // image of currency shown single
int quantity = curr.quantity;
BuyPackage[] packages = curr.packages; // packages to buy this currency
});
public IGCurrency GetCurrencyDetails(string currencyId);
currencyId | use currencyId to fetch currency details |
Fetch single currency details using currencyId
IGCurrency currency = CurrencyManager.GetCurrencyDetails("currency01");
public void BuyCurrency(string currencyId, int packageIndex, Callback<bool, IGCurrency, IGCurrency> callback);
currencyId | buy currency using currencyId |
packageIndex | use packageIndex to buy currency using selected package. This can be IAP or other local currency. |
callback | Get status, boughtCurrencyDetails, spentCurrencyDetails in callback |
Buy currency (using currencyId) using its one of the curresponding packages. Spend currency can't be same as buying currency. And spend currency quanity must be greater than or equal to buying currency
CurrencyManager.BuyCurrency("currency01", 0, (bool OnComplete, IGCurrency boughtCurrency, IGCurrency spentCurrency) => {
Toast.Show(boughtCurrency.name + " Bought", 2f);
});
public void AddQuantity(string currencyId, int quantity);
currencyId | use currencyid corresponding to the currency you want to add |
quantity | Add/Subtract quantity |
Add/Subtract a currency quantity without spending any other currency in the game or using IAP. This can be useful when adding currency using ads.
Add:
CurrencyManager.AddQuantity("currency01", 10);
Subtract:
CurrencyManager.AddQuantity("currency01", -20);
public string LocalizedPrice(string iapId);
iapId | use in app purchase id same as in store page and inspector to fetch its localized price |
Fetch localized price of given in app purchase item by providing its id.
string price = CurrencyManager.LocalizedPrice("iap01");
public void ShowBuyCurrencyUI(string currencyId);
currencyId | use currencyId to open corresponding currency purchase panel |
Open currency purchase panel using currencyId
CurrencyManager.ShowBuyCurrencyUI("currency01");
Enable DailyChallenges and unfold to fill in the information
Drag "DailyChallengesPanel" from Prefabs folder to the Canvas in order to use it.
There are two method by which you can show 3 daily challenges to user. Again 3 is the perfect number according to the resaearch. If you want it to be dynamic, contact developer. Functioning of Daily Challenges is explained below:
You have to make an API in which there will be new 3 daily challenges provided daily. And API's output format must be in JSON. Here is an example API provided
Example JSON File{
"challenges":[
{
"challengeId":"test_1",
"name":"Test 1",
"actionId": "die1",
"maxProgress":"10",
"rewardId":"igc_gem",
"rewardAmount":"1"
},
{
"challengeId":"test_2",
"name":"Test 2",
"actionId": "die2",
"maxProgress":"10",
"rewardId":"igc_gem",
"rewardAmount":"1"
},
{
"challengeId":"test_3",
"name":"Test 3",
"actionId": "die3",
"maxProgress":"10",
"rewardId":"igc_gem",
"rewardAmount":"1"
}
],
"startDate": "1"
}
An array challenges is required with 3 objects in it. and a startDate string. It must be increased everyday with every new version of challenges.
challengeId | Id of challenges by which you'll identify, skip it |
name | Name of the challenge |
actionId | Action by which this challenge will proceed. e.g. Player have to die 10 times to complete this challenge. Use DailyChallenges.Action("die1", 1); everytime when player die. And this challenge's progress will increment |
maxProgress | Maximum progress of the challenge |
rewardId | You'll give a reward to user when they complete a challenge. It must be in game currency. Enter the id of the currency here. |
rewardAmount | Amount of the currency will be awarded after completing this challenge |
Everytime if user completes their challenge it'll be marked as completed. But if they complete the challenge next day and new challenges are available then the completed/skipped challenge will be replaced by the new challenge available
If you don't want your game to communicate through network and want everything offline then Local File is a great solution for Daily Challenges.
In a text file you'll have to create a json, which contains all the action and challenges that can occur in future and add this file in the slot File instead of File Url.
Length of array in the file must be greater that 6 if you want different challenges everytime a user engage with. An example file is provided in the package, you can take a look at that.
Format Explained:{
"challenges":[
{
"challengeId":"test_1",
"name":"Test 1",
"actionId": "die1",
"maxProgress":"10",
},
{
"challengeId":"test_2",
"name":"Test 2",
"actionId": "die2",
"maxProgress":"10",
},
{
...
}
]
}
Each object contains 4 strings smae as Server file. Each string is explained above.
User will engage with new Daily challange everyday and you'll have to complete each action provided by the given functions, which is explained below.
DailyChallenges.Action("die1", 1);
Call this code every time an action occur. e.g. If player has to die 10 times to complete a challenge. Put this code on player's death and increment it by 1 every time. When progress reached to maxProgress, the challenge will complete.
OutputNote: All JSON format must be same as explained above, or there will be errors in parsing the files.
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
public void AddOnTodaysChallengeFetchListener(System.Action<DC> callback);
callback | get callback (contains challenges) when today's challenges are fetched from file or server |
Get notified when a today's challenges are fetched from provided file or from server
DailyChallenges.AddOnTodaysChallengeFetchListener((DC callback) => {
System.Collections.Generic.List challenges = callback.challenges;
DailyChallenge challenge = challenges[0];
string name = challenge.name;
string challengeId = challenge.challengeId;
string actionId = challenge.actionId;
float progress = challenge.progress;
float maxProgress = challenge.maxProgress;
bool completed = challenge.completed;
bool skipped = challenge.skipped;
long startDate = challenge.startDate;
string rewardId = challenge.rewardId;
int rewardAmount = challenge.rewardAmount;
});
public void AddOnChallengeActionDoneListener(System.Action<DailyChallenge, int> callback);
callback | get callback(contains done Challenge and index of challenge in file/server) |
Add this callback to get notified when defined action completes a challenge at the given moment. And receive challenge (DailyChallenge object) and index of challenge in file/server from callback
DailyChallenges.AddOnChallengeActionDoneListener((DailyChallenge challenge, int indexInInspector) => {
string name = challenge.name;
string challengeId = challenge.challengeId;
string actionId = challenge.actionId;
float progress = challenge.progress;
float maxProgress = challenge.maxProgress;
bool completed = challenge.completed;
bool skipped = challenge.skipped;
long startDate = challenge.startDate;
string rewardId = challenge.rewardId;
int rewardAmount = challenge.rewardAmount;
});
public void AddOnNewChallengeArriveListener(System.Action<DailyChallenge, int> callback);
callback | get callback(contains new challenge and index of challenge in file/server) |
Add this callback to get notified when a challenge is completed/skipped and there is a new challenge is available to replace the previous one. Receive challenge (DailyChallenge object) and index of challenge in file/server from callback
DailyChallenges.AddOnNewChallengeArriveListener((DailyChallenge challenge, int indexInInspector) => {
string name = challenge.name;
string challengeId = challenge.challengeId;
string actionId = challenge.actionId;
float progress = challenge.progress;
float maxProgress = challenge.maxProgress;
bool completed = challenge.completed;
bool skipped = challenge.skipped;
long startDate = challenge.startDate;
string rewardId = challenge.rewardId;
int rewardAmount = challenge.rewardAmount;
});
public void AddOnChallengeSkipListener(System.Action<DailyChallenge, int>)
callback | get callback(contains skipped challenge and its index in file/server) |
Add this callback to get notifies when a challenge is skipped. Receive the skipped challenge info and its index in file/server in callback.
DailyChallenges.AddOnChallengeSkipListener((DailyChallenge challenge, int indexInInspector) => {
string name = challenge.name;
string challengeId = challenge.challengeId;
string actionId = challenge.actionId;
float progress = challenge.progress;
float maxProgress = challenge.maxProgress;
bool completed = challenge.completed;
bool skipped = challenge.skipped;
long startDate = challenge.startDate;
string rewardId = challenge.rewardId;
int rewardAmount = challenge.rewardAmount;
});
public void Action(string actionId, int incrementBy)
actionId | actionId to progress an action which will increase the progress of a corresponding challenge |
incrementBy | add quantity to increase |
Call this action in every function which contains action to complete a possible daily challenge. If you want a daily challenge to kill a particular enemy for 3 times. Then call this function on death of that enemy and increase it by 1 every time. State the action in file/server too.
DailyChallenges.Action("action01", 1);
challengeId | skip a challenge using challengeId |
Skip a challenge using challengeId
DailyChallenges.SkipChallenge("challenge01");
Enable Daily Reward and unfold to fill in the information.
Drag "DailyRewardPanel" from Prefabs folder to the Canvas in order to use it.
There can be two types of daily reward which user can receive: In Game Currency and Character(if multiple characters are available in the game). Both will be given to user at a random rate which is provided by you in the inspector.
In Game CurrencyUse currency for daily reward | Enable currency as daily reward |
Currency Reward Random Percentage | Percentage/probability of currency to be received as daily reward by the user. |
Size | Count of in game currencies and their probabilty to be received in daily reward |
Id | Id of currency |
% | Probability/Percentage of the currency to be received in daily reward. |
Random Qty. | Quantity of currency to be given random or constant. |
Min | If qty is random, you have to provide minimum quantity that can occur in random value. If not random, Min will be equal to Max. |
Max | Provide maximum quantity that can occur in random value. |
Multiple Of | Make round off to a random value generated. e.g. 456 will become 500 if multiple is 100. |
Use character for daily reward | Enable character as daily reward |
Character Reward Random Percentage | Percentage/probability of character to be received as daily reward by the user. |
DailyRewards.AddOnRewardReceiveListener(() => {
dailyRewardPanel.SetActive(true);
});
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
callback | callback to receive user's today reward |
Add this callback at start of the game, so that user will receive today's reward and show a hidden panel or execute anything.
DailyRewards.AddOnRewardReceiveListener(() => {
dailyRewardPanel.SetActive(true);
});
Fetch today's reward info ready for player
Reward reward = DailyRewards.GetReward();
int rewardType = reward.rewardType;
int rewardQuantity = reward.rewardquantity;
string rewardRelativeId = reward.rewardRelativeId;
string dayNumber = reward.dayNumber;
if(rewardType == Reward.CURRENCY){
// reward is currency
string currencyId = rewardRelativeId;
}else if(rewardType == Reward.CHARACTER){
// reward is character
string characterId = rewardRelativeId;
}else{
// no reward received yet
}
Claim today's reward if any
DailyRewards.ClaimReward();
Enable Player Level and XP and unfold to fill in the information.
Drag "PlayerXPPanel" from Prefabs folder to the Canvas in order to use it.
XP First Level | Xp required to level up for the first time |
Level Upgrade Multiplier | It'll be multiplied by previous XP required for level up for the next level. e.g. If player required 1000 XP to reach Level 2, they'll require 1500 XP to reach Level 3. |
PlayerLevel.AddOnXpChangeListener((LevelInfo levelInfo) => {
if(levelInfo.levelUp){
playerXpPanel.SetActive(true);
playerXpPanel.GetComponent().UpdateUI(levelInfo);
}
});
You'll require to add code PlayerLevel.AddXP to give XP to the player after any action or game.
PlayerLevel.AddXP(200);
You can use "PlayerLevelPanel" to show current XP and level of player everytime
Note: These are the mendatory steps in order to implement this module, if you want to customize anything using inbuilt functions, you can read further functions inside it. Or ignore them, if you are using default UI provided in the package and skip to the next module.
callback | get player's level info when notified on xp change |
Add this callback to get player's level information including it's xp and level when player's xp is changed
PlayerLevel.AddOnXpChangeListener((LevelInfo li) => {
int currentXp = li.currentXp;
int level = li.playerLevel;
int nextLevelXp = li.nextLevelXP; // total xp required to level up
bool levelUp = li.levelUp; // player levelled up or not
int oldXp = li.oldXP; // if player is levelled up, you'll receive old xp and current xp will have latext xp.
int xpGained = li.xpGained; // increased xp.
});
xp | pass amount to xp to increase |
Increase player's XP according to gameplay
PlayerLevel.AddXP(20);
Get player's currency level and XP info.
LevelInfo li = PlayerLevel.GetLevelInfo();
int currentXp = li.currentXp;
int level = li.playerLevel;
int nextLevelXp = li.nextLevelXP; // total xp required to level up
bool levelUp = li.levelUp; // player levelled up or not. false in this case
int oldXp = li.oldXP; // same as currentXp in this case
int xpGained = li.xpGained; // increased xp. 0 in this case
Show a small notification for any status to update the user with latest info. This is using "ToastPanel". You can change the panel prefab as you want.
Toast.Show("text toast", 2f);