How-To: Using Statics Events in Unity

Mustafa Pamir Ünsal
4 min readNov 11, 2019

--

Welcome back, last time I wrote about how to clean your design with abstract classes and this time I want explain why I regularly use static events in Unity.

Why Events?

Let’s assume that you want to invoke some methods in run-time. Methods you want, all of those have different purposes in your game mechanics then you sit down and start code some dependent design in your game. Most of us use only Update() in our games and I guess many of you heard that bloating Update() is a bad practice in Unity. However, conditions I am here to explain is not about Update(). Some are about flag, aka bool types, others are about bad design decisions. So, here comes an example;

//Make GameManager Singleton, make it reachable from everywhere
public class GameManager : MonoBehaviour{
public static GameManager Instance;

//Here comes the bad boolean
public bool isGameStart;

private void Awake(){
Instance = this;
isGameStart = false;
}

//Assume there is a button that starts game
public void StartGame(){
isGameStart = true;
}
}

//Maybe, there is a patrolling Enemy
public class Enemy : MonoBehaviour{
private void Update(){
if(GameManager.Instance.isGameStart){
Patrol();
}
}

private void Patrol(){
// Some patrolling behaviour..
}
}

Looks ok isn’t it? I don’t think so, because right now we have a meaningless Singleton and non-functional Update(). So, here is a nice looking example of a bad design and wasteful resource usage. I encourage you to take another look on script and come up with an idea to make it better. If you don't think this example has flaws then I must say you are correct. This approach is so common in Unity programming many of us don't know or step up with better solutions. Still interested in events; come, join me.

Events are based on delegations. When you delegate a function to execute its behavior from different method, you need to invoke delegated ones. There is a simple example of delegation down below.

public delegate void DelegatedMethod();

After this line of code you create a non-return type method with no parameter. Here comes more traditional way to declare a delegate method.

public delegate void DelegateMethod(object sender, EventArgs e);

Generally I don’t use traditional approach because I use events for simple tasks. They look like,

public delegate bool DelegateMethod(Vector3 position)

Usage Scenario

Now, I want to redesign my GameManager and my Enemy classes.

Say Goodbye to Update

I don’t want my Enemy class to have its Update method, yet still I need a repetitive method to run when it's necessary. Thus, I need a Coroutine which is a IENumerator basically. If I put my logic in a infinite loop then cache it with a variable to start and stop it, I'm ready to apply events.

public class Enemy : MonoBehaviour{
private IEnumerator coroutine;

private IEnumerator Patrol(){
while(true){
//Some patrolling behavior..
}
}
}

Enemy class is mostly ready. It needs an event from GameManager to listen and act.

public class GameManager : MonoBehaviour{
public delegate void StartGame();
public static event StartGame OnStartGame;

//Assume there is a button that starts game
public void StartGame(){
//isGameStart = true;
//Instead of booleans now, we can Invoke static event
OnStartGame?.Invoke();
}
}

If you are not familiar with event invokes and some C# syntax then these lines look interesting.

//This line is a simplified version of below
OnStartGame?.Invoke();

//You can write this line like this;
if(OnStartGame != null){
//Invoke() method tells every eventListener to react and execute
OnStartGame.Invoke();
}

After all these changes, we need to listen event in Enemy class. Important note is when you listen an event you need to remove that event from listener, otherwise there will be null-pointer. Because of adding and removing events we need to start and stop our Coroutine in script, so adding a support method would be useful.

public class Enemy : MonoBehaviour{
private IEnumerator coroutine;

//I like to add my events in OnEnable() & remove my events in OnDisable()
private void OnEnable(){
GameManager.OnStartGame += StartPatrol;
}

private void OnDisable(){
if(coroutine != null){
StopCoroutine(corotine);
}
GameManager.OnStartGame -= StartPatrol;
}

//Supportive method for coroutine
private void StartPatrol(){
coroutine = Patrol;
StartCoroutine(coroutine);
}

private IEnumerator Patrol(){
while(true){
//Some patrolling behaviour.
}
}
}

As you can see syntax of adding and removing events are easy to remember. Because of events are static we can access them from another scripts.

In conclusion, yes, we could use static events in Update() method but it would be no useful for this example in my opinion, yet sometimes it is useful when delegating Input based events. I will leave another example down below.

public class InputManager : MonoBehaviour{
private Vector3 initialPosiiton;
private Vector3 holdPosition;

public delegate void Pressed();
public static event Pressed OnPressed;

public delegate void Hold(float distance);
public static event Hold OnHold;

public delegate string Release();
public static event Release OnRelease;

private void Update(){
if(Input.GetMouseButtonDown(0)){
initialPosition = Input.mousePosition;
OnPressed?.Invoke();
}

if(Input.GetMouseButton(0)){
holdPosition = Input.mousePosition;
OnHold?.Invoke(Vector3.Distance(initialPosition, holdPosition));
}

if(Input.GetMouseButtonUp(0)){
var message = OnRelease?.Invoke();
Debug.Log(message);
}
}
}

--

--

Mustafa Pamir Ünsal

I’m a software engineer who works in game development and I’m really passionate about design principles.