> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/scripting/entities-and-components.md).

# Entities and Components

All Out games are built around entities (things in the world) and components (behavior/data attached to entities).

If you’ve used Unity before: think “GameObject + Components”. If you’ve used Roblox: think “Instance + Scripts/Components”. The core idea is the same: **you compose gameplay by adding components to entities.**

## Entities

Entities exist in two common ways:

* **Placed in the editor**: they’re already in the scene at startup.
* **Created at runtime**: you spawn them from script.

### Creating and destroying entities

```go
entity := Scene.create_entity();
entity.set_local_position({10, 20});
entity.set_local_scale({2.5, 2.5});
entity.set_local_rotation(0);

// When you're done:
entity.destroy();
```

{% hint style="warning" %}
Destroying an entity destroys its components too. Don’t keep references to components after you destroy their entity.
{% endhint %}

### Parenting runtime-created entities

Use `keep_world_transform=true` to preserve the child's world position, rotation, and scale when changing its parent:

```go
child.set_parent(parent, keep_world_transform=true);
```

Pass `false` to keep the child's local transform instead. Pass `null` as the parent to detach it.

### Iterating entities

```go
for e: entity_iterator() {
    // ...
}
```

## Components

Components are the units of behavior. A component “lives on” an entity and can read/modify that entity.

### Getting and adding components

```go
sprite := entity.get_component(Sprite_Renderer);

player := entity.get_component(Player);

my_comp := entity.add_component(My_Component);
```

`add_component` calls `ao_start()` before returning. To initialize fields that `ao_start()` reads, pass a callback; it may capture locals from the caller:

```go
health := 100;
enemy := entity.add_component(Enemy, proc(enemy: Enemy) {
    enemy.health = health;
});
```

### Iterating components

```go
for enemy: component_iterator(Enemy) {
    enemy.tick_ai();
}
```

## Built-in components you’ll use a lot

### `Sprite_Renderer`

```go
texture := get_asset(Texture_Asset, "ui/button.png");

sprite := entity.get_component(Sprite_Renderer);
sprite.set_texture(texture);
sprite.color = {1, 1, 1, 1}; // RGBA
sprite.depth_offset = 0.5;
sprite.layer = 10;

// Optional: use a custom material. Pass true if the renderer should own it.
// sprite.set_material(material, true);
```

### Prefabs (`Prefab_Asset`)

Prefabs must be created in the editor. In scripts, you instantiate them.

```go
prefab := get_asset(Prefab_Asset, "MyPrefab.prefab");
spawned := Scene.instantiate(prefab);
spawned.set_local_position({1, 3});
```

The initialization callback runs before components on the prefab are started. It can be passed with either the default or an explicit position:

```go
target := v2.{1, 3};
spawned := Scene.instantiate(prefab, target, proc(entity: Entity) {
    entity.set_local_scale({2, 2});
});
```

### Spine (`Spine_Animator`)

If you’re using 2D animated characters, you’ll often work with `Spine_Animator`. See [Spine](/core-engine-concepts/spine.md).

## Writing a custom component

Make new components in dedicated files (e.g. `orbiter.csl`) and attach them to entities either:

* **Manually in the editor**, or
* **At runtime** with `entity.add_component(...)`

Components can implement lifecycle callbacks:

* `ao_start()`
* `ao_update(dt)`
* `ao_late_update(dt)` (after all updates)
* `ao_draw(dt)` (cosmetic-only; renderable frames)
* `ao_end()` (when destroyed)

`ao_draw` skips resim. Gameplay and interactive UI must use `ao_update`/`ao_late_update`, never `ao_draw`.

Example:

```go
Orbiter :: class : Component {
    center: v2;
    radius: float;
    speed: float;
    angle: float;

    ao_start :: method() {
        center = entity.local_position;
        radius = 2;
        speed = 1;
        angle = 0;
    }

    ao_update :: method(dt: float) {
        angle += speed * dt;

        offset_x := cos(angle) * radius;
        offset_y := sin(angle) * radius;

        entity.set_local_position({center.x + offset_x, center.y + offset_y});
    }
}
```

{% hint style="info" %}
Lifecycle methods for global scripts (`ao_start`, `ao_update`, ...) are covered in [Game/Frame Lifecycle](/scripting/game-frame-lifecycle.md).
{% endhint %}

## Serialized fields (`@ao_serialize`)

Use `@ao_serialize` to expose a field in the Inspector and include it in scene and JSON serialization.

```go
Chest :: class : Component {
    @ao_serialize capacity: int;
}
```

## Triggers and proximity queries

Trigger colliders expose `on_trigger_start`, `on_trigger_stay`, and `on_trigger_end` callbacks. See [Navmesh and Collision](/core-engine-concepts/navmesh-and-collision.md) for setup and callback signatures.

Use proximity queries when you need every component in a radius or only the closest one.

Useful helpers:

```go
Scene.get_all_components_in_range     :: proc(position: v2, range: float, results: ref [..]$T)
Scene.get_closest_component_in_range  :: proc(position: v2, range: float, $T: typeid) -> (T, bool)
```

Example:

```go
nearby: [..]Enemy;
Scene.get_all_components_in_range(player.entity.world_position, 5.0, ref nearby);

for e: nearby {
    // ...
}

closest_pickup, found := Scene.get_closest_component_in_range(player.entity.world_position, 2.0, Pickup);
if found {
    // ...
}
```

## Best practices

* **Per-player state belongs on `Player`.** Avoid global variables that would break with multiple players.
* **Prefer components for gameplay behaviors.** You’ll end up with reusable pieces you can attach to different entities.
* **Draw player UI from `Player.ao_late_update`.** Wrap it in `is_local_or_server()`.
* **Use `is_local()` only for player-specific visual overrides.** Do not change gameplay or UI state inside it.
