Unity Achievements System Tutorial Pt.3

Unity Achievements System Tutorial Pt.3

Advertisements

Previous Part

In this part You will learn how to create new scene with panel that will list all achievements in your game, and it will highlight achievements that are unlocked.

 

First you need to prefab the Achievement panel from first tutorial. Than add new new Scene to the project, and create UI panel  and add multiple panels to it. Use Grid Layout to adjust the locations of panels.

Capture2

Capture3.PNG

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Now Add buttons to bots scenes and make them change between scenes. Use the following method. Remember to add scenes to build settings.

public void LoadLevel(int i)
{
  Application.LoadLevel(i);
}

Attach Script with this method to a button on the scene, and select the scene number;

Capture4

Now Let’s create a scrip that will create achievements panels, and check if achievement is is unlocked.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LitJson;
using System.IO;
using UnityEngine.UI;
public class PanelWithAchievements : MonoBehaviour {

 public static Dictionary<string, bool> achievements = new Dictionary<string, bool>();
 private static bool DictCreated=false;
 private List<Database_Achievements.Achievement> databaseAchievements;
 private JsonData AchData;
 public GameObject achPanel;

  void Start()
  {
  Debug.Log("DictCreated" + DictCreated);
  databaseAchievements = Database_Achievements.databaseAchievements;
//Create Panels based on database items.
  InstntiatePanels();
//Set them all to locked as a default
  foreach (var kvp in achievements)
  {
     GameObject achievement = this.transform.FindChild(kvp.Key).gameObject;
     achievement.transform.FindChild("Panel").gameObject.SetActive(true);
  }
//This will highlight unlocked achievements
  UpdateBoard();

  }

  void InstntiatePanels()
  {
  if (this.transform.childCount==0)
{
     foreach (Database_Achievements.Achievement ach in databaseAchievements)
     {
         GameObject newach = Instantiate(achPanel);
         newach.transform.SetParent(this.transform);
         newach.name = ach.Title;
         newach.transform.GetChild(2).GetComponent&lt;Image&gt;().sprite = ach.image;
         newach.transform.GetChild(0).GetComponent&lt;Text&gt;().text = ach.Title;
         newach.transform.GetChild(1).GetComponent&lt;Text&gt;().text = ach.Describtion;
//Condition for adding only new items
         if (!achievements.ContainsKey(ach.Title))
         {
             achievements.Add(ach.Title, false);
         }
      }
    }
   }
void UpdateBoard()
   {
     foreach (var kvp in achievements)
     {
         if (kvp.Value == true)
         {
             GameObject achievement = this.transform.FindChild(kvp.Key).gameObject;
             achievement.transform.FindChild("Panel").gameObject.SetActive(false);
          }
      }
   }
}

I’m using dictionary to store the state of an achievement and i am using name as a key.
This scrip is attached to a panel object on new scene.

In our AchievementPanel Scrip from previous part we add one extra line in CountClicks() Method

void CountClicks()
{
 numberOfClicks++;
 if (numberOfClicks &gt;= 10 &amp;&amp; !achievement01Unlocked)
 {
   PassInformations(1);
   StartCoroutine(PanelCall());
   achievement01Unlocked = true;
//This one here
   PanelWithAchievements.achievements["Explorer"] = true;

 }
}

 

You need to delete all achievements panels from layout, because script will create a new one. Start game on Game Screen and then if achievement will be unlocked go to next scene and see if achievement is highlighted;

 

Tut04

 

To Avoid creating new panels every time site loads. We need to include one condition in Database script.

if (databaseAchievements.Count==0)

This will prevent creating too many objects.

 void ConstructItemDatabase()
 {
     if (databaseAchievements.Count==0)
{
       for (int i = 0; i &lt; AchData.Count; i++)
       {
databaseAchievements.Add(new Achievement((int)AchData[i]["ID"], AchData[i]["Title"].ToString(), AchData[i]["Description"].ToString()));
       }
     Debug.Log("Database Created");
     }
 }

 

 

 

This will probably sum up the topic of achievements. Now you know how to trigger achievements, read data from files and pass information between scenes. If you have any questions write in comments.  Stay Awesome!

 

 

 

 

Unity Achievements System Tutorial Pt.2

Unity Achievements System Tutorial Pt.2

Previous Part

In this part I’ll demonstrate how to read values from JSON files and mapping them into Achievement Class. I’ll refer to scripts from  previous part, so you should have them working already.

TestAch03
Awesome Icon, I know.

Let’s crate JSON file first. If you are not familiar with this type of data, read my article about it here.

[
 {
 "ID":0,
 "Title": "Slayer",
 "Description": "kill 10 Enemies"
 },
 {
 "ID": 1,
 "Title": "Explorer",
 "Description": "Visit another planet"
 },
 {
 "ID" : 2,
 "Title": "First Blood",
 "Description": "Die!"
 },
 {
 "ID": 3,
 "Title": "Collecter",
 "Description": "collect 5 items"
 }
]

In this file we will store information that will be displayed in our achievement panel. We need Title and Description.

 

Next You will need a database of items, Items will be stored in generic List.

We take all vales form JSON file and add 2 new properties State and Sprite, sprite is loaded from array of sprites. Sprites must be sliced first to store them in array.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using LitJson;
using System.IO;

public class Database_Achievements : MonoBehaviour
{ 
  private List<Achievement> databaseAchievements = new List<Achievement>();
  private JsonData AchData;
  // Use this for initialization
  void Awake()
  {
     AchData = JsonMapper.ToObject(File.ReadAllText(Application.dataPath + "/Achievements.json"));

     ConstructItemDatabase();
     Debug.Log(databaseAchievements[1].ID);
  }

  void ConstructItemDatabase()
  {
     for (int i = 0; i < AchData.Count; i++)
     {
databaseAchievements.Add(new Achievement((int)AchData[i]["ID"], AchData[i]["Title"].ToString(), AchData[i]["Description"].ToString()));
     }
  }
public Achievement FetchItembyID(int id)
  {
    for (int i = 0; i < databaseAchievements.Count; i++)
    {
      if (databaseAchievements[i].ID == id)
          return databaseAchievements[i];
    }
     return null;
  }
 public class Achievement
 {
  public string Title { get; set; }
  public string Describtion { get; set; }
  public Sprite image { get; set; }
  public bool active { get; set; }
  public int ID { get; set; }
  private Sprite[] sprites = UnityEngine.Resources.LoadAll<Sprite>("Achievements");
public Achievement(int newID, string name, string desc)
  {
     this.Title = name;
     this.Describtion = desc;
     this.active = false;
     this.image = sprites[newID];
     this.ID = newID;
  }
Achievement(string name)
  {
      this.Title = name;
  }

 }
}

We need a method for finding item in database, we will use FetchItembyID(int id) to find item by int ID number.

We add new method to the AchievementPanel Class.

void PassInformations(int ID)
{
  Database_Achievements.Achievement ach =database.FetchItembyID(ID);
  Debug.Log(ach.ID);
  panelAchievements.transform.GetChild(0).GetComponent<Text>().text = ach.Title;
  panelAchievements.transform.GetChild(1).GetComponent<Text>().text = ach.Describtion;
  panelAchievements.transform.GetChild(2).GetComponent<Image>().sprite = ach.image;
}

This will pass the values from database to UI.

I’m calling it inside existing CountClicks() method.

//We need to add following namespace
using UnityEngine.UI;
//and reference
public Database_Achievements database;
  void CountClicks()
  {
  numberOfClicks++;
    if (numberOfClicks >= 10 && !achievement01Unlocked)
    {
      PassInformations(1);
      StartCoroutine(PanelCall());
      achievement01Unlocked = true;
    }
  }

You need to add reference to the database. You can attach database script to any object, i would crate new empty one. In next part i’ll show you how to create a new scene for collection of all achievements. You will also learn how to pass information between scenes. Stay Awesome!

Unity Achievements System Tutorial

Unity Tutorial- Achievements System, Part 1

In this series of tutorials I will demonstrate how to create a modularized system of achievements. In this part I will show you how to use Coroutines to create linear movement and wait method. For messaging  between object I’ll use EventManager. You can read more about those topics if you are not familiar with it.

 

Tutorial02
Achievement Unlocked!

In first lesson I will demonstrate the basic structure of the system using pre made achievement panel, in next you will learn how to read data from files to set the text and images for individual achievements.

To unlock this achievement player must click button 10 times.

 

 

First lets create an Event Manager for passing messages between button and Achievement panel. I described process of creating this in previous post, here.

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

public class EventManager : MonoBehaviour {
private Dictionary<string, UnityEvent> eventDictionary;
private static EventManager eventManager;
public static EventManager instance
{
  get
  {
     if (!eventManager)
     {
         eventManager = FindObjectOfType(typeof(EventManager)) as EventManager;
         eventManager.Init();
     }
     return eventManager;
   }
 }

 void Init()
 {
     eventDictionary = new Dictionary<string, UnityEvent>();
 }

 public static void StartListening(string eventName, UnityAction listener)
 {
   //We need to crate place in memory for reference to the object
   UnityEvent thisEvent = null;
   if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
   {
       thisEvent.AddListener(listener);
   }
   else
   {
     //If there is no event with this name add new one to dictionary.
      thisEvent = new UnityEvent();
      thisEvent.AddListener(listener);
      instance.eventDictionary.Add(eventName, thisEvent);
   }
 }

 public static void StopListening(string eventName, UnityAction listener)
 {
   UnityEvent thisEvent = null;
   if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
   {
      thisEvent.RemoveListener(listener);
   }
 }

 public static void TriggerEvent(string eventName)
 {
   UnityEvent thisEvent = null;
   if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
   {
       thisEvent.Invoke();
   }
 }
}

This script is attached to an empty game object called Event Manager. Now we will create an action of clicking a button and subscribe counting method to it.

This Script is attached to UI Button, in this Script we trigger our event and also display the number of clicks an the scene.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
public class TESTClick : MonoBehaviour, IPointerDownHandler
{
 int numberofClicks=0;
 public void OnPointerDown(PointerEventData eventData)
 {
   numberofClicks++;
   EventManager.TriggerEvent("buttonClicked");
   this.GetComponentInChildren&lt;Text&gt;().text = numberofClicks.ToString();
 }
}

I’m using OnPointerDown() method to trigger the event. This method is called when player clicks the button, To use it you need to add  IPointerDownHandler Interface. and EventSystem to your script.

Now we finally have Script attached to Panel Game Object.

using UnityEngine;
using System.Collections;

public class AchievementPanle : MonoBehaviour {

private GameObject panelAchievements;
 private int numberOfClicks=0;
 float lerpTime = 3f;
 float currentLerpTime = 0f;
 Vector3 startpPos = new Vector3(425, -470, 0);
 Vector3 endPos = new Vector3(425, -315, 0);
//
 private bool achievement01Unlocked=false;

 void OnEnable()
 {
   panelAchievements = this.gameObject;
   EventManager.StartListening("buttonClicked", CountClicks);
 }
 void OnDisable()
 {
     EventManager.StopListening("buttonClicked", CountClicks);
 }
 void CountClicks()
 {
   numberOfClicks++;
   if (numberOfClicks &gt;= 10 &amp;&amp; !achievement01Unlocked)
   {
     StartCoroutine(PanelCall());
     achievement01Unlocked = true;
   }
 }

 private IEnumerator PanelCall()
 {
  float perc = currentLerpTime / lerpTime;
  panelAchievements.SetActive(true);
  while (currentLerpTime &lt; lerpTime)
  {
     currentLerpTime += Time.deltaTime;
     perc = currentLerpTime / lerpTime;
     panelAchievements.transform.localPosition = Vector3.Lerp(startpPos, endPos, perc);
     yield return true;
  }
  yield return new WaitForSeconds(2);
  panelAchievements.SetActive(false);
  panelAchievements.transform.localPosition = startpPos;
 }
}

In this script we subscribe to the “buttonClicked” Action, And we use Coroutine to initialize panel movement without using Update() Method. Linear transition is described in detains in my previous post, here. In this example I have fixed start and end position which will be changed later.

This method works well for one achievement, but to use more of them in efficient way we need to upgrade the code. In next Part I’ll show you how to read the JSON files to change the text and image an the achievement panel. In future article I’ll show you how to create separate scene for achievements where all achievements will be stored, and unlocked achievements will be highlighted after unlocking. To do this I’ll use Dictionary and List collections. If you have any questions ask in comments or message me on Facebook. Stay Awesome!