Unityevent

  

Events are a key part of general C# development. In Unity, they can sometimes be overlooked and less optimal options are used. If you’ve never used events, you’ll be happy to know that they’re easy to get started with and add a lot of value to your project architecture.

In this Unity beginners tutorial I explain and show how to use UnityEvents to be notified when something happens like a conversation of two characters is sta.

C# (CSharp) UnityEngine.Events UnityEvent.AddListener - 9 examples found. These are the top rated real world C# (CSharp) examples of UnityEngine.Events.UnityEvent.AddListener extracted from open source projects. You can rate examples to help us improve the quality of examples. This tutorial builds on Tutorial: Viewing a remotely rendered model.; Get started with the Mixed Reality Toolkit (MRTK) The Mixed Reality Toolkit (MRTK) is a cross-platform toolkit for building mixed reality experiences. ScriptableObject is a serializable Unity class that allows you to store large quantities of shared data independent from script instances. Using ScriptableObjects makes it. UnityEvent is mainly used with Unity UI system, because ugui need to serialize callbacks configuration in ui system, such as the button's OnClick callback. Serialization is a most important feature in unity game engine, with c# builtin event system, you can't do serialize.

Before I cover the Event system in detail, I’d like to go over some of the common alternatives you’ll see in Unity projects.

BroadcastMessage

The BroadcastMessagemethod is part of the MonoBehaviour class. It allows you to send a loosely coupled message to all active gameobjects.

BroadcastMessage is simple to use and accomplishes the task of sending a message from one gameObject to another.

The biggest issue with BroadcastMessage is how it refers to the method that will be called. Because it takes a string as the first parameter, there is no compile time verification that the method actually exists.

It can be prone to typos, and it introduces danger when refactoring / renaming your methods. If the method name in the string doesn’t match the method name in your classes, it will no-longer work, and there’s no obvious indication of this beyond your game failing to work properly.

The other parameters are also not tightly coupled, so there’s no parameter verification. If your method required a string and an int, but you call BroadcastMessage with two strings, your call will fail, and again there’s no compile time indication of this issue.

Another bigdrawback to BroadcastMessage is the fact that it only broadcasts to children. For the example given, the UI Text would only receive the message if it’s a child of the player.

This does not work

Update Polling

Another common technique I see in Unity projects is polling properties in the Update() method.

Polling in an Update() method is fine for many things, but generally not the cleanest way to deal with cross gameobject communication.

In this example, we update the text of our UI every frame to match the HP value of the player. While this works, it’s not very extensible, it can be a bit confusing, and it requires us to make variables public that may not really need to be.

It also get a lot messier using Update Polling when we want to only do things on a specific situation. For updating the player HP UI, we may not mind doing it every frame, but imagine we want to play a sound effect when the player takes damage too, suddenly this method becomes much more complicated.

Events

If you’ve never coded an event, you’ve probably at least hooked into one before.

One built in Unity event I’ve written about recently is the SceneManager.sceneLoaded event.

This event fires whenever a new scene is loaded.

You can register for the sceneLoaded event and react to it like this.

Each event can have a different signature, meaning the parameters the event will pass to your method can vary.

In the example, we can see that the sceneLoaded event passes two parameters. The parameters for this event are the Scene and the LoadSceneMode.

Creating your own Events

Unityevent

Now, let’s see how we can build our own events and tie them into the example before.

In this example, we create a new delegate named PlayerTookDamageEvent which takes a single integer for our HP value.

Then we use the delegate to create an event named OnPlayerTookDamage.

Now, when we take damage, our Player class actually fires our new event so all listeners can deal with it how they like.

We have to check our event for null before calling it. If nothing has registered with our event yet, and we don’t do a null check, we’ll get a null reference exception.

Next, we need to registerfor this newly created event. To do that, we’ll modify the PlayerHPBar script like this.

To test our event, let’s use this PlayerDamager.cs script.

This script calls the TakeDamage() method on the Player every 5 seconds.

TakeDamage() then calls the OnPlayerTookDamage event which causes our PlayerHPBar to update the text.

Let’s see how this looks in action.

Example playing at 10x speed

We can see here that the players HP is decreasing and the text is updating.

You may have noticed something strange though. The first value shown is -1. This caught me off guard the first time, but the cause is visible in the code.

Before you continue reading, take a look and see if you can findit.

….

In our Player.cs script, we set the HP to 10 in the Start() method.

Our PlayerDamager.cs script also starts dealing damage in the Start() method.

Because our script execution order isn’t specified, the PlayerDamager script happens to be running first.

Since an int in c# defaults to a value of Zero, when TakeDamage() is called, the value changes to -1.

Fix #1

There are a few ways we can fix this.

We could change the script execution order so that Player always executes before PlayerDamager.

In the Script Execution Order screen, you can set the order as a number. Lower numbered scripts are run before higher numbered scripts.

Fix #2 – Better

While this would work, there’s a much simpler and cleaner option we can use.

We can change the Player.cs script to set our HP in the Awake() method instead of Start().

Awake() is always called before Start(), so script execution order won’t matter.

So now we have our event working, but we haven’t quite seen a benefit yet.

Let’s add a new requirement for our player. When the player takes damage, let’s play a sound effect that indicates that they were hurt.

PlayerImpactAudio

To do this, we’ll create a new script named PlayerImpactAudio.cs

Notice on line 13, we register for the same OnPlayerTookDamage event that we used in the PlayerHPBar.cs script.

One of the great things about events is that they allow multipleregistrations.

Because of this, we don’t need to change the Player.cs script at all. This means we’re less likely to break something.

If you’re working with others, you’re also less likely to need to do a merge with another developers code.

We’re also able to more closely adhere to the single responsibility principal.

The single responsibility principle states that every module or class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. All itsservices should be narrowly aligned with that responsibility.
Robert C. Martin expresses the principle as follows:[1] “A class should have only one reason to change.”

The GameObject & AudioSource

You may have also noticed line 5 which tells the editor that this component requires another component to work.

Here, we’re telling it that we need an AudioSource on the gameobject. We do this because line 11 looks for an AudioSource to play our sound effect from.

For this example, we’ve created a new gameobject and attached both our PlayerImpactAudioSource.cs script and an AudioSource.

Then we need to assign an AudioClip. As you can see in the example, I’ve recorded my own sound effect and named it “oww”.

Now when we hit play, a sound effect triggers every time the TakeDamage() method is called.

If this is all new to you, don’t worry, we’re almost done, and it gets easier.

Actions were added to c# with .net 2.0. They’re meant to simplify events by removing some of the ceremony around them.

Let’s take another quick look at how we defined the event in our Player.cs script.

Unityevent Vs Unityaction

First, we define the event signature, declaring that our event will pass one integer named “hp”.

Then we declare the event so that other code can register for it.

With Actions, we can cut that down to one line.

That’s all there is to it. Nothing else needs to change. All the other scripts work exactly the same. We’ve simply reduced the amount of code needed for the same effect.

While this is great for the majority of events, there is one reason you may want to still use the occasional event. That would be when your event has many parameters that can be easily confused with each other. My recommendation for that situation however is to re-think your events and see if the amount of data you’re passing is larger than it needs to be. If you really need to pass a lot of data to an event though, another great option is to create a new class or struct and fill it with your data, then pass that into the event.

Final Tip

Before I go, it’s also worth mentioning that you can have multiple parameters to an Action. To do this, simply comma separate your parameter types like this.

If you have questions or comments about Events or using Action, please leave a comment or send me an email.

Unityevent

Related

Communication is key. In all aspects of life, problems can often be solved quicker through slick communication between people than raw individual brainpower alone. When making games, you’ll encounter problems which are best solved by maintaining strong separation between the components involved and providing a robust way to send data between them. In this article, we’ll look at messaging and event systems in Unity, discuss why they’re important, and develop smarter ways to send data between objects.

My favourite programming book, hands-down, is Game Programming Patterns by Bob Nystrom, and it’s available for free online. When I started to program games in Unity, there’s a certain amount of organisational discipline enforced by Unity’s component structure, but I still ended up with monolith classes which invariably contained the word Manager or Controller somewhere in the name. This book details many strategies for splitting up classes and decoupling parts of the code from each other - one of those is called the Observer design pattern.

This pattern exists to replace direct function calls with a system where observers (a class which wishes to receive messages) subscribe to a subject (the class sending the messages). In this pattern, a subject doesn’t know any details about who is subscribing to it - it just maintains a list of some Observer type or similar. This is good because the subject can send the same message to any number of observers, and those observers can implement custom event-processing behaviour on their end. Without this pattern, the event-processing logic occurs on the subject’s end, which strongly couples together the subject and all its observers. We’ll go into more detail about this pattern later.

Unity has a built-in method which iterates through all components on a GameObject and invokes all instances of a particular method. The SendMessage method takes the name of the function to invoke as a string parameter, alongside an optional parameter of type object to be used for sending arbitrary data with the message, and a third parameter to control how the messaging system behaves. Here’s a simple example.

Whenever the Attack method is called, Unity will look at the components attached to the attackTargetGameObject, attempt to find a method called TakeDamage accepting a single float as a parameter on each object, and if one exists, invoke (i.e. call) it. The third parameter, which isn’t included here, is an enum called SendMessageOptions with two possible values - RequireReceiver and DontRequireReceiver. When RequireReceiver is used - the default behaviour - the receiving GameObject must have a component attached with a matching method, else an error is logged to the console.

There are two similar variations with the same parameters as SendMessage. Where SendMessage only invokes the method on the singular GameObject it is called on, SendMessageUpwards also send the message to its parent, and its parent’s parent, and so on; BroadcastMessage sends the message to the GameObject plus all its children recursively.

Why is SendMessage useful? Well, it’s easy to understand. You need a message sent somewhere? Just send it! From a “fire-and-forget” perspective, there’s little effort required on the part of the programmer because there’s no need to iterate through the list of components, or through parents and children, to send a whole heap of messages - just make sure the method names and parameters match and you’re good. If one extra data variable seems restrictive, then don’t fret - since it extends object you can send anything, including custom struct data wrappers.

However, it isn’t without drawbacks. SendMessage and its variants all need to search through a potentially huge list of components to find those that contain a matching method, introducing significant amounts of overhead. Worse still, because they use strings for method names, they use reflection to identify matching methods. In this context, reflection is the ability to interact with and modify the type system at runtime - but invoking methods through reflection is slower than calling a method in the normal way. That’s fine if you’re using reflection once or twice, but if you’re frequently using SendMessage then those small performance hits add up. Not only that, but because all of this is happening at runtime, there’s no compile-time error checking at all. That makes it easier for small mistakes like typos in the method name take ages to debug.

Another huge drawback comes courtesy of another feature of reflection: you can call private methods on other objects. In the following scenario, where one object contains this code:

and a second object contains this code:

then the private method on the second object will be called, and privacy is a thing of the past.

A key potential issue with the architecture that we’ve set up is that we’ve coupled together the subject (the GameObject sending the message) with the object it’s sending a message to. In some scenarios, this is fine. Picture two objects, where one has a trigger collider and contains a script intended to send a ‘hit’ message to the other. They might get away with the following methods:

Unityevent With Parameters

In this example, an observer pattern doesn’t make much sense - that would mean all objects capable of being hit need to subscribe to all objects that could hit them. The sender would still need to know which object it hit anyway, so that all other subscribers can ignore the message since they’re not being hit. However, an alternative scenario sees an object updating some UI element when it is hit, such as a health bar. Here, the health bar is just watching the health value of the first object and will spring into action when it changes, but the object shouldn’t need to know that. The object ought to broadcast some message to the wider world mentioning its health has changed without worrying who is listening. That’s where events come in.

In Game Programming Patterns, the Observer chapter opens by mentioning that C# bakes the pattern right into the language through the event keyword. If you needed any indication that Observer is an important design pattern, that’s it. Due to some heavy terminology, C# events can be a bit tricky to get used to if you’ve never used them before, so we’ll start with basic examples and work our way up.

A subject - or publisher as the C# documentation calls it - declares an event. You declare them kind of like a variable - it looks something like this:

Let’s take this apart word by word. The public keyword is self-explanatory - you can also use other access modifiers like private or protected, which work as you’d expect here. If it’s private, for example, then other objects won’t be able to subscribe to this event. The event keyword comes next and, as expected, tells C# that this is an event which can be invoked. The next thing, which in this case is EventHandler, is a delegate method - it’s basically a stand-in that says “any subscribers must subscribe using a method with the same parameter list and return type as me”. It’s difficult to appreciate what’s happening here without seeing examples, so rest assured I’ll go into more detail later. Just remember that in the most basic case, you’ll probably write EventHandler here. The final word is the event’s name, which in our case is HealthChangedEvent. The EventHandler type is contained in the System namespace, so make sure you’re using it.

A publisher is responsible for invoking the event whenever it wants. In our scenario, we want to raise an event whenever a health variable changes. We do this using the Invoke method. For the example, pretend the publisher has some other method which calls OnGetHit at the appropriate time.

See the ?. operator? That’s the null-conditional member access operator, which is an unnecessarily long name for “if not null”; if nobody has subscribed to the event, this null check will fail and the event will not fire. The Invoke method takes the same parameters as the event delegate - in our case, this is EventHandler, which has an (object sender, EventArgs e) signature. Usually, the sender will be the publisher, so we can just use this, and EventArgs is a base type for additional data. For now, we’re including no extra data so we can use EventArgs.Empty.

That’s the publisher sorted out. What about the subscribers? Well, to start off, the subscribers need access to the publisher, at least temporarily. Usually, a publisher-subscriber relationship is one-to-many, so the publisher should be easy to access - we’ll pretend the subscribers already have access in our magical code black box, but in practice you could read up on the Service Locator design pattern (Game Programming Patterns comes to my rescue yet again). This is the inverse of the situation we had when using SendMessage, where the message-sender needed a reference to the message-receiver. To receive events, a subscribing object needs a method with the same signature (except the method name) as the delegate.

The subscriber needs to subscribe to the publisher’s event. This is handled using the += operator, where the thing being “added” is the name of the method we just wrote. You can add several subscribers in this manner - those subscribers could be different instances of the same type as this one, or a mix of other types which have a method which matches the delegate, or you can even define a method on the publisher and subscribe it to its own event. Note that we’re still using a private method, but since this class is responsible for subscribing its own methods, it’s a far cry from the SendMessage approach which could raid another class through reflection. Assume we got the publisher reference by magic elsewhere:

I love the semantics of using this operator to mean “add this method to the event subscriber list”. Similar semantics are used when unsubscribing from an event - use the -= operator.

Unityevent

Basic events are as easy as that! However, you probably noticed we didn’t send any useful data to the subscribers. Let’s look at two ways to send extra data - the “custom delegate” way and the “custom EventArgs” way.

Custom delegates

Typically, this isn’t the way you’ll pass custom data through events (or at least, it’s not the way recommended by the C# docs), but I think it’s worth discussing how delegates work in a bit more detail. The problem we faced was that a basic EventHandler and basic EventArgs were unable to contain custom data, and one solution is to use a custom delegate to replace EventHandler. Let’s discard EventArgs completely.

We define new delegates with the delegate keyword. They are defined similarly to an abstract method; all you need to supply is the method signature - its name, return types and parameter list. In our case, I called it HealthChangedEventHandler so it’s clear what this is for, kept the void return type, and changed the EventArgs in the parameter list to an int called health. I also removed the sender variable because we weren’t using it. Then, we tweak the event definition to use the new HealthChangedEventHandler instead of EventHandler. Finally, we change the invocation from using EventArgs.Empty to using the health value directly.

On the receiving end, we must change the subscribing method’s parameter list to match the new delegate. Now that we have access to the health value, we can also do something with it in the method body.

The C# documentation states that scenarios requiring a custom delegate are rare, but I hope this might clarify what a delegate is, and thereby solidify your understanding of what EventArgs is doing in the original example. It’s just a stand-in for a concrete method elsewhere in code. Now let’s look the other approach.

Custom EventArgs

To send custom data, we’ll need to create a new class which derives from EventArgs - you can put its definition in a separate file or below an existing class. This class holds the event data in the form of member variables; we’re not required to put anything else in here, but we’ll add a constructor.

To use HealthEventArgs, we need to modify the event definition. So far, we’ve used the regular version of EventHandler, but in order to swap out EventArgs can use the generic version instead. I won’t go into too many details about what generics are, but if you’ve ever used a List<Something> then that’s generics in action - we use a type parameter to specify what the List will hold. In our case, we can use EventHandler<HealthEventArgs> and C# will know that the second parameter when we invoke the event needs to be of type HealthEventArgs instead of the regular EventArgs. We’ll also tweak the line where we fire the event - we’ll construct a new HealthEventArgs and use that instead of EventArgs.Empty.

We also need to modify the subscriber’s method again to use HealthEventArgs.

And that’s how to use custom EventArgs. We established in the SendMessage section that there are problems that don’t really fit the Observer pattern, but if you’ve found a problem where you can reasonably choose between normal method calls, SendMessage and C# events, then there are advantages of using C# events. Compared to SendMessage, there is no reflection so it’s much faster, and we can take advantage of compile-time type checking to avoid common errors like misspelling a method name. And compared to a traditional method call, we’ve removed the burden on the publisher to keep a list of all the objects it needs to send messages to; it makes a lot more sense for the subscribers to locate the publisher and register themselves as a listener. With events, a wide range of different classes can become subscribers, as long as they contain a matching method - with direct method calls, all ‘subscribers’ would have to be the same type, or otherwise inherit a common interface. In my opinion, events deal with this more elegantly. The drawback is that they take a bit longer to get used to, but hopefully this tutorial goes some way to alleviating that issue.

The other major drawback of C# events is that they exist solely within code, so they are not quite as friendly for non-programmers. Luckily, C# events are not the only mechanism for event invocation in Unity.

One of the most powerful features of Unity is the ability to assign and modify things in the Inspector. When you create public variables on a script, by default they’ll appear in the Inspector so that they’re easy to change per instance. However, as we saw with C# events, there are some things that don’t translate from code to the Editor very well. That’s where UnityEvents come in. A UnityEvent is a simplified event callback mechanism which still allows you to subscribe and unsubscribe at runtime through code, but also can be exposed to the Inspector so that designers and other non-programmers can assign subscribers in-Editor.

The base UnityEvent takes no parameters, like our first C# event example. The architecture is the same too. The publisher declares a UnityEvent and is responsible for invoking it where appropriate. The class is contained in the UnityEngine.Events namespace, so make sure you’re using it.

On the subscriber’s end, it’s not too different either. If we want to subscribe via code, we’ll change the lines where we subscribe and unsubscribe. Instead of using += and -= operators, we’ll use AddListener and RemoveListener methods respectively.

So far, this is possibly a bit easier to understand than C# events because there are fewer cumbersome keywords to keep track of, but we haven’t gained much else apart from that - until we check the Inspector. We’re met with a window which lets us press the plus arrow to add a new object from the Hierarchy or the Project View, then add a public method - we’ll need to make UpdateHealthBarpublic, then we can add it to the list. Even if we remove the AddListener and RemoveListener calls above, it’s very easy to add this method as a listener.

You can add as many objects and methods as you’d like to this list, and they’ll all get called when the event fires. Now a programmer can set up an event framework like this, and if an extra event needs to be added or an existing one removed from the list at a later date, a designer or artist working away from the codebase can make the change themselves without needing to call upon a programmer to dive behind the scenes.

Custom event data

Of course, we’ve lost the ability to pass custom data through the event. To add this back, we can add up to four type parameters to UnityEvent to specify what kind of data we want to send.

Then on the subscriber, we’ll add back the extra parameter.

We can subscribe via code like before, but this isn’t quite what we want yet because the event has now disappeared from the Inspector. One of the slightly more annoying parts of this approach is that UnityEvent<int>, or indeed any UnityEvent<T>, is no longer serializable, so we’ll need one more step to make it show up in the Inspector again. Luckily, all we need to do is create a tiny class that derives the generic UnityEvent<int> and label it with [System.Serializable].

The HealthChangedEvent type is essentially just a UnityEvent<int>, and by labelling it with the System.Serializable attribute, it should now appear in the Inspector again. This time, when selecting a method from the drop-down, there will be a section at the top with the heading “Dynamic int”, which will list the methods which match the parameter list (in our case, a single int). When one of those is selected, the method will receive the value passed into the Invoke method.

The other useful feature of UnityEvent is that we can also add methods which don’t match the parameter list - they will just fire normally without receiving the data passed into Invoke. You can add up to four parameters by using UnityEvent<int, string>, or UnityEvent<float, int, bool> and so on - just use the “extension class” trick above to make it pop up in the Inspector.

We’ve seen a range of messaging and event systems which can be used in Unity and scenarios where each one might be useful, as well as use cases where they fall down. The backbone of a smoothly running game - and, crucially, an easily maintainable one - is robust communication between the different components, and hopefully with this article you’ll be able to level up your game’s messaging architecture.

Supporters

Support me on Patreon or buy me a coffee on Ko-fi for PDF versions of each article and to access certain articles early! Some tiers also get early access to my YouTube videos or even copies of my asset packs!

Special thanks to my Patreon backers!

Gemma Louise Ilett
Jesper Kuutti
BVR Jack Dixon John Selig Pablo Ruiz
Chris Sims FonzoUA Jason Swearingen Moishi Rand Shaun Wall Udons
Anna Voronova Gabriella Pimenta James Poole Christopher Pereira Zachary Alstadt

And a shout-out to my top Ko-fi supporters!

Hung Hoang Takuya Mysterious Anonymous Person