groupsCommunity

Performance Best Practices

calendar_today May 3, 2026 schedule ~1 min read person patrickjr verified 50

s&box Performance Best Practices

Community guide for writing performant s&box code based on engine behavior and developer recommendations.

Object Pooling

Avoid frequent new GameObject() and Destroy() calls:

CSHARP
public class BulletPool : Component
{
    private Queue<Bullet> _pool = new();
    private const int POOL_SIZE = 50;
    
    void InitializePool()
    {
        for (int i = 0; i < POOL_SIZE; i++)
        {
            var bullet = new GameObject();
            bullet.Components.Create<Bullet>();
            bullet.Enabled = false;
            _pool.Enqueue(bullet.Components.Get<Bullet>());
        }
    }
    
    public Bullet GetBullet()
    {
        if (_pool.Count > 0)
        {
            var bullet = _pool.Dequeue();
            bullet.GameObject.Enabled = true;
            return bullet;
        }
        
        // Fallback: create new (but warn)
        Log.Warning("Bullet pool exhausted!");
        var newBullet = new GameObject();
        return newBullet.Components.Create<Bullet>();
    }
    
    public void ReturnBullet(Bullet bullet)
    {
        bullet.GameObject.Enabled = false;
        _pool.Enqueue(bullet);
    }
}

Component Caching Patterns

Cache at startup, never in Update:

CSHARP
public class Weapon : Component
{
    // Cache all needed components
    private ModelRenderer _renderer;
    private SoundPointComponent _sound;
    private List<Attachment> _attachments = new();
    
    protected override void OnAwake()
    {
        // One-time lookup
        _renderer = Components.Get<ModelRenderer>();
        _sound = Components.Get<SoundPointComponent>();
        _attachments = Components.GetAll<Attachment>().ToList();
    }
}

Scene Query Optimization

Minimize queries in hot paths:

CSHARP
public class EnemySpawner : Component
{
    private List<GameObject> _cachedEnemies = new();
    private RealTimeSince _lastCacheUpdate;
    
    protected override void OnUpdate()
    {
        // Refresh cache every second, not every frame
        if (_lastCacheUpdate > 1.0f)
        {
            _cachedEnemies = Scene.FindAllObjectsWithTag("enemy").ToList();
            _lastCacheUpdate = 0;
        }
        
        // Use cached list
        foreach (var enemy in _cachedEnemies)
        {
            if (!enemy.IsValid()) continue;
            UpdateEnemy(enemy);
        }
    }
}

String and Allocation Reduction

CSHARP
public class HUD : Component
{
    // ❌ Bad: Allocates every frame
    protected override void OnUpdate()
    {
        ScoreLabel.Text = $"Score: {Score}";
    }
    
    // ✅ Good: Only update when changed
    private int _lastScore = -1;
    protected override void OnUpdate()
    {
        if (Score != _lastScore)
        {
            ScoreLabel.Text = $"Score: {Score}";
            _lastScore = Score;
        }
    }
}

Physics Optimization

Use layers and collision rules:

CSHARP
void OptimizedTrace()
{
    // ✅ Good: Use collision rules instead of checking tags after
    var tr = Scene.Trace
        .Ray(start, end)
        .WithCollisionRules("weapon", "player")
        .WithoutTag("trigger", "decal")
        .Run();
}

Networking Efficiency

CSHARP
public class PlayerState : Component
{
    // ✅ Good: Batch sync properties, use interpolation
    [Sync(SyncFlags.Interpolate)]
    public Vector3 Position { get; set; }
    
    [Sync]
    public int Health { get; set; }
    
    // ✅ Good: Rate-limit network updates
    private RealTimeSince _lastHealthUpdate;
    
    void UpdateHealth(int newHealth)
    {
        if (_lastHealthUpdate < 0.1f) return; // Max 10 updates/sec
        Health = newHealth;
        _lastHealthUpdate = 0;
    }
}

UI Performance

CSHARP
// ✅ Good: Use BuildHash to prevent rebuilds
public class ScorePanel : Panel
{
    public int Score { get; set; }
    
    // Panel only rebuilds when hash changes
    public override int BuildHash() => Score.GetHashCode();
}

Source

  • Official optimization docs: https://sbox.game/dev/doc
  • Facepunch developer blog posts
  • Community benchmark results
Was this helpful?