Skip to main content
A Component is attached to a GameObject to give it behaviour. A ModelRenderer draws a model at the object’s position. A RigidBody adds physics simulation. Your own components hold your game logic — movement, health, weapons, and anything else your game needs.

Adding components

In the editor

Select a GameObject in the scene, then click Add Component in the Inspector. Search for the component type you want and click to add it.

In code

Call AddComponent<T>() on a GameObject reference:
// Add and configure a model renderer
var modelRenderer = go.AddComponent<ModelRenderer>();
modelRenderer.Model = Model.Load( "models/dev/box.vmdl" );
modelRenderer.Tint = Color.Red;
Use GetOrAddComponent<T>() when you want to ensure the component exists but don’t want to add a duplicate:
var modelRenderer = go.GetOrAddComponent<ModelRenderer>();
modelRenderer.Tint = Color.Green;

Querying components

Retrieve components from a GameObject and its hierarchy using the methods below:
// Get all components of a type on this GameObject
var renderers = go.GetComponents<ModelRenderer>();

// Get a single component on this GameObject
var renderer = go.GetComponent<ModelRenderer>();

// Get the first matching component on this GameObject or any child
var renderer = go.GetComponentInChildren<ModelRenderer>();

// Get all matching components on this GameObject and all children
var renderers = go.GetComponentsInChildren<ModelRenderer>();

// Get all matching components from this GameObject's ancestors and itself
var renderers = go.Components.GetComponentsInParent<ModelRenderer>();

// Get a single matching component from ancestors and itself
var renderer = go.Components.GetComponentInParent<ModelRenderer>();

Specialized queries with FindMode

For more control over which GameObjects and states to include, use Components.Get<T> and Components.GetAll<T> directly with FindMode flags:
// Find disabled components in ancestors
var x = go.Components.Get<ModelRenderer>( FindMode.Disabled | FindMode.InAncestors );

// Find all enabled components in ancestors and self
var x = go.Components.GetAll<ModelRenderer>( FindMode.Enabled | FindMode.InAncestors | FindMode.InSelf );

// Get every component on this GameObject regardless of type
var all = go.Components.GetAll();

Component references

Declare component references as properties to wire them up in the editor or enforce dependencies:
// Creates a drag-and-drop slot in the Inspector — drag any ModelRenderer from the scene into it
[Property] ModelRenderer BodyRenderer { get; set; }

// Finds or creates a ModelRenderer on the same GameObject automatically
[RequireComponent] ModelRenderer BodyRenderer { get; set; }
Use [RequireComponent] when your component always needs another component alongside it. It ensures the dependency is always present without manual setup.

Removing and destroying components

Call Destroy() on the component instance. You cannot reuse a destroyed component.
var depthOfField = GetComponent<DepthOfField>();
depthOfField.Destroy();
To destroy the entire GameObject from within a component, call DestroyGameObject() or GameObject.Destroy().

Component lifecycle methods

Override these methods in your component class to hook into the s&box update cycle:
Called after deserialization, before the scene starts. The loading screen stays open until all OnLoad tasks complete. Use this for heavy setup work like procedural generation.
protected override async Task OnLoad()
{
    LoadingScreen.Title = "Loading Something..";
    await Task.DelayRealtimeSeconds( 1.0f );
}
Called whenever a property changes in the editor, and after deserialization. Use this to enforce property limits or update derived state.
Called once when the component is created, but only if its parent GameObject is enabled. This fires after deserialization and loading.
Called when the component is enabled for the first time. Always runs before the first OnFixedUpdate.
Called each time the component is enabled (including after being disabled and re-enabled).
Called every frame. Use this for frame-rate-dependent logic like reading input or updating visual effects.
Called every frame just before rendering. Not called on dedicated servers. This fires after animation bones have been calculated, making it a good place to attach things to bones.
Called every fixed timestep. Use this for physics-dependent logic like player movement to keep behaviour consistent regardless of frame rate.
Called each time the component is disabled.
Called when the component is destroyed.
A component’s callbacks only run when the component is enabled and its GameObject and all ancestor GameObjects are also enabled.

Execution order

s&box does not guarantee the order in which the same callback fires across different GameObjects. If you need predictable cross-object ordering, use a GameObjectSystem instead.

Async in components

By default, async tasks in s&box run on the main thread, similar to coroutines. Use async Task methods to write sequential async logic:
async Task LerpSize( float seconds, Vector3 to, Easing.Function easer )
{
    TimeSince timeSince = 0;
    Vector3 from = WorldScale;

    while ( timeSince < seconds )
    {
        var size = Vector3.Lerp( from, to, easer( timeSince / seconds ) );
        WorldScale = size;
        await Task.Frame(); // wait one frame
    }
}

// Chain multiple async operations sequentially
await LerpSize( 3.0f, Vector3.One * 3.3f, Easing.BounceOut );
await LerpSize( 1.0f, Vector3.One * 4.0f, Easing.EaseInOut );
Run multiple async tasks concurrently with Task.WhenAll:
async Task DoMultipleThings()
{
    Task taskOne = PrintSomething( 2.0f, "One" );
    Task taskTwo = PrintSomething( 3.0f, "Two" );

    await Task.WhenAll( taskOne, taskTwo );
}
Async tasks can return values:
async Task<string> GetKanyeQuote()
{
    string kanyeQuote = await Http.RequestStringAsync( "https://api.kanye.rest/" );
    return kanyeQuote;
}
To fire an async method from synchronous code, discard the task with _:
protected override void OnEnabled()
{
    _ = DoMultipleThings();
}
Think about what happens when your GameObject is destroyed or disabled while an async method is awaiting. s&box automatically cancels tasks awaited through Component.Task, but tasks you fire independently may keep running after the object is gone.

Component events

You can broadcast and listen to events across your scene using interfaces. Events reach all active components and GameObjectSystems — they are not sent over the network.

Defining an event interface

public interface IPlayerEvent : ISceneEvent<IPlayerEvent>
{
    void OnSpawned( Player player ) { }
    void OnJumped( Player player ) { }
    void OnDied( Player player ) { }
}

Posting an event

// Broadcast to all active listeners in the scene
IPlayerEvent.Post( x => x.OnSpawned( playerThatSpawned ) );

// Broadcast only to components on a specific GameObject
IPlayerEvent.PostToGameObject( player.GameObject, x => x.OnSpawned( player ) );

Listening to an event

Implement the interface on any component:
public class ScoreTracker : Component, IPlayerEvent
{
    void IPlayerEvent.OnDied( Player player )
    {
        Log.Info( $"{player.Name} died" );
    }
}

Advanced broadcasting

Scene.RunEvent lets you target any type — not just custom event interfaces:
// Tint every SkinnedModelRenderer red
Scene.RunEvent<SkinnedModelRenderer>( x => x.Tint = Color.Red );

// Let listeners modify a value by reference
float damage = 100.0f;
Scene.RunEvent<IPlayerDamageMesser>( x => x.ModifyDamage( player, damageinfo, ref damage ) );

Component interfaces

s&box provides built-in interfaces you can implement on your components for common behaviours.
Runs OnAwake, OnEnabled, OnDisabled, OnUpdate, and OnFixedUpdate while in editor edit mode — useful for preview components.
public sealed class ExecuteInEditorSample : Component, Component.ExecuteInEditor
{
    protected override void OnEnabled()
    {
        base.OnEnabled();

        if ( Game.IsEditor )
        {
            Log.Error( "OnEnabled is also executed in editor" );
        }
    }
}
React to physics collisions on this collider or rigidbody.
MethodWhen it’s called
OnCollisionStartWhen this object starts touching another collider
OnCollisionUpdateOnce per physics step while touching another collider
OnCollisionStopWhen this object stops touching another collider
public sealed class CollisionListenerSample : Component, Component.ICollisionListener
{
    public void OnCollisionStart( Collision other )
    {
        Log.Info( "Collision started with: " + other.Other.GameObject );
    }

    public void OnCollisionUpdate( Collision other )
    {
        Log.Info( "Collision continued with: " + other.Other.GameObject );
    }

    public void OnCollisionStop( CollisionStop other )
    {
        Log.Info( "Collision stopped with: " + other.Other.GameObject );
    }
}
React to objects entering and leaving a trigger volume.
MethodWhen it’s called
OnTriggerEnterWhen a collider enters the trigger
OnTriggerExitWhen a collider stops touching the trigger
public sealed class TriggerListenerSample : Component, Component.ITriggerListener
{
    public void OnTriggerEnter( Collider other )
    {
        Log.Info( "Trigger entered with: " + other.GameObject );
    }

    public void OnTriggerExit( Collider other )
    {
        Log.Info( "Trigger exited with: " + other.GameObject );
    }
}
Mark a component as something that can receive damage. Other code can find and call OnDamage on it without knowing the specific type.
public sealed class SampleDamageable : Component, Component.IDamageable
{
    public void OnDamage( in DamageInfo damage )
    {
        Log.Info( $"I got damaged for {damage.Damage} by {damage.Attacker}" );
    }
}
// Finding and damaging something via IDamageable
var damageable = trace.GameObject.Components.Get<IDamageable>();
if ( damageable != null )
{
    damageable.OnDamage( new DamageInfo()
    {
        Damage = 12,
        Attacker = GameObject,
        Position = trace.HitPosition,
    });
}
React to network events. INetworkListener handles general network events; INetworkSpawn handles object spawn events. See the Networking section for details.

PlayerController reference

The PlayerController component is a ready-to-use first- and third-person player controller. Add it to a GameObject and you can walk around your scene immediately with no code required.

How it works

At its core, PlayerController is a special RigidBody. It participates in the physics system and exposes the same properties (velocity, mass, etc.) while adding movement controls.

Input

Built-in keyboard, mouse, and controller support. Override EyeAngles and WishVelocity in OnFixedUpdate if you want to drive it from custom input.

Camera

Built-in first- and third-person camera that you can toggle with a configured button. Disable the camera tab if you manage your own camera.

Animator

Built-in animator for Citizen-compatible AnimGraphs. Disable the animator tab if you provide your own animation logic.
The input, camera, and animator features are all optional. Right-click any tab in the Inspector to disable features you don’t need.

Listening to PlayerController events

Implement PlayerController.IEvents on a sibling component to react to player events and modify camera behaviour:
public interface IEvents : ISceneEvent<IEvents>
{
    /// Our eye angles are changing. Allows you to change the sensitivity, or stomp all together.
    void OnEyeAngles( ref Angles angles ) { }

    /// Called after we've set the camera up
    void PostCameraSetup( CameraComponent cam ) { }

    /// The player has just jumped
    void OnJumped() { }

    /// The player has landed on the ground, after falling this distance.
    void OnLanded( float distance, Vector3 impactVelocity ) { }

    /// Used by the Using system to find components we can interact with.
    Component GetUsableComponent( GameObject go ) { return default; }

    /// We have started using something (use was pressed)
    void StartPressing( Component target ) { }

    /// We have stopped using something
    void StopPressing( Component target ) { }

    /// We pressed USE but it did nothing
    void FailPressing() { }
}

TemporaryEffect

The TemporaryEffect component is designed for prefabs that contain particle effects and sounds. It monitors all child effects using the ITemporaryEffect interface and destroys the GameObject automatically once every effect has finished playing. Add TemporaryEffect to any prefab that should clean itself up — for example, an explosion effect that spawns at a point and then disappears. You can implement ITemporaryEffect on your own components to make them compatible with this system.