groupsCommunity

Debugging and Profiling Tips

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

Debugging and Profiling Tips

Community guide for debugging s&box games based on official tools and developer recommendations.

Console Commands

Useful built-in commands (press ~ in game):

CODE
stat fps          # Show FPS counter
stat net          # Network statistics
stat physics      # Physics performance
showtriggers      # Visualize trigger volumes
showhitboxes      # Show collision hitboxes
mat_wireframe     # Wireframe rendering

Visual Debugging

Draw gizmos and debug info:

CSHARP
public class Weapon : Component
{
    protected override void OnUpdate()
    {
        // Draw debug ray for aim
        if (Input.Down("attack1"))
        {
            var start = WorldPosition;
            var end = start + WorldRotation.Forward * 10000;
            
            // Red line: aim direction
            DebugOverlay.Line(start, end, Color.Red, 0.1f);
            
            var tr = Scene.Trace.Ray(start, end).Run();
            if (tr.Hit)
            {
                // Green sphere: hit point
                DebugOverlay.Sphere(tr.EndPosition, 10f, Color.Green, 0.5f);
                // Normal arrow: surface normal
                DebugOverlay.Arrow(tr.EndPosition, tr.EndPosition + tr.Normal * 50f, 10f, Color.Yellow, 0.5f);
            }
        }
    }
    
    // Draw persistent gizmo in editor
    protected override void DrawGizmos()
    {
        // Shows weapon range in editor
        Gizmo.Draw.LineCircle(WorldPosition, WorldRotation, 1000f, 32);
    }
}

Logging Best Practices

CSHARP
public class GameManager : Component
{
    // Use Log.Category for filtering
    public static Logger GameLog { get; } = new("Game");
    public static Logger NetLog { get; } = new("Network");
    
    void StartGame()
    {
        GameLog.Info($"Game starting with {PlayerCount} players");
        
        if (PlayerCount < MinPlayers)
        {
            GameLog.Warning($"Low player count: {PlayerCount}/{MinPlayers}");
        }
    }
    
    void NetworkEvent()
    {
        NetLog.Verbose($"Received RPC from {Rpc.CallerId}");
    }
}

Network Debugging

CSHARP
public class NetworkDebugger : Component
{
    [ConCmd("debug_network")]
    public static void DebugNetwork()
    {
        Log.Info($"Is Host: {Network.IsHost}");
        Log.Info($"Is Client: {Network.IsClient}");
        Log.Info($"Connection Count: {Network.Connections.Count()}");
        
        foreach (var conn in Network.Connections)
        {
            Log.Info($"  - {conn.DisplayName} (ID: {conn.Id})");
        }
    }
    
    protected override void OnUpdate()
    {
        // Visualize ownership
        if (Gizmo.IsSelected)
        {
            var ownerText = Network.IsOwner ? "OWNER" : (IsProxy ? "PROXY" : "HOST");
            Gizmo.Draw.Text(ownerText, WorldPosition, "white", 24f);
        }
    }
}

Performance Profiling

CSHARP
public class PerformanceMonitor : Component
{
    private List<float> _frameTimes = new();
    
    protected override void OnUpdate()
    {
        // Track frame times
        _frameTimes.Add(Time.Delta);
        if (_frameTimes.Count > 100) _frameTimes.RemoveAt(0);
        
        // Log slow frames
        if (Time.Delta > 0.033f) // < 30 FPS
        {
            Log.Warning($"Slow frame: {Time.Delta * 1000:F1}ms");
        }
    }
    
    [ConCmd("perf_report")]
    public static void PerformanceReport()
    {
        var avg = _frameTimes.Average() * 1000;
        var max = _frameTimes.Max() * 1000;
        
        Log.Info($"Frame Time: {avg:F1}ms avg, {max:F1}ms max");
        Log.Info($"FPS: {1f / avg * 1000:F0}");
    }
}

Finding Memory Leaks

CSHARP
public class MemoryTracker : Component
{
    private static int _instanceCount = 0;
    
    public MemoryTracker()
    {
        _instanceCount++;
        Log.Info($"Created {GetType().Name}. Total: {_instanceCount}");
    }
    
    protected override void OnDestroy()
    {
        _instanceCount--;
        Log.Info($"Destroyed {GetType().Name}. Remaining: {_instanceCount}");
    }
}

Scene Debugging

CSHARP
[ConCmd("find_object")]
public static void FindObject(string name)
{
    var obj = Scene.FindObjectByName(name);
    if (obj.IsValid())
    {
        Log.Info($"Found: {obj.Name}");
        Log.Info($"  Position: {obj.WorldPosition}");
        Log.Info($"  Components: {string.Join(", ", obj.Components.GetAll().Select(c => c.GetType().Name))}");
        
        // Teleport player to it
        if (Scene.Camera.IsValid())
        {
            Scene.Camera.WorldPosition = obj.WorldPosition + Vector3.Up * 100;
        }
    }
    else
    {
        Log.Warning($"Object '{name}' not found");
    }
}

Source

  • Official debugging docs: https://sbox.game/dev/doc
  • Facepunch developer console commands
  • Community debugging tools and extensions
Was this helpful?