s&box Skill — Router
READ BEFORE WRITING CODE
s&box is not Unity. MonoBehaviour, Start(), Update(), GetComponent<T>() call sites, Instantiate(), Destroy(gameObject), Debug.Log, [SerializeField], Input.GetKey(), Physics.Raycast — none of these exist. If you write any of them you have hallucinated.
s&box is a C# scripting layer on Source 2 by Facepunch. The scene system uses GameObject + Component with a different lifecycle, a different networking model, and a different API surface. The API schema in references/api-schema-core.md is ground truth; if anything in this skill contradicts the schema, the schema wins.
Before you write a single line of s&box code, open the relevant reference file. SKILL.md is a router — it points you at the answer, it does not contain the answer. Writing a component? Open references/core-concepts.md. Writing UI? Open references/ui-razor.md. No exceptions for "simple" tasks — your muscle memory is wrong.
Architecture in 30 Seconds
Scene (is-a GameObject — the root)
└── GameObject (transform, tags, children, components)
└── Component (all gameplay code extends this)
- All gameplay code is a
sealed class extending Sandbox.Component.
- Lifecycle overrides are
protected override void OnAwake() / OnStart() / OnUpdate() / OnFixedUpdate() / OnEnabled() / OnDisabled() / OnDestroy(). Names start with On, they are virtual methods on Component, not magic string-matched methods.
- Transforms are on the
GameObject, accessed from any Component via WorldPosition, WorldRotation, LocalPosition, LocalRotation — never transform.position.
- UI is Razor (
.razor files) — HTML + SCSS + C#. Panels use flexbox layout. Hot-reloads in the editor.
- Networking is owner-authoritative. Mark state with
[Sync], mark methods with [Rpc.Broadcast / Host / Owner]. Skip simulation on non-owners with if ( IsProxy ) return;.
- Physics uses
Rigidbody + Collider components. Raycasts are Scene.Trace.Ray(from, to).Run() — a builder-pattern API. Collisions come through Component.ICollisionListener and Component.ITriggerListener interfaces.
- Coordinate system is Z-up:
Vector3.Forward = (1,0,0), Vector3.Right = (0,-1,0), Vector3.Up = (0,0,1).
- Restricted .NET:
System.IO.File, raw sockets, Console, Thread, Process — all blocked. Use FileSystem.Data, Http, Log, async/await.
Routing Table — "I need to…"
Match the task, open the file. Do not guess; open the file.
| Task |
Read |
| Understand the scene/GameObject/Component model |
references/core-concepts.md |
Write a Component (lifecycle, [Property], Tags, async) |
references/core-concepts.md |
| Spawn / clone / destroy a prefab |
references/core-concepts.md → Prefabs |
Fire a scene event (ISceneEvent<T>) |
references/core-concepts.md → Scene Events |
Write a GameObjectSystem |
references/core-concepts.md → GameObjectSystem |
Use ModelRenderer / SkinnedModelRenderer / bones / animgraph |
references/components-builtin.md → Rendering |
Use Rigidbody, any Collider, joints |
references/components-builtin.md → Physics |
Use CharacterController for movement |
references/components-builtin.md → CharacterController |
Use the full PlayerController (built-in FPS/TPS) |
references/components-builtin.md → Gameplay |
| Set up a camera, HUD painter, post-processing |
references/components-builtin.md → Camera, Post-Processing |
| Use lights, fog, envmap probes, skybox |
references/components-builtin.md → Lighting, Environment |
Add audio (SoundPointComponent, Sound.Play) |
references/components-builtin.md → Audio |
Use NavMeshAgent, NavMeshLink, query NavMesh |
references/components-builtin.md → Navigation |
| Create particles, decals, trails, beams |
references/components-builtin.md → Effects, Rendering |
Write a Razor UI panel (.razor, PanelComponent, BuildHash) |
references/ui-razor.md |
Style with SCSS — flexbox, transitions, :intro / :outro, :bind |
references/ui-razor.md → Styling, Layout System, Transitions |
Use built-in controls (Button, TextEntry, DropDown, VirtualList) |
references/ui-razor.md → Built-in Controls |
| Build a world-space panel or a NavigationHost app |
references/ui-razor.md → WorldPanel, Navigation |
Set up a lobby, connect/disconnect, query Connection |
references/networking.md → Lobby & Connection |
Network an object (NetworkMode, NetworkSpawn, ownership) |
references/networking.md → Networked Objects, Ownership |
Use [Sync] / [Sync(SyncFlags.X)] / [Change] / NetList / NetDictionary |
references/networking.md → [Sync] Properties |
Write RPCs ([Rpc.Broadcast/Host/Owner], NetFlags, caller info, filtering) |
references/networking.md → RPC Messages |
React to connections (INetworkListener, INetworkSpawn, snapshot data) |
references/networking.md → Network Events |
Use ISceneStartup for host vs client initialization |
references/networking.md → Scene Startup |
Dedicated server / #if SERVER / user permissions |
references/networking.md → Dedicated Servers |
| Poll keyboard/mouse/controller input, haptics, glyphs |
references/input-and-physics.md → Input |
| Raycast / sphere / box / capsule trace with tag filters |
references/input-and-physics.md → SceneTrace |
Access PhysicsWorld, gravity, physics events |
references/input-and-physics.md → Physics World |
| Implement collision/trigger listeners |
references/input-and-physics.md → Collision System |
Use Vector3 / Rotation / Angles / Transform / BBox / Ray / Capsule |
references/input-and-physics.md → Math Types |
Use Time.Now, Time.Delta, TimeSince, TimeUntil |
references/input-and-physics.md → Time |
Draw debug gizmos (DrawGizmos, Gizmo.Draw) |
references/input-and-physics.md → Gizmo |
Need the full signature of GameObject, Component, Scene, Input, etc. |
references/api-schema-core.md |
| Look up whether a given type exists & what it does |
references/api-schema-extended.md |
| See a complete worked example of a pattern before writing your own |
references/patterns-and-examples.md |
Unity → s&box Translation Table
Any time you write one of the left column, you are hallucinating. Use the right column.
| Unity / Wrong |
s&box / Correct |
class Foo : MonoBehaviour |
public sealed class Foo : Component |
void Awake() |
protected override void OnAwake() |
void Start() |
protected override void OnStart() |
void Update() |
protected override void OnUpdate() |
void FixedUpdate() |
protected override void OnFixedUpdate() |
void OnEnable() / OnDisable() |
protected override void OnEnabled() / OnDisabled() |
void OnDestroy() |
protected override void OnDestroy() |
[SerializeField] float speed |
[Property] public float Speed { get; set; } |
[HideInInspector] |
[Hide] |
transform.position |
WorldPosition (or GameObject.WorldPosition) |
transform.localPosition |
LocalPosition |
transform.rotation |
WorldRotation |
transform.forward |
WorldRotation.Forward |
gameObject.SetActive(false) |
GameObject.Enabled = false |
Destroy(gameObject) / Destroy(this) |
GameObject.Destroy() / Component.Destroy() / DestroyGameObject() |
Instantiate(prefab, pos, rot) |
prefab.Clone( pos, rot ) |
Instantiate(prefab); NetworkServer.Spawn(...) |
prefab.Clone(pos).NetworkSpawn( owner ) |
GetComponent<T>() in Start/Update |
GetComponent<T>() is fine; also Components.Get<T>( FindMode ) for ancestor/descendant searches |
FindObjectOfType<T>() / FindObjectsOfType<T>() |
Scene.Get<T>() / Scene.GetAll<T>() / Scene.GetAllComponents<T>() |
GameObject.Find("Name") |
Scene.Directory.FindByName("Name") |
OnCollisionEnter(Collision c) |
Implement Component.ICollisionListener.OnCollisionStart(Collision c) |
OnTriggerEnter(Collider c) |
Implement Component.ITriggerListener.OnTriggerEnter(Collider c) |
Physics.Raycast(...) |
Scene.Trace.Ray( from, to ).Run() (builder; returns SceneTraceResult) |
Physics.OverlapSphere(pos, r) |
Scene.Trace.Sphere( r, pos, pos ).RunAll() |
Rigidbody.AddForce(f, ForceMode.Impulse) |
Rigidbody.ApplyImpulse( f ) |
Rigidbody.AddForce(f) |
Rigidbody.ApplyForce( f ) |
Rigidbody.velocity |
Rigidbody.Velocity (capital V) |
Input.GetKey(KeyCode.W) |
Input.Down( "forward" ) — actions are strings configured in Project Settings |
Input.GetKeyDown(...) |
Input.Pressed( "action" ) |
Input.GetAxis("Horizontal") / Vertical |
Input.AnalogMove (Vector3) |
Input.mousePosition |
Mouse.Position (Vector2) |
Camera.main |
Scene.Camera |
Camera.main.ScreenPointToRay(Input.mousePosition) |
Scene.Camera.ScreenPixelToRay( Mouse.Position ) |
StartCoroutine(Foo()) with IEnumerator |
async Task Foo() with await Task.DelaySeconds(...); call as _ = Foo(); |
yield return new WaitForSeconds(1f) |
await Task.DelaySeconds( 1f ) |
yield return null |
await Task.Frame() |
Debug.Log(x) / .LogWarning / .LogError |
Log.Info(x) / Log.Warning(x) / Log.Error(x) |
Time.time |
Time.Now |
Time.deltaTime |
Time.Delta |
Time.fixedDeltaTime |
Scene.FixedDelta |
Mathf.Lerp / Clamp / Approach |
MathX.Lerp / Clamp / Approach |
Random.Range(a, b) |
Game.Random.Next(a, b) / Game.Random.NextSingle() |
Vector3.forward = (0,0,1) |
Vector3.Forward = (1,0,0) — s&box is Z-up; re-check every literal direction |
SceneManager.LoadScene("name") |
Scene.LoadFromFile("path/to/scene.scene") or Scene.Load( sceneResource ) |
DontDestroyOnLoad(go) |
go.Flags = GameObjectFlags.DontDestroyOnLoad |
Application.isPlaying |
Game.IsPlaying (or !Game.IsEditor) |
System.IO.File.ReadAllText(...) |
FileSystem.Data.ReadAllText(...) / FileSystem.Mounted.ReadAllText(...) |
UnityEngine.Networking.UnityWebRequest |
Http.RequestStringAsync(...) / Http.RequestJsonAsync<T>(...) |
Update() reads Input.* AND moves rigidbody |
Read input in OnUpdate, move in OnFixedUpdate |
If a Unity pattern isn't in the table, assume it doesn't exist in s&box and look it up in references/api-schema-core.md before writing it.
The Ten Rules You Must Not Break
- Every gameplay class extends
Component. Not MonoBehaviour, not object, not ScriptableObject — just Component. Mark it sealed unless inheritance is required.
- Lifecycle methods are
protected override void On*(). If you wrote void Update() instead of protected override void OnUpdate(), your code does nothing.
- Serialize fields with
[Property]. Not [SerializeField], not public alone. [Property] both shows in the inspector and saves to prefab/scene.
- Networked state uses
[Sync]. Only the object owner may assign. Everyone else sees replicated values. Combine with [Change(nameof(Method))] for change callbacks.
- Guard networked logic with
if ( IsProxy ) return;. Every component that reads input or drives movement starts with this line — otherwise every client tries to move every player.
- Ray/box/sphere traces go through
Scene.Trace. It's a builder: Scene.Trace.Ray(from, to).UseHitboxes(true).WithoutTags(new[]{"player"}).Run() returns a SceneTraceResult. Never Physics.Raycast.
- UI is Razor + flexbox.
display: flex is the default and effectively the only layout. display: block does not exist. Properties :intro / :outro animate creation and deletion. Every root panel overrides BuildHash() to control re-render.
- Coroutines don't exist — use
async Task. await Task.DelaySeconds( n ), await Task.Frame(). Fire-and-forget with _ = MyTask();. The Component.Task property scopes cancellation to the GameObject lifetime.
- Never touch blocked .NET APIs.
System.IO.File, Console, Thread, raw sockets, System.Net.Http.HttpClient — use FileSystem.Data, Log, async/await, Http instead. Code that references these won't compile in the sandbox.
- Look up every API before you use it. The schema is ground truth. If you can't find a method in
api-schema-core.md or api-schema-extended.md, either you're guessing (stop) or it's nested on a specific type (search the topical reference).
Project Structure (s&box Game)
An s&box game project (.sbproj) typically looks like:
MyGame/
├── MyGame.sbproj # project manifest
├── code/ # C# source (gameplay code)
│ ├── GameManager.cs
│ ├── Player.cs
│ ├── weapons/
│ │ └── Rifle.cs
│ └── ui/
│ ├── Hud.razor # Razor panel
│ ├── Hud.razor.scss # auto-loaded stylesheet
│ └── InventoryPanel.razor
├── prefabs/ # .prefab files
├── scenes/ # .scene files
├── models/ # .vmdl, .vmdl_c
├── materials/ # .vmat, .vmat_c
├── sounds/ # .sound, .wav, .mp3
├── ui/ # UI images, textures
└── localization/
└── en/
└── mygame.json
Notable:
.razor and .razor.scss files pair by name — the stylesheet auto-loads when the panel is built.
.cs files are hot-reloaded in the editor; saving a file rebuilds and re-injects within seconds.
- Asset paths in code are forward-slash strings rooted at the project:
Model.Load( "models/dev/box.vmdl" ).
- There is no
Assets/ folder; paths are flat under the project root.
A Reference Component (Shape Only — Not the Content)
This is the shape of a component. The content depends on what you're building — read the referenced file, then write it.
using Sandbox;
public sealed class MyComponent : Component
{
[Property] public float Speed { get; set; } = 200f;
[Property] public GameObject Target { get; set; }
[Sync] public int Score { get; set; }
TimeSince _lastAction;
protected override void OnStart()
{
// runs once before first update
}
protected override void OnUpdate()
{
if ( IsProxy ) return; // networking guard
if ( !Target.IsValid() ) return;
// per-frame logic (input, visuals, camera)
}
protected override void OnFixedUpdate()
{
if ( IsProxy ) return;
// physics / movement — deterministic timestep
}
[Rpc.Broadcast]
public void PlayEffect( Vector3 position )
{
// runs on all clients — cosmetic / non-authoritative
}
}
For complete runnable examples — a full FPS controller, a networked player, a Razor HUD, a hitscan weapon, a NavMeshAgent AI, a physics grenade, a prefab spawner, a trigger pickup — see references/patterns-and-examples.md.
Gotchas Captured From Real Builds
These are things that will bite you. They're documented deeper in the reference files; this is the quick-lookup list.
ICollisionListener parameter names are collision, not other (the raw docs are wrong about this).
Color, Capsule, Vector3, Rotation, Angles, Transform, BBox, Ray are global types, not Sandbox.*.
LobbyConfig and LobbyPrivacy live in Sandbox.Network — you need using Sandbox.Network;.
Scene is-a GameObject — it is the root GameObject. Scene.GetAllObjects(true) walks the tree.
- Most
Component.ISomething interfaces are nested on Component: Component.IDamageable, Component.ICollisionListener, Component.ITriggerListener, Component.INetworkListener, Component.INetworkSpawn. But IGameObjectNetworkEvents is top-level Sandbox.IGameObjectNetworkEvents.
ComponentList.GetOrCreate<T>( FindMode ) requires the FindMode arg. For the common "on this GameObject" case, use GetOrAddComponent<T>() on GameObject or Component instead.
SceneTrace.WithoutTags / WithAnyTags / WithAllTags take string[] — not params. Pass new[] { "tag" }, or use WithTag(string) for the single-tag case.
Game.Random is System.Random — NextSingle(), Next(n) work directly. The FromList extension exists but requires a defVal; list[Game.Random.Next(list.Count)] is simpler.
FileSystem is a static facade. Actual methods are on BaseFileSystem, accessed via FileSystem.Data (game user data) or FileSystem.Mounted (mounted content).
- There is no standalone
Log class — it's Sandbox.Diagnostics.Logger, but the global Log instance works fine everywhere.
PlayerController.TraceBody has 4 parameters (the 4th is heightScale), not 3.
- Operators (
Rotation * Rotation, Vector3 * Rotation) aren't listed in the API schema because they're systematically excluded — they do exist, use them normally.
NavigationHost is in Sandbox.UI.Navigation, needs @using Sandbox.UI.Navigation in your Razor file.
Verification Loop (When In Doubt)
If you're about to write an API call and you're not sure it exists:
- Check the topical file for the area first (
networking.md, ui-razor.md, etc.). Topical files include inline signatures for the APIs they cover.
- If not there, check
api-schema-core.md. It has full signatures for the ~50 most-used types.
- If not there, check
api-schema-extended.md. It's a namespace-organized index of 738 more types — find the type, then look up full signatures elsewhere if needed.
- If a method or property is in NONE of the above, it does not exist. Do not write it. Revisit your design — there is almost certainly an s&box-idiomatic way to do what you want.
The schema under raw/api-schema.json is not shipped with the skill and is not for you to read directly; the reference files are the curated, searchable view of it.
This file is a router. Reference files teach. Do not answer s&box questions from SKILL.md alone — open the relevant reference and read it.
Source: gavogavogavo/claude-sbox — distributed by TomeVault.
1---2name: sbox3description: Use when writing or modifying code for s&box, sbox, the Facepunch sandbox engine, or any Source 2 game project in C#. Triggers on mentions of s&box, sbox, sandbox game, Facepunch engine, Source 2 game, `.sbproj`, `.razor` with `PanelComponent`, `@inherits PanelComponent`, `Sandbox.Component`, `GameObject` + `Components.Get<T>`, `Scene.Trace`, `CharacterController` + `.Move()`, `SkinnedModelRenderer`, `NavMeshAgent`, `[Sync]`, `[Rpc.Broadcast]`, `[Property]`, `INetworkListener`, `ISceneEvent`, `PlayerController` in C#. Also triggers on any file with `using Sandbox;` or `using Sandbox.UI;` that isn't Unity/Godot/Unreal. Writes idiomatic s&box C# components, Razor UI, and networking code; prevents Unity-pattern leakage.4---56# s&box Skill — Router78## READ BEFORE WRITING CODE910**s&box is not Unity.** `MonoBehaviour`, `Start()`, `Update()`, `GetComponent<T>()` call sites, `Instantiate()`, `Destroy(gameObject)`, `Debug.Log`, `[SerializeField]`, `Input.GetKey()`, `Physics.Raycast` — **none of these exist**. If you write any of them you have hallucinated.1112s&box is a C# scripting layer on Source 2 by Facepunch. The scene system uses `GameObject` + `Component` with a different lifecycle, a different networking model, and a different API surface. The API schema in `references/api-schema-core.md` is ground truth; if anything in this skill contradicts the schema, the schema wins.1314**Before you write a single line of s&box code, open the relevant reference file.** SKILL.md is a router — it points you at the answer, it does not contain the answer. Writing a component? Open `references/core-concepts.md`. Writing UI? Open `references/ui-razor.md`. No exceptions for "simple" tasks — your muscle memory is wrong.1516---1718## Architecture in 30 Seconds1920```21Scene (is-a GameObject — the root)22 └── GameObject (transform, tags, children, components)23 └── Component (all gameplay code extends this)24```2526- **All gameplay code** is a `sealed class` extending `Sandbox.Component`.27- **Lifecycle overrides** are `protected override void OnAwake() / OnStart() / OnUpdate() / OnFixedUpdate() / OnEnabled() / OnDisabled() / OnDestroy()`. Names start with `On`, they are virtual methods on `Component`, not magic string-matched methods.28- **Transforms** are on the `GameObject`, accessed from any Component via `WorldPosition`, `WorldRotation`, `LocalPosition`, `LocalRotation` — never `transform.position`.29- **UI** is Razor (`.razor` files) — HTML + SCSS + C#. Panels use flexbox layout. Hot-reloads in the editor.30- **Networking** is owner-authoritative. Mark state with `[Sync]`, mark methods with `[Rpc.Broadcast / Host / Owner]`. Skip simulation on non-owners with `if ( IsProxy ) return;`.31- **Physics** uses `Rigidbody` + `Collider` components. Raycasts are `Scene.Trace.Ray(from, to).Run()` — a builder-pattern API. Collisions come through `Component.ICollisionListener` and `Component.ITriggerListener` interfaces.32- **Coordinate system** is **Z-up**: `Vector3.Forward = (1,0,0)`, `Vector3.Right = (0,-1,0)`, `Vector3.Up = (0,0,1)`.33- **Restricted .NET:** `System.IO.File`, raw sockets, `Console`, `Thread`, `Process` — all blocked. Use `FileSystem.Data`, `Http`, `Log`, `async/await`.3435---3637## Routing Table — "I need to…"3839Match the task, open the file. Do not guess; open the file.4041| Task | Read |42|---|---|43| Understand the scene/GameObject/Component model | `references/core-concepts.md` |44| Write a `Component` (lifecycle, `[Property]`, Tags, async) | `references/core-concepts.md` |45| Spawn / clone / destroy a prefab | `references/core-concepts.md` → *Prefabs* |46| Fire a scene event (`ISceneEvent<T>`) | `references/core-concepts.md` → *Scene Events* |47| Write a `GameObjectSystem` | `references/core-concepts.md` → *GameObjectSystem* |48| Use `ModelRenderer` / `SkinnedModelRenderer` / bones / animgraph | `references/components-builtin.md` → *Rendering* |49| Use `Rigidbody`, any `Collider`, joints | `references/components-builtin.md` → *Physics* |50| Use `CharacterController` for movement | `references/components-builtin.md` → *CharacterController* |51| Use the full `PlayerController` (built-in FPS/TPS) | `references/components-builtin.md` → *Gameplay* |52| Set up a camera, HUD painter, post-processing | `references/components-builtin.md` → *Camera*, *Post-Processing* |53| Use lights, fog, envmap probes, skybox | `references/components-builtin.md` → *Lighting*, *Environment* |54| Add audio (`SoundPointComponent`, `Sound.Play`) | `references/components-builtin.md` → *Audio* |55| Use `NavMeshAgent`, `NavMeshLink`, query NavMesh | `references/components-builtin.md` → *Navigation* |56| Create particles, decals, trails, beams | `references/components-builtin.md` → *Effects*, *Rendering* |57| Write a Razor UI panel (`.razor`, `PanelComponent`, `BuildHash`) | `references/ui-razor.md` |58| Style with SCSS — flexbox, transitions, `:intro` / `:outro`, `:bind` | `references/ui-razor.md` → *Styling*, *Layout System*, *Transitions* |59| Use built-in controls (`Button`, `TextEntry`, `DropDown`, `VirtualList`) | `references/ui-razor.md` → *Built-in Controls* |60| Build a world-space panel or a NavigationHost app | `references/ui-razor.md` → *WorldPanel*, *Navigation* |61| Set up a lobby, connect/disconnect, query `Connection` | `references/networking.md` → *Lobby & Connection* |62| Network an object (`NetworkMode`, `NetworkSpawn`, ownership) | `references/networking.md` → *Networked Objects*, *Ownership* |63| Use `[Sync]` / `[Sync(SyncFlags.X)]` / `[Change]` / `NetList` / `NetDictionary` | `references/networking.md` → *[Sync] Properties* |64| Write RPCs (`[Rpc.Broadcast/Host/Owner]`, `NetFlags`, caller info, filtering) | `references/networking.md` → *RPC Messages* |65| React to connections (`INetworkListener`, `INetworkSpawn`, snapshot data) | `references/networking.md` → *Network Events* |66| Use `ISceneStartup` for host vs client initialization | `references/networking.md` → *Scene Startup* |67| Dedicated server / `#if SERVER` / user permissions | `references/networking.md` → *Dedicated Servers* |68| Poll keyboard/mouse/controller input, haptics, glyphs | `references/input-and-physics.md` → *Input* |69| Raycast / sphere / box / capsule trace with tag filters | `references/input-and-physics.md` → *SceneTrace* |70| Access `PhysicsWorld`, gravity, physics events | `references/input-and-physics.md` → *Physics World* |71| Implement collision/trigger listeners | `references/input-and-physics.md` → *Collision System* |72| Use `Vector3` / `Rotation` / `Angles` / `Transform` / `BBox` / `Ray` / `Capsule` | `references/input-and-physics.md` → *Math Types* |73| Use `Time.Now`, `Time.Delta`, `TimeSince`, `TimeUntil` | `references/input-and-physics.md` → *Time* |74| Draw debug gizmos (`DrawGizmos`, `Gizmo.Draw`) | `references/input-and-physics.md` → *Gizmo* |75| Need the full signature of `GameObject`, `Component`, `Scene`, `Input`, etc. | `references/api-schema-core.md` |76| Look up whether a given type exists & what it does | `references/api-schema-extended.md` |77| See a complete worked example of a pattern before writing your own | `references/patterns-and-examples.md` |7879---8081## Unity → s&box Translation Table8283Any time you write one of the left column, you are hallucinating. Use the right column.8485| Unity / Wrong | s&box / Correct |86|---|---|87| `class Foo : MonoBehaviour` | `public sealed class Foo : Component` |88| `void Awake()` | `protected override void OnAwake()` |89| `void Start()` | `protected override void OnStart()` |90| `void Update()` | `protected override void OnUpdate()` |91| `void FixedUpdate()` | `protected override void OnFixedUpdate()` |92| `void OnEnable()` / `OnDisable()` | `protected override void OnEnabled()` / `OnDisabled()` |93| `void OnDestroy()` | `protected override void OnDestroy()` |94| `[SerializeField] float speed` | `[Property] public float Speed { get; set; }` |95| `[HideInInspector]` | `[Hide]` |96| `transform.position` | `WorldPosition` (or `GameObject.WorldPosition`) |97| `transform.localPosition` | `LocalPosition` |98| `transform.rotation` | `WorldRotation` |99| `transform.forward` | `WorldRotation.Forward` |100| `gameObject.SetActive(false)` | `GameObject.Enabled = false` |101| `Destroy(gameObject)` / `Destroy(this)` | `GameObject.Destroy()` / `Component.Destroy()` / `DestroyGameObject()` |102| `Instantiate(prefab, pos, rot)` | `prefab.Clone( pos, rot )` |103| `Instantiate(prefab); NetworkServer.Spawn(...)` | `prefab.Clone(pos).NetworkSpawn( owner )` |104| `GetComponent<T>()` in `Start/Update` | `GetComponent<T>()` is fine; also `Components.Get<T>( FindMode )` for ancestor/descendant searches |105| `FindObjectOfType<T>()` / `FindObjectsOfType<T>()` | `Scene.Get<T>()` / `Scene.GetAll<T>()` / `Scene.GetAllComponents<T>()` |106| `GameObject.Find("Name")` | `Scene.Directory.FindByName("Name")` |107| `OnCollisionEnter(Collision c)` | Implement `Component.ICollisionListener.OnCollisionStart(Collision c)` |108| `OnTriggerEnter(Collider c)` | Implement `Component.ITriggerListener.OnTriggerEnter(Collider c)` |109| `Physics.Raycast(...)` | `Scene.Trace.Ray( from, to ).Run()` (builder; returns `SceneTraceResult`) |110| `Physics.OverlapSphere(pos, r)` | `Scene.Trace.Sphere( r, pos, pos ).RunAll()` |111| `Rigidbody.AddForce(f, ForceMode.Impulse)` | `Rigidbody.ApplyImpulse( f )` |112| `Rigidbody.AddForce(f)` | `Rigidbody.ApplyForce( f )` |113| `Rigidbody.velocity` | `Rigidbody.Velocity` (capital V) |114| `Input.GetKey(KeyCode.W)` | `Input.Down( "forward" )` — actions are strings configured in Project Settings |115| `Input.GetKeyDown(...)` | `Input.Pressed( "action" )` |116| `Input.GetAxis("Horizontal")` / `Vertical` | `Input.AnalogMove` (`Vector3`) |117| `Input.mousePosition` | `Mouse.Position` (`Vector2`) |118| `Camera.main` | `Scene.Camera` |119| `Camera.main.ScreenPointToRay(Input.mousePosition)` | `Scene.Camera.ScreenPixelToRay( Mouse.Position )` |120| `StartCoroutine(Foo())` with `IEnumerator` | `async Task Foo()` with `await Task.DelaySeconds(...)`; call as `_ = Foo();` |121| `yield return new WaitForSeconds(1f)` | `await Task.DelaySeconds( 1f )` |122| `yield return null` | `await Task.Frame()` |123| `Debug.Log(x)` / `.LogWarning` / `.LogError` | `Log.Info(x)` / `Log.Warning(x)` / `Log.Error(x)` |124| `Time.time` | `Time.Now` |125| `Time.deltaTime` | `Time.Delta` |126| `Time.fixedDeltaTime` | `Scene.FixedDelta` |127| `Mathf.Lerp / Clamp / Approach` | `MathX.Lerp / Clamp / Approach` |128| `Random.Range(a, b)` | `Game.Random.Next(a, b)` / `Game.Random.NextSingle()` |129| `Vector3.forward = (0,0,1)` | `Vector3.Forward = (1,0,0)` — **s&box is Z-up**; re-check every literal direction |130| `SceneManager.LoadScene("name")` | `Scene.LoadFromFile("path/to/scene.scene")` or `Scene.Load( sceneResource )` |131| `DontDestroyOnLoad(go)` | `go.Flags = GameObjectFlags.DontDestroyOnLoad` |132| `Application.isPlaying` | `Game.IsPlaying` (or `!Game.IsEditor`) |133| `System.IO.File.ReadAllText(...)` | `FileSystem.Data.ReadAllText(...)` / `FileSystem.Mounted.ReadAllText(...)` |134| `UnityEngine.Networking.UnityWebRequest` | `Http.RequestStringAsync(...)` / `Http.RequestJsonAsync<T>(...)` |135| `Update()` reads `Input.*` AND moves rigidbody | Read input in `OnUpdate`, move in `OnFixedUpdate` |136137If a Unity pattern isn't in the table, assume it doesn't exist in s&box and look it up in `references/api-schema-core.md` before writing it.138139---140141## The Ten Rules You Must Not Break1421431. **Every gameplay class extends `Component`.** Not `MonoBehaviour`, not `object`, not `ScriptableObject` — just `Component`. Mark it `sealed` unless inheritance is required.1442. **Lifecycle methods are `protected override void On*()`.** If you wrote `void Update()` instead of `protected override void OnUpdate()`, your code does nothing.1453. **Serialize fields with `[Property]`.** Not `[SerializeField]`, not `public` alone. `[Property]` both shows in the inspector and saves to prefab/scene.1464. **Networked state uses `[Sync]`.** Only the object owner may assign. Everyone else sees replicated values. Combine with `[Change(nameof(Method))]` for change callbacks.1475. **Guard networked logic with `if ( IsProxy ) return;`.** Every component that reads input or drives movement starts with this line — otherwise every client tries to move every player.1486. **Ray/box/sphere traces go through `Scene.Trace`.** It's a builder: `Scene.Trace.Ray(from, to).UseHitboxes(true).WithoutTags(new[]{"player"}).Run()` returns a `SceneTraceResult`. Never `Physics.Raycast`.1497. **UI is Razor + flexbox.** `display: flex` is the default and effectively the only layout. `display: block` does not exist. Properties `:intro` / `:outro` animate creation and deletion. Every root panel overrides `BuildHash()` to control re-render.1508. **Coroutines don't exist — use `async Task`.** `await Task.DelaySeconds( n )`, `await Task.Frame()`. Fire-and-forget with `_ = MyTask();`. The `Component.Task` property scopes cancellation to the GameObject lifetime.1519. **Never touch blocked .NET APIs.** `System.IO.File`, `Console`, `Thread`, raw sockets, `System.Net.Http.HttpClient` — use `FileSystem.Data`, `Log`, `async/await`, `Http` instead. Code that references these won't compile in the sandbox.15210. **Look up every API before you use it.** The schema is ground truth. If you can't find a method in `api-schema-core.md` or `api-schema-extended.md`, either you're guessing (stop) or it's nested on a specific type (search the topical reference).153154---155156## Project Structure (s&box Game)157158An s&box game project (`.sbproj`) typically looks like:159160```161MyGame/162├── MyGame.sbproj # project manifest163├── code/ # C# source (gameplay code)164│ ├── GameManager.cs165│ ├── Player.cs166│ ├── weapons/167│ │ └── Rifle.cs168│ └── ui/169│ ├── Hud.razor # Razor panel170│ ├── Hud.razor.scss # auto-loaded stylesheet171│ └── InventoryPanel.razor172├── prefabs/ # .prefab files173├── scenes/ # .scene files174├── models/ # .vmdl, .vmdl_c175├── materials/ # .vmat, .vmat_c176├── sounds/ # .sound, .wav, .mp3177├── ui/ # UI images, textures178└── localization/179 └── en/180 └── mygame.json181```182183Notable:184- `.razor` and `.razor.scss` files pair by name — the stylesheet auto-loads when the panel is built.185- `.cs` files are hot-reloaded in the editor; saving a file rebuilds and re-injects within seconds.186- Asset paths in code are forward-slash strings rooted at the project: `Model.Load( "models/dev/box.vmdl" )`.187- There is no `Assets/` folder; paths are flat under the project root.188189---190191## A Reference Component (Shape Only — Not the Content)192193This is the *shape* of a component. The *content* depends on what you're building — read the referenced file, then write it.194195```csharp196using Sandbox;197198public sealed class MyComponent : Component199{200 [Property] public float Speed { get; set; } = 200f;201 [Property] public GameObject Target { get; set; }202203 [Sync] public int Score { get; set; }204205 TimeSince _lastAction;206207 protected override void OnStart()208 {209 // runs once before first update210 }211212 protected override void OnUpdate()213 {214 if ( IsProxy ) return; // networking guard215 if ( !Target.IsValid() ) return;216217 // per-frame logic (input, visuals, camera)218 }219220 protected override void OnFixedUpdate()221 {222 if ( IsProxy ) return;223 // physics / movement — deterministic timestep224 }225226 [Rpc.Broadcast]227 public void PlayEffect( Vector3 position )228 {229 // runs on all clients — cosmetic / non-authoritative230 }231}232```233234For complete runnable examples — a full FPS controller, a networked player, a Razor HUD, a hitscan weapon, a NavMeshAgent AI, a physics grenade, a prefab spawner, a trigger pickup — see `references/patterns-and-examples.md`.235236---237238## Gotchas Captured From Real Builds239240These are things that will bite you. They're documented deeper in the reference files; this is the quick-lookup list.241242- `ICollisionListener` parameter names are `collision`, not `other` (the raw docs are wrong about this).243- `Color`, `Capsule`, `Vector3`, `Rotation`, `Angles`, `Transform`, `BBox`, `Ray` are **global** types, not `Sandbox.*`.244- `LobbyConfig` and `LobbyPrivacy` live in `Sandbox.Network` — you need `using Sandbox.Network;`.245- `Scene` is-a `GameObject` — it *is* the root GameObject. `Scene.GetAllObjects(true)` walks the tree.246- Most `Component.ISomething` interfaces are **nested** on `Component`: `Component.IDamageable`, `Component.ICollisionListener`, `Component.ITriggerListener`, `Component.INetworkListener`, `Component.INetworkSpawn`. But `IGameObjectNetworkEvents` is top-level `Sandbox.IGameObjectNetworkEvents`.247- `ComponentList.GetOrCreate<T>( FindMode )` requires the `FindMode` arg. For the common "on this GameObject" case, use `GetOrAddComponent<T>()` on `GameObject` or `Component` instead.248- `SceneTrace.WithoutTags` / `WithAnyTags` / `WithAllTags` take `string[]` — not `params`. Pass `new[] { "tag" }`, or use `WithTag(string)` for the single-tag case.249- `Game.Random` is `System.Random` — `NextSingle()`, `Next(n)` work directly. The `FromList` extension exists but requires a `defVal`; `list[Game.Random.Next(list.Count)]` is simpler.250- `FileSystem` is a static facade. Actual methods are on `BaseFileSystem`, accessed via `FileSystem.Data` (game user data) or `FileSystem.Mounted` (mounted content).251- There is no standalone `Log` class — it's `Sandbox.Diagnostics.Logger`, but the global `Log` instance works fine everywhere.252- `PlayerController.TraceBody` has **4** parameters (the 4th is `heightScale`), not 3.253- Operators (`Rotation * Rotation`, `Vector3 * Rotation`) aren't listed in the API schema because they're systematically excluded — they do exist, use them normally.254- `NavigationHost` is in `Sandbox.UI.Navigation`, needs `@using Sandbox.UI.Navigation` in your Razor file.255256---257258## Verification Loop (When In Doubt)259260If you're about to write an API call and you're not sure it exists:2612621. **Check the topical file** for the area first (`networking.md`, `ui-razor.md`, etc.). Topical files include inline signatures for the APIs they cover.2632. **If not there, check `api-schema-core.md`.** It has full signatures for the ~50 most-used types.2643. **If not there, check `api-schema-extended.md`.** It's a namespace-organized index of 738 more types — find the type, then look up full signatures elsewhere if needed.2654. **If a method or property is in NONE of the above, it does not exist.** Do not write it. Revisit your design — there is almost certainly an s&box-idiomatic way to do what you want.266267The schema under `raw/api-schema.json` is not shipped with the skill and is not for you to read directly; the reference files are the curated, searchable view of it.268269---270271*This file is a router. Reference files teach. Do not answer s&box questions from SKILL.md alone — open the relevant reference and read it.*272273---274> Source: [gavogavogavo/claude-sbox](https://github.com/gavogavogavo/claude-sbox) — distributed by [TomeVault](https://tomevault.io).275<!-- tomevault:4.0:skill_md:2026-06-17 -->