Table of Contents

Anti-Patterns

Things to never do. Each one has a "do this instead" alternative.


Polling for State Changes

// BAD
private void Update()
{
    if (_player.IsDead) { ShowGameOver(); }
}

// GOOD — subscribe to event
_player.Died += ShowGameOver;

FindObjectOfType / FindObjectsByType

Expensive scene-wide search. O(n) every call.

// BAD
var manager = FindObjectOfType<GameManager>();

// GOOD — service locator or dependency injection
var manager = ServiceLocator.Get<GameManager>();

God Classes

A single class doing everything — input, movement, health, UI, audio.

// BAD — 1000-line MonoBehaviour
public class Player : MonoBehaviour
{
    // handles input AND movement AND health AND UI AND audio ...
}

// GOOD — single responsibility, composed
public class PlayerHealth : MonoBehaviour { }
public class PlayerMovement : MonoBehaviour { }
public class PlayerInput : MonoBehaviour { }

Switch Statements for Type Dispatch

// BAD
switch (enemy.Type)
{
    case EnemyType.Melee: HandleMelee(enemy); break;
    case EnemyType.Ranged: HandleRanged(enemy); break;
}

// GOOD — polymorphism
enemy.Attack(); // each type implements its own behavior

Role Booleans

// BAD
public class Unit
{
    public bool isHealer;
    public bool isTank;
    public bool isDps;
}

// GOOD — separate types or interface
public interface IRole { void PerformRole(); }
public class Healer : IRole { }
public class Tank : IRole { }

String Concatenation in Hot Paths

// BAD — allocates every frame
private void Update()
{
    _label.text = "HP: " + _health + "/" + _maxHealth;
}

// GOOD — update only on change, use StringBuilder or cached string
private void OnHealthChanged(int current, int max)
{
    _label.text = string.Format("HP: {0}/{1}", current, max);
}

new Allocations in Update

// BAD — GC pressure every frame
private void Update()
{
    var results = new List<RaycastHit>();
}

// GOOD — pre-allocate, reuse
private readonly RaycastHit[] _hitBuffer = new RaycastHit[16];

Magic Numbers

// BAD
if (_health < 25) { Flee(); }

// GOOD
private const int FLEE_THRESHOLD = 25;
if (_health < FLEE_THRESHOLD) { Flee(); }

Empty Unity Callbacks

// BAD — Unity still invokes these, wasting cycles
private void Update() { }
private void FixedUpdate() { }

// GOOD — delete empty callbacks entirely

See Unity performance best practices.


SendMessage / BroadcastMessage

// BAD — string-based, no compile-time safety, uses reflection
gameObject.SendMessage("TakeDamage", 10);

// GOOD — direct call or event
_health.TakeDamage(10);

Non-Cached GetComponent in Loops

// BAD
private void Update()
{
    var rb = GetComponent<Rigidbody>(); // called every frame
}

// GOOD — cache in Awake
private Rigidbody _rb;

private void Awake()
{
    _rb = GetComponent<Rigidbody>();
}

Summary Table

Anti-Pattern Fix
Polling in Update C# events / delegates
FindObjectOfType Service locator / DI
God class Single responsibility, composition
Switch on type Polymorphism / interfaces
Role booleans Separate types
String concat in hot path Event-driven UI update
new in Update Pre-allocate buffers
Magic numbers Named constants
Empty callbacks Delete them
SendMessage Direct calls / events
Uncached GetComponent Cache in Awake