Unity Achievements System Tutorial

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.

 

[caption id=”attachment_2197” align=”alignleft” width=”365”]Tutorial02 Achievement Unlocked![/caption]

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. ~~~ c#
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<Text>().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. ~~~ c#
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 >= 10 && !achievement01Unlocked) { StartCoroutine(PanelCall()); achievement01Unlocked = true; } }

private IEnumerator PanelCall() { float perc = currentLerpTime / lerpTime; panelAchievements.SetActive(true); while (currentLerpTime < 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!