Back in the day when I first started programming in Unity (during COVID) this was probably one those hard concepts to wrap my head around. I kept mixing them up, and didn't know which one to use when. When I did a google search I was left even more confused! I thought, hey, now that I actually understand the whole thing; why not write an article about it because it's actually really simple.
So, do you use a UnityAction, or do you opt for a UnityEvent? They sound kind of the same. They show up in similar places, and yet mixing them up in the wrong context can leave you debugging phantom null references for a loooooong time, wondering why your inventory system just... stopped listening.
So, let me try to explain it in my own words and hopefully I can make some sense of it all, including exactly when to use UnityEvent in Unity, and when it's just adding overhead for no reason.
What Exactly Is UnityAction?
UnityAction is basically Unity's fancier name for a delegate (a way of saying, “I don't know which method I'll run yet, but I'll give you a method to run later" - like a placeholder for a method). If you know C#'s built-in System.Action, you already know 90% of what UnityAction does. It's basically a method contract that says "I promise to invoke a void method with these parameters, whenever you tell me to." (I'm terrible with explaining these things in that context, but please bear with me, it'll all become clear).
Let's just take a look at the following code;
public UnityAction onPlayerDeath;
That's it. No Inspector magic, no serialization, just a plain old delegate you assign and invoke from code:
onPlayerDeath += HandleDeath;
onPlayerDeath?.Invoke();
The cool thing is, any script can "subscribe" to it. Once invoked it will "fire" all the scripts. The UI manager can show a death panel, and the audio manager can play a "game-over" sound. Decoupling is the key word here.
Fast, lightweight, and completely invisible in the Inspector. If a designer opens your script expecting to drag in a reference, they'll find... nothing. Because UnityAction doesn't do that. It's a code-only citizen. And by the way, don't forget to unsubscribe from it either to prevent headaches in the future.
Then What Is UnityEvent?
UnityEvent is its fancier, Inspector-friendly cousin. It wraps around delegate-style logic but adds serialization, so it shows up as a little "+" button in the Inspector where you can drag in GameObjects and pick which public method to call, drag 'n drop, no code required.
public UnityEvent onPlayerDeath;
Now you can hook up a death animation, a sound effect, or a "You dEAD lol" UI popup, all without touching a single line of code. That's the whole pitch: UnityEvent turns code-only wiring into visual, drag-and-drop wiring.
Under the hood, though? UnityEvent actually uses UnityAction-style delegates to do its job. So really, UnityAction is the plumbing, and UnityEvent is the nice fixture on top that lets non-programmers turn the tap. You probably guessed which one has better performance...
UnityAction Versus UnityEvent Performance
Here's where it gets a little less "which one is prettier" and more "which one won't tank my frame rate." When it comes to UnityAction vs UnityEvent performance, there's a real, measurable gap and it's not close.
UnityAction (and C# delegates/events in general) are about as fast as function calls get. There's no reflection, no serialization overhead, the compiler basically turns your invocation into a direct method call, nothing really fancy.
UnityEvent, on the other hand, has to do way more work under the hood to support that nice Inspector-driven, drag-and-drop workflow. It uses reflection to invoke persistent listeners assigned in the Inspector, and that reflection comes at a cost. Community benchmarks over the years have generally put it somewhere in the range of 4-7x slower per invocation compared to a plain delegate call... that's a lot, especially if you're developing mobile applications!
Is that going to matter if you're firing an event once when the player opens a menu? Naaah. Nobody's frame rate has ever died from a UI button click. But if you're invoking an event every frame, or hundreds of times a frame, let's say inside a physics loop or a particle callback, now that UnityAction vs UnityEvent performance gap stops being academic and starts being your bottleneck!
Rule of thumb: hot path, high-frequency, called-every-frame stuff use UnityAction or plain C# events. Occasional, designer-facing, called-a-few-times-a-scene stuff go fora UnityEvent.
If you only remember one thing about UnityAction vs UnityEvent performance, make it this: the gap only matters at scale. One UnityEvent call a second won't hurt you. A thousand a frame will!
When To Use UnityEvent, And When Not To
So when should you actually use a UnityEvent? Honestly, this comes down to one question: does a human need to configure this in the Inspector without opening a code editor? Personally I don't use them at all, I rather hard code those things in, but I did think of some ways they could be useful to some;
When to use UnityEvent in Unity:
- UI interactions, like button
onClick, slideronValueChanged, etc, are allUnityEventunder the hood, and for good reason. Designers live in the Inspector, not in Visual Studio. That's why it's probably more a thing for non-coders that work in a team. - Level and quest triggers, perhaps a trigger volume that fires "on player enter," letting a level designer wire up whatever should happen (door opens, cutscene plays, alarm blares) without you writing a new script for every trigger.
- Solo devs prototyping fast ok I'm guilty of this as a one-person team,
UnityEventcan save you some time. Wiring up a temporary sound effect or debug log through the Inspector is often quicker than writing and recompiling code. However, I never ship a game like that.
When NOT to reach for it, which is really just the "when to use UnityEvent in Unity" question flipped inside out - but like I said before, I don't really use it and that has to do with the following reasons:
- Anything performance-sensitive (see above) - I optimize code, not make it slower.
- Purely internal systems where no one but you, the programmer, will ever touch the wiring. If there's no reason for it to live in the Inspector, don't pay the reflection tax for nothing.
- Events that need to pass complex or non-serializable data.
UnityEventplays nicest with simple types.
UnityEvent Versus UnityAction Inspector Workflow: Seeing The Difference
This is really the crux of the whole UnityEvent vs UnityAction Inspector debate, it's not about which one is "better" in a vacuum, it's about who needs to touch the wiring.
Let's say we have two versions of the same health system:
Code-only (UnityAction):
Health script fires onDeath (UnityAction)
→ HandleDeath() subscribed in code
→ No Inspector entry, no drag-and-drop, nothing to see
Inspector-friendly (UnityEvent):
Health script fires onDeath (UnityEvent)
→ Inspector shows onDeath list
→ Drag in AnimatorController → PlayDeathAnim()
→ Drag in AudioSource → PlayDeathSound()
→ Drag in GameManager → ShowGameOverScreen()
See the difference? With UnityAction, everything is invisible unless you're reading the script. With UnityEvent, the entire flow is visible and editable right there in the Inspector. That's the whole UnityEvent vs UnityAction Inspector trade-off in a nutshell: visibility and flexibility for non-coders, versus speed and simplicity for pure code.
If you're a solo dev who's also your own designer (as most of us are), you might think the Inspector angle doesn't matter. It still does. You can make the important stuff visible instead of buried three method calls deep in a script you barely remember writing, that's why it's great for prototyping.
Don't Forget The Generic Versions
Both types come in generic flavors, and this trips people up constantly.
UnityAction<T>is a delegate that takes a parameter, e.g.UnityAction<int> onScoreChanged. Just declare it and go.UnityEvent<T0>you actually have to subclassUnityEvent<T0>to get a serializable version with a parameter, because Unity can't serialize an open generic type directly in the Inspector.
[System.Serializable]
public class IntUnityEvent : UnityEvent<int> { }
public IntUnityEvent onScoreChanged;
Yes, it's a little annoying that you can't just slap <int> on UnityEvent and call it a day like you can with UnityAction , but blame Unity's serializer, not yourself.
The Quick Cheat Sheet
Still not sure which to pick? Ask yourself:
Needs Inspector wiring? UnityEvent fires every frame or in a hot loop. With UnityAction only programmers will ever touch it? UnityAction
Designers or non-coders need to hook things up? UnityEvent
Passing a custom parameter and need it serializable, then use Subclassed UnityEvent<T0>
Wrapping Up
Sooo, at the end of the day, UnityAction and UnityEvent aren't rivals, they're just tools for different jobs and UnityEvent is quite literally built on top of UnityAction-style delegates anyway. Use UnityAction when speed matters and only your code needs to know. Use UnityEvent when the Inspector needs to be part of the conversation. Once that clicks, the "which one do I use" question basically answers itself every time.
No comments:
Post a Comment