Learn how prefabs let you save and instantiate pre-configured GameObjects, and how the GameResource system underpins every asset in s&box.
s&box has two complementary asset concepts: prefabs — serialized GameObject hierarchies you can stamp into a scene at runtime — and resources — typed C# objects backed by files on disk. Understanding both lets you build content-driven games where spawnable actors, materials, models, and sounds are all first-class assets.
A prefab is a .prefab file that stores a complete GameObject hierarchy: the object tree, all component values, and nested prefab references. You create one in the editor by right-clicking a GameObject and choosing Save as prefab. At runtime you load and instantiate the prefab from C#.
// Load the cached prefab scene (read-only, do not modify)var prefabGo = GameObject.GetPrefab( "prefabs/enemies/grunt.prefab" );
GetPrefab calls PrefabFile.Load internally and returns the root GameObject of the cached prefab scene. The returned object is a read-only template — clone it rather than parenting it directly into your scene.
The idiomatic way to spawn a prefab is to use SceneUtility.Instantiate:
var spawnPoint = Transform.World;// Instantiate the prefab at a world transformvar enemy = SceneUtility.Instantiate( ResourceLibrary.Get<PrefabFile>( "prefabs/enemies/grunt.prefab" ), spawnPoint);
The following properties let you query whether a GameObject came from a prefab:
bool isInstance = go.IsPrefabInstance; // part of a prefab instancebool isRoot = go.IsPrefabInstanceRoot; // the root object of the instancestring sourceFile = go.PrefabInstanceSource; // path to the .prefab file
// Disconnect from the prefab source (becomes a regular GameObject)go.BreakFromPrefab();// Pull the latest values from the prefab source filego.UpdateFromPrefab();
Calling BreakFromPrefab() is irreversible at runtime. Any subsequent changes to the original .prefab file will not be reflected in the broken instance.
Every asset that lives on disk — materials, models, sounds, scenes, and your own custom data types — is represented by a Resource in C#. The engine provides two layers:
Class
Purpose
Resource
Base for native engine resources (Model, Material, Texture, SoundFile, …)
GameResource
Base for JSON-serialized C# assets you define yourself
Resource provides the identity and lifetime properties shared by all assets:
Resource r = Material.Load( "materials/dev/white.vmat" );string path = r.ResourcePath; // "materials/dev/white.vmat"string name = r.ResourceName; // "white"bool ok = r.IsValid; // false if the asset has been unloaded
Native resources (Model, Material, Texture, SoundFile, AnimationGraph, Shader) are loaded through their own static Load methods:
Model model = Model.Load( "models/citizen/citizen.vmdl" );Material mat = Material.Load( "materials/custom/hero.vmat" );SoundFile sfx = SoundFile.Load( "sounds/weapons/shotgun_fire.vsnd" );
Inherit GameResource to define your own asset type. Decorate it with [GameResource] to give it a file extension and editor icon. Properties decorated with [Property] are serialized to JSON automatically.
using Sandbox;[GameResource( "Item Definition", "item", "Defines a collectible item", Icon = "backpack" )]public sealed class ItemDefinition : GameResource{ [Property] public string DisplayName { get; set; } [Property] public string Description { get; set; } [Property] public Model PickupModel { get; set; } [Property] public float HealAmount { get; set; } // Called once after the asset is first loaded from disk protected override void PostLoad() { Log.Info( $"Loaded item: {DisplayName}" ); } // Called when the asset is hot-reloaded in the editor protected override void PostReload() { Log.Info( $"Reloaded item: {DisplayName}" ); }}
// Load by path (returns null if not found)var item = ResourceLibrary.Get<ItemDefinition>( "data/items/medkit.item" );if ( item is not null ){ Log.Info( item.DisplayName );}
The simplest approach: use a [Property] of the resource type and assign it in the editor. The asset picker shows a filtered list of compatible files.
public sealed class WeaponComponent : Component{ [Property] public Model WeaponModel { get; set; } [Property] public SoundFile FireSound { get; set; } [Property] public Material TracerMat { get; set; } [Property] public ItemDefinition ItemData { get; set; }}
FileReference<T> is a typed wrapper around a resource path string. Use it when you want to defer loading, store the path without creating a hard dependency, or serialize the path to JSON without loading the asset at deserialization time.
public sealed class SpawnPoint : Component{ // Stores the path; asset is loaded on first access [Property] public FileReference<PrefabFile> SpawnablePrefab { get; set; } protected override void OnUpdate() { if ( Input.Pressed( "attack1" ) ) { var prefab = SpawnablePrefab.GetAsset(); // loads if not already loaded if ( prefab is not null ) SceneUtility.Instantiate( prefab, Transform.World ); } }}
FileReference<T> is useful in data-driven designs where a designer needs to assign a prefab or asset at edit time without loading it until it is needed.
GameResource fires two virtual hooks you can override:
Method
When it fires
PostLoad()
After the resource is first loaded from disk
PostReload()
After the resource is hot-reloaded (e.g. a .item file is saved in the editor)
OnDestroy()
When the resource is unloaded and removed from ResourceLibrary
protected override void PostLoad(){ // Build derived runtime data from serialized properties _damageRange = new RangeInt( MinDamage, MaxDamage );}protected override void PostReload(){ // Invalidate caches when the file changes on disk _damageRange = new RangeInt( MinDamage, MaxDamage );}
Scenes and GameObjects
The hierarchy that prefab instances live inside
Components
Attach behavior and resource references to GameObjects