Understand how s&box organizes game worlds: Scenes as the root container and GameObjects as the nodes that make up every object in your game.
A Scene is the root container for everything that exists in your game at runtime. Every mesh, light, player, trigger, and piece of logic lives inside a Scene as a GameObject. GameObjects in turn hold Components that give them behavior — but before getting to components, it helps to understand how the hierarchy itself works.
Scene inherits from GameObject, making it both the root node of the hierarchy and a special container that owns the physics world, the render world, the directory of all objects, and the frame tick.
// The currently active scene — available from anywhereScene active = Game.ActiveScene;// Create a new blank scenevar scene = new Scene();// Destroy a scene when you're done with itscene.Destroy();
Only one scene is “active” at a time, exposed via Game.ActiveScene. Code you write in components always runs inside the active scene’s tick, so Game.ActiveScene is the scene you’re operating in.
Scene exposes two helpers for tag-based search that iterate the full Directory:
// All GameObjects that have the "enemy" tagforeach ( var enemy in Scene.FindAllWithTag( "enemy" ) ){ Log.Info( enemy.Name );}// All GameObjects that have every tag in the listvar tagged = Scene.FindAllWithTags( new[] { "pickup", "active" } );
A GameObject is a node in the scene hierarchy. On its own it has a name, a transform (position / rotation / scale), an enabled flag, and a tag set. All actual behavior comes from the components attached to it.
// Construct directly — automatically parents to Game.ActiveScenevar go = new GameObject( "Player" );// Construct with a specific enabled statevar go2 = new GameObject( false, "Disabled Prop" );// Construct with an explicit parentvar child = new GameObject( parentGo, true, "Child" );
Every GameObject has one parent and can have any number of children. Setting Parent to null automatically re-parents to the scene root.
// Re-parent, preserving world positionchild.SetParent( newParent, keepWorldPosition: true );// Direct parent property (also keeps world position by default via SetParent)child.Parent = anotherParent;// Iterate childrenforeach ( var c in go.Children ){ Log.Info( c.Name );}// Walk the whole subtreeforeach ( var obj in go.GetAllObjects( enabled: true ) ){ Log.Info( obj.Name );}
IsRoot is true when a GameObject’s parent is the Scene itself. Use go.Root to climb to the top-most non-scene ancestor from anywhere in the hierarchy.
The Flags property on a GameObject controls editor and engine behaviour:
// Keep this object alive across scene loadsgo.Flags = GameObjectFlags.DontDestroyOnLoad;// Hide from the editor hierarchy and inspectorgo.Flags |= GameObjectFlags.Hidden;// Exclude from networkinggo.Flags |= GameObjectFlags.NotNetworked;// Only exist in the editor; don't spawn in-gamego.Flags |= GameObjectFlags.EditorOnly;// Ignore the parent's transform (like `position: absolute`)go.Flags |= GameObjectFlags.Absolute;
Setting DontDestroyOnLoad prevents the object from being cleaned up when a new scene loads. Make sure you manage its lifetime manually or it will persist for the entire session.
Here is a typical pattern: create a GameObject, configure it, add components, and parent it.
using var scope = Game.ActiveScene.Push(); // make this scene active for the scopevar container = new GameObject( "Enemies" );container.Tags.Add( "group" );for ( int i = 0; i < 5; i++ ){ var enemy = new GameObject( container, true, $"Enemy {i}" ); enemy.Transform.LocalPosition = Vector3.Forward * i * 100f; enemy.Tags.Add( "enemy" ); enemy.AddComponent<EnemyComponent>();}
Components
Add behavior to GameObjects with components
Component lifecycle
Understand when OnAwake, OnUpdate, and friends are called