Dirty Flag is a simple optimization: track whether something changed, and only recalculate when it actually did. Instead of recomputing expensive work every frame, you mark data as “dirty” when it changes and defer the work until it’s needed.
This means expensive operations only run when necessary. You trade a boolean check for potentially heavy recalculations. For example:
- Recalculate pathfinding only when obstacles move, not every frame
- Rebuild UI layout only when content changes, not on every update
- Recompute stats only when equipment changes, not on every damage calculation
- Update mesh only when vertices actually changed
Inventory Stats Example
Without dirty flag—recalculates every time you ask:
public class Inventory
{
private List<Item> _items = new();
public int GetTotalWeight()
{
// Runs every call, even if nothing changed
int total = 0;
foreach (var item in _items)
total += item.weight;
return total;
}
}With dirty flag—recalculates only when needed:
public class Inventory
{
private List<Item> _items = new();
private int _cachedWeight;
private bool _isDirty = true;
public void AddItem(Item item)
{
_items.Add(item);
_isDirty = true; // Mark dirty on change
}
public void RemoveItem(Item item)
{
_items.Remove(item);
_isDirty = true; // Mark dirty on change
}
public int GetTotalWeight()
{
if (_isDirty)
{
_cachedWeight = 0;
foreach (var item in _items)
_cachedWeight += item.weight;
_isDirty = false; // Clean after recalculation
}
return _cachedWeight;
}
}Transform Hierarchy Example
Unity’s transform system uses this internally—child world positions are only recalculated when a parent moves.
public class CachedBounds : MonoBehaviour
{
private Bounds _cachedBounds;
private bool _isDirty = true;
// Call this whenever children change
public void MarkDirty() => _isDirty = true;
public Bounds GetBounds()
{
if (_isDirty)
{
_cachedBounds = CalculateCombinedBounds();
_isDirty = false;
}
return _cachedBounds;
}
private Bounds CalculateCombinedBounds()
{
var bounds = new Bounds(transform.position, Vector3.zero);
foreach (var renderer in GetComponentsInChildren<Renderer>())
bounds.Encapsulate(renderer.bounds);
return bounds;
}
}When to Use This
The pattern pays off when:
- The calculation is expensive (iteration, math, allocations)
- The value is read more often than it changes
- You have clear mutation points to set the flag
The magic: One boolean saves potentially thousands of redundant calculations. The cost is negligible—a branch prediction hit—and the savings can be massive for things like pathfinding, physics, or UI layout.