groupsCommunity
Common Pitfalls and How to Avoid Them
s&box Common Pitfalls and How to Avoid Them
Community-compiled list of frequently encountered issues and their solutions, based on Discord support channels and official docs.
1. Component Reference Caching
The Pitfall: Calling GetComponent() every frame causes performance issues. The Fix: Cache references in OnAwake or OnStart:CSHARP
public class Player : Component
{
// ❌ Bad: Getting component every frame
protected override void OnUpdate()
{
GetComponent<Health>().Damage(10); // Expensive!
}
// ✅ Good: Cache the reference
Health HealthComponent { get; set; }
protected override void OnAwake()
{
HealthComponent = Components.Get<Health>();
}
protected override void OnUpdate()
{
HealthComponent?.Damage(10);
}
}2. Not Checking IsProxy in Multiplayer
The Pitfall: All clients run the same logic, causing desync. The Fix: Always check if you're the owner or proxy:CSHARP
protected override void OnUpdate()
{
// ❌ Bad: Everyone runs this
MovePlayer();
// ✅ Good: Only owner runs simulation
if (IsProxy) return;
MovePlayer();
}3. Forgetting GameObject.IsValid()
The Pitfall: Accessing destroyed GameObjects causes null reference exceptions. The Fix: Use IsValid() before accessing:CSHARP
void UpdateTarget(GameObject newTarget)
{
// ✅ Good: Check validity first
if (Target.IsValid())
{
Target.Tags.Remove("targeted");
}
Target = newTarget;
if (Target.IsValid())
{
Target.Tags.Add("targeted");
}
}4. Calling Destroy() on Components Wrong
The Pitfall: Component.Destroy() doesn't exist. The Fix: Destroy the GameObject, not the Component:CSHARP
// ❌ Bad: This won't compile
GetComponent<Bullet>().Destroy();
// ✅ Good: Destroy the GameObject
GameObject.Destroy();5. Scene.Load in Synchronous Context
The Pitfall: Forgetting await causes race conditions. The Fix: Always await scene loads:CSHARP
// ❌ Bad: Fire and forget
Scene.Load("next_level.scene");
ShowLoadingScreen(); // Might show after scene loads!
// ✅ Good: Proper async flow
async void LoadNextLevel()
{
ShowLoadingScreen();
await Scene.Load("next_level.scene");
HideLoadingScreen();
}6. Using LINQ in Hot Paths
The Pitfall: LINQ allocates and is slow for frequent operations. The Fix: Use regular loops for performance-critical code:CSHARP
// ❌ Bad: Allocates every frame
var enemies = Scene.GetAllObjects().Where(o => o.Tags.Has("enemy"));
// ✅ Good: Zero allocation
foreach (var obj in Scene.GetAllObjects())
{
if (obj.Tags.Has("enemy"))
{
ProcessEnemy(obj);
}
}7. Modifying Sync Properties from Non-Owners
The Pitfall: Changes get overwritten by the owner. The Fix: Only the owner modifies Sync properties:CSHARP
[Sync] public int Health { get; set; }
void TakeDamage(int amount)
{
// ✅ Good: Only owner applies damage
if (!Network.IsOwner) return;
Health -= amount;
}Source
- Official docs: https://sbox.game/dev/doc
- Discord community support patterns
- Facepunch developer recommendations
Was this helpful?