GameEntity
Every script you write derives from GameEntity. It is your script and the entity it's attached to — there's no separate "get the entity this script is on" step.
using JoystickEngine;
public class Player : GameEntity
{
protected override void OnCreate()
{
Log.Info($"{Name} is ready");
}
protected override void OnUpdate(float ts)
{
Translation += new Vector3(1, 0, 0) * ts;
}
}Identity
ID
The entity's unique id, stable for as long as the entity exists. Read-only, and hidden from the inspector — identity is engine state, not something you author.
Name
The entity's name, as shown in the Scene Hierarchy panel. Readable and writable. Reads as an empty string for an entity that no longer exists, which is the cheapest way to check whether a reference you're holding is still alive.
if (other.Name == "Player")
TakeDamage();Enabled
Whether the entity is active. Setting it to false hides the entity and stops its scripts, and it takes the whole child subtree with it — the usual way to hold a pause menu or a boss room in the scene, switched off, until something turns it on.
GameEntity? menu = FindEntityByName("PauseMenu");
if (menu != null)
menu.Enabled = true;Position
Translation
The entity's position as a Vector3. Read and write.
Translation = new Vector3(0, 5, 0);
Translation += new Vector3(move.X, move.Y, 0) * m_Speed * ts;Moving a physics body by hand
Setting Translation on an entity with a dynamic Rigidbody 2D teleports it — it skips collision detection along the way, so it can end up inside a wall. For anything simulated, apply an impulse or set velocity instead. See Rigidbody2DComponent.
There is also a TransformComponent façade whose Translation forwards to exactly this property, for when you want the component shape for consistency:
GetComponent<TransformComponent>().Translation = Vector3.Zero;Hierarchy
Parenting keeps an entity's world position, so attaching something to a parent never makes it visually jump.
Parent
The entity's parent, or null if it's a root entity. Read and write — assigning re-parents the entity.
ChildCount
How many direct children this entity has.
GetChild(int index)
The child at index (0 to ChildCount - 1), or null if out of range.
FindChild(string name)
The first direct child with that name, or null. Direct children only — it doesn't search grandchildren.
GameEntity? fill = FindChild("Fill");FindEntityByName(string name)
Finds an entity anywhere in the scene by name, or null. Cache the result in OnCreate rather than calling it every frame.
private GameEntity? m_Player;
protected override void OnCreate()
{
m_Player = FindEntityByName("Player");
}Components
HasComponent<T>()
Whether this entity has the component.
GetComponent<T>()
The component, or null if the entity doesn't have it. Returns a fresh façade object each call — the façade holds no state of its own, it reads and writes through to the engine — so cache it in a field rather than calling this every frame.
private Rigidbody2DComponent? m_Body;
protected override void OnCreate()
{
m_Body = GetComponent<Rigidbody2DComponent>();
}See Components for everything you can ask for.
As<T>()
Casts this entity to another script class, so you can reach that class's own fields and methods. Returns null if the entity isn't running that script.
protected override void OnTriggerEnter(GameEntity? other)
{
Player? player = other?.As<Player>();
if (player != null)
player.TakeDamage(1);
}Surviving a scene change
Persist()
Moves this entity, and its whole child subtree, into a hidden always-loaded scene that survives every future Scenes.Load. This is how a music player, a save manager or a singleton controller outlives a level change. Calling it again once already persisted does nothing.
public class AudioManager : GameEntity
{
protected override void OnCreate() => Persist();
}A persisted entity becomes a root — cross-scene parenting isn't representable, so it loses its parent if it had one.
The duplicate guard
If a persistent entity with the same Name already exists, the new one is destroyed and the original keeps running untouched. That's the fix for the classic "reloading the level created a second AudioManager" bug, and it's why persistent entities want stable, unique names.
For plain data crossing a scene boundary — a score, a checkpoint, a flag — prefer Session. A persistent entity carries a transform, components, physics and a script instance across a boundary where none of that is usually meaningful, and each one is a way to leak.
Coroutines
RunCoroutine(IEnumerator routine)
Starts a coroutine and returns a handle. See Coroutines for what you can yield return.
StopCoroutine(CoroutineHandle handle)
Stops that coroutine, including one paused mid-wait.
StopAllCoroutines()
Stops every coroutine this entity started.
Lifecycle hooks
Override the ones you need. All are protected override.
Core
| Hook | When it runs |
|---|---|
OnCreate() | Once, when the entity comes to life |
OnUpdate(float ts) | Once per rendered frame. ts is the scaled time since the last frame |
OnDestroy() | Once, when the entity is destroyed |
Overriding OnUpdate on a script that uses coroutines
The base OnUpdate is what advances this entity's coroutines. If you override it and call RunCoroutine, call base.OnUpdate(ts) somewhere in your override — otherwise your override replaces the base entirely (ordinary C# virtual dispatch) and the coroutines quietly stop advancing. A script that never overrides OnUpdate needs no such call.
Physics
| Hook | When it runs |
|---|---|
OnFixedUpdate(float fixedTs) | Once per fixed physics substep — zero, one or several times per rendered frame |
OnCollisionEnter(Collision2D collision) | A solid contact begins |
OnCollisionExit(Collision2D collision) | A solid contact ends |
OnTriggerEnter(GameEntity? other) | A sensor overlap begins |
OnTriggerExit(GameEntity? other) | A sensor overlap ends |
Forces and impulses belong in OnFixedUpdate. Applying one in OnUpdate makes its strength depend on frame rate — the same bug as a variable-timestep jump height, wearing a different hat.
Collision callbacks fire for a solid contact (neither collider is a sensor); trigger callbacks fire when at least one side is a sensor. Both entities in a touch get the callback, once each, with no ordering guarantee between the two sides — don't write gameplay that assumes one runs first.
other can be null
collision.Entity and other are null when the far side was destroyed earlier in the same dispatch batch — two bullets hitting one enemy in a single physics step, for instance. It's never a dangling reference, but you do have to null-check.
See Physics2D for queries and 2D Physics for the concepts.
Pooling
| Hook | When it runs |
|---|---|
OnSpawn() | Every time the entity is handed out of a pool |
OnDespawn() | Every time it's returned to its pool |
A pooled entity is built once and reused forever, so OnCreate and OnDestroy fire once each for the whole life of the instance. OnSpawn and OnDespawn are what fire on every reuse — that's where "start of life" and "clean up" belong for anything pooled. Neither is called for an entity that isn't pooled.
OnDespawn is not optional
Your state is not reset for you. Velocity, animation frame, timers and every field on your script survive into the next life. A recycled bullet that arrives already moving is an empty OnDespawn, not an engine bug — the engine can't know what "clean" means for your game.
It fires before the entity is disabled, so physics and audio are still live: zeroing a velocity there works.
See Pools.
Animation
| Hook | When it runs |
|---|---|
OnAnimatorStateEntered(string stateName) | The animator enters a state |
OnAnimatorStateExited(string stateName) | The animator leaves a state |
OnAnimatorPose() | After the animator has written this frame's pose |
Entered/Exited fire once per transition, in Exited-then-Entered order. OnAnimatorPose is specifically for layering procedural motion — recoil, look-at, breathing — on top of a clip without the next frame's sample overwriting it. See Animator.
UI
| Hook | When it runs |
|---|---|
OnClick() | A completed click on a UI Button on this same entity |
The script has to be on the button's own entity — see UI.
Networking
NetIdentity
This entity's network identity — net id, owner, and authority. Always safe to read; IsSpawned is false for any entity that isn't on the network, which is every entity in a single-player game. See Networking.
See also
- Components — every component façade
- Coroutines — running code across frames
- Physics2D — raycasts and overlap queries
- Scenes — loading and unloading
- Session — data that outlives a scene