| Type | Class |
|---|---|
| Namespace | UnityEngine.Events |
| Variants | UnityEvent<T0> up to UnityEvent<T0, T1, T2, T3> |
| Seen in | Button "On Click ()", Slider "On Value Changed" |
UnityEvent
A UnityEvent is a list of methods to call when something happens. The special part is that you can fill that list in the Inspector by dragging objects and picking methods from a dropdown, the same way a UI Button's "On Click" works. You don't need code to connect them.
A doorbell. The button (your script) doesn't know or care what the bell is wired to. It could be a chime, a light, or a message to your phone. It just rings (Invoke()). Whoever installs it (you, in the Inspector) decides what's connected. This means you can reuse the same button in many places and connect it to different things each time.
Methods
| Method | Returns | Description |
|---|---|---|
Invoke() | void | Calls every connected method (Inspector ones first, then code ones). For UnityEvent<T>: Invoke(value). |
AddListener(UnityAction call) | void | Connect a method from code. |
RemoveListener(UnityAction call) | void | Disconnect a method added with AddListener. |
RemoveAllListeners() | void | Removes all code-added listeners. Does not remove the ones set in the Inspector. |
GetPersistentEventCount() | int | How many listeners were set up in the Inspector. |
PressurePlate.cs.What the Inspector can call
In the dropdown, Unity lists public methods and property setters of every component on the object you dragged in, as long as they take zero or one parameter of a simple type (int, float, string, bool, or a Unity Object type).
- Static parameters: you type a fixed value in the Inspector, e.g. always call
SetActive(true). - Dynamic parameters (only for
UnityEvent<T>): the value passed toInvoke(value)is forwarded. They appear at the top of the dropdown under "Dynamic float" (etc.).
Code Samples
A reusable pressure plate
using UnityEngine;
using UnityEngine.Events;
public class PressurePlate : MonoBehaviour
{
[SerializeField] private UnityEvent onPressed;
[SerializeField] private UnityEvent onReleased;
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player")) onPressed.Invoke();
}
void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player")) onReleased.Invoke();
}
}
In the Inspector, click + under "On Pressed", drag in a door, and choose Door > Open(). Add another entry for a light: GameObject > SetActive (bool) with the box ticked.
An event that passes a value
using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour
{
[SerializeField] private float maxHealth = 100f;
public UnityEvent<float> onHealthChanged; // passes health as a 0-1 fraction
public UnityEvent onDied;
private float health;
void Start()
{
health = maxHealth;
onHealthChanged.Invoke(1f);
}
public void TakeDamage(float amount)
{
if (health <= 0f) return;
health = Mathf.Max(health - amount, 0f);
onHealthChanged.Invoke(health / maxHealth);
if (health == 0f) onDied.Invoke();
}
}
Now drag a UI Slider into "On Health Changed" and choose Dynamic float > value. The bar updates by itself.
Listening from code
using UnityEngine;
public class DeathCounter : MonoBehaviour
{
[SerializeField] private Health health;
private int deaths;
void OnEnable() { health.onDied.AddListener(HandleDied); }
void OnDisable() { health.onDied.RemoveListener(HandleDied); }
private void HandleDied()
{
deaths++;
Debug.Log("Deaths: " + deaths);
}
}
UnityEvent vs. C# event
| UnityEvent | C# event Action | |
|---|---|---|
| Connect in Inspector | Yes | No |
| Speed | Slower (fine for gameplay moments) | Faster |
| Best for | Designer-facing hooks: doors, buttons, triggers, "on death" | Code-to-code events, things fired very often |
| Safety | Anyone with access can call Invoke() | Only the owning class can invoke |
What happens at runtime
- The Inspector connections are saved with the scene/prefab as data (object reference + method name).
- The first time you call
Invoke, Unity looks up those methods by name and caches them. Invokethen calls each listener in order, one after another, on the same frame.- If a listener's object was destroyed, Unity skips it for Inspector connections. Code listeners on destroyed objects can throw errors, which is why you remove them in
OnDisable.
Common Mistakes
- My method isn't in the dropdown. It must be
public, returnvoid(or be a property setter), and have 0 or 1 simple parameter. - Renaming a method breaks the connection. The Inspector shows "Missing". Reconnect it. This is the main downside of Inspector wiring.
RemoveListenerwith a lambda does nothing.AddListener(() => X())andRemoveListener(() => X())are two different lambdas. Store the delegate in a variable, or use a named method.- Invoking every frame. Fine occasionally, but for per-frame values, a C# event or direct reference is lighter.
See also
- Tutorial: Using UnityEvents in a Game
- Tutorial: ScriptableObjects (event channels)
This page is a simplified, AI-assisted interpretation. For the complete and authoritative reference, see the official Unity documentation.