Unity advanced 3. Delegates and events
Delegates and events
Delegates
- Delegate: a container for a function that can be passed around or used like a variable
- variables only contain data, but delegates can contain functions
- let’s create a delegate signature - a reference for a type of delegate
- you can declare its return type and parameter types:
public delegate void OnGameOver(int level); public static OnGameOver onGameOver;
- you can declare its return type and parameter types:
Delegate example: change between two active attacks
public class DelegateExample : MonoBehaviour
{
delegate void MyDelegate();
MyDelegate attack;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
if (attack != null)
attack();
if (Input.GetKeyDown(KeyCode.Alpha1))
attack = PrimaryAttack;
if (Input.GetKeyDown(KeyCode.Alpha2))
attack = SecondaryAttack;
}
void PrimaryAttack()
{
// Primary attack
}
void SecondaryAttack()
{
// Secondary attack
}
}
Multicasting
- Multicasting with
+=
- Learn: Multicasting
delegate void MyDelegate(); MyDelegate attack; void Start() { attack += PrimaryAttack; attack += SecondaryAttack; }
- Learn: Multicasting
- Now both PrimaryAttack and SecondaryAttack trigger when attack is called.
Events
- Observer pattern
- Events are specialized multicast delegates
- Can only be triggered from within their own class, not from elsewhere
- Learn: Events
Events example
public class Player : MonoBehaviour
{
public void Start()
{
PlayerHealth.onGameOver += RestartGame;
}
private void RestartGame()
{
// do stuff
}
}
public class PlayerHealth : MonoBehaviour
{
public delegate void OnGameOver();
public static event OnGameOver onGameOver;
}
Actions
- It can sometimes be inconvenient to declare a new delegate type every time you want to use one
- Especially if all you want to do is create a basic event
- Actions allow you to use a generic delegate type without needing to define it in your script first
// this... public static event Action OnGameOver; // ...is basically the same as this public delegate void OnGameOver(); public static event OnGameOver onGameOver;
- Adding parameters
public static event Action<string> OnGameOver; public static event Action<float, bool> OnPlayerHurt;
- Calling with parameters
public static event Action<string> OnGameOver; public void TakeDamage(float damage) { health -= damage; if(health < 0) { OnGameOver?.Invoke("The game is over"); } }
UnityEvents
- To confuse matters further, Unity has its own UnityEvent system as well.
- Good stuff
- You won’t need to nullcheck UnityEvents.
- Unity Events have special controls in Inspector
- Contains the list of event function calls
- Add function calls by drag-and-dropping
- Thus, extremely useful for making logical connections between scripts in the Inspector
using UnityEngine;
using UnityEngine.Events;
public class PlayerHealth : MonoBehaviour
{
float health=100;
public UnityEvent onPlayerDeath;
public void TakeDamage(float damage)
{
health -= damage;
if (health < 0)
{
onPlayerDeath.Invoke();
}
}
}
UnityEvents with parameters
using UnityEngine.Events;
using System;
[Serializable]
public class FloatEvent : UnityEvent <float> { }
UnityEvents Example
using UnityEngine;
using UnityEngine.Events;
public class PlayerHealth : MonoBehaviour
{
float health=100;
public UnityEvent onPlayerDeath;
public FloatEvent onPlayerHurt;
public void TakeDamage(float damage)
{
health -= damage;
onPlayerHurt.Invoke(damage);
if (health < 0)
{
onPlayerDeath.Invoke();
}
}
}
public class HealthBar : MonoBehaviour
{
public void UpdateHealthBar(float value)
{
Debug.Log(value + " health was removed");
}
}
- Bad stuff
- Hooking up scripts in the Inspector requires you to make a manual connection which may not work well for different objects in the scene, especially if they’re created as the game runs.
- When connecting events between unrelated objects, you may find it more useful to use event delegates instead.
- To overcome this, there’s the Scriptable Object Unity Event :)))))))
Scriptable object Unity Event
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName ="Game Event")]
public class GameEvent : ScriptableObject
{
private List<GameEventListener> listeners = new List<GameEventListener>();
public void TriggerEvent()
{
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventTriggered();
}
}
public void AddListener(GameEventListener listener)
{
listeners.Add(listener);
}
public void RemoveListener(GameEventListener listener)
{
listeners.Remove(listener);
}
}
using UnityEngine;
using UnityEngine.Events;
public class GameEventListener : MonoBehaviour
{
public GameEvent gameEvent;
public UnityEvent onEventTriggered;
void OnEnable()
{
gameEvent.AddListener(this);
}
void OnDisable()
{
gameEvent.RemoveListener(this);
}
public void OnEventTriggered()
{
onEventTriggered.Invoke();
}
}