Course contentsShow
← Unity Game Development
Lesson 454 of 8287. Advanced Game ArchitectureFree lesson

Decoupling Systems with C# Delegates

Implement basic delegates to create flexible callback mechanisms that allow objects to communicate without explicit hard-coded dependencies.

In this lesson, you will learn how to use C# delegates to create flexible callback mechanisms that allow systems to communicate without needing direct, hard-coded references to one another.

The Problem: Rigid Dependencies

Imagine a PlayerHealth script that needs to trigger a SoundManager to play a "Low Health" sound and an UIPanel to flash red. If PlayerHealth holds a direct reference to both, it becomes tightly coupled. If you later remove the SoundManager or switch to a different UI system, your code breaks.

The Solution: C# Delegates

A delegate acts as a "method signature blueprint." By using a delegate, an object can essentially say, "I don't know who is listening, but I have a notification for anyone who cares."

Instead of talking to specific scripts, your PlayerHealth script simply invokes a delegate variable. Other systems can then "subscribe" their methods to that delegate.

public class PlayerHealth : MonoBehaviour
{
    // Define the blueprint for methods that want to listen
    public delegate void HealthChangedAction(float currentHealth);
    
    // An instance of the delegate
    public HealthChangedAction OnHealthChanged;

    public void TakeDamage(float amount)
    {
        currentHealth -= amount;
        
        // Trigger the callback if anyone is listening
        OnHealthChanged?.Invoke(currentHealth);
    }
}

Decoupling the Listeners

Now, your UIPanel can listen to PlayerHealth without PlayerHealth ever knowing the UIPanel exists:

public class UIPanel : MonoBehaviour
{
    public PlayerHealth player;

    void OnEnable() => player.OnHealthChanged += UpdateHealthBar;
    void OnDisable() => player.OnHealthChanged -= UpdateHealthBar;

    void UpdateHealthBar(float health) 
    {
        // Update UI logic here
    }
}

By using this pattern, you keep your PlayerHealth logic clean and focused solely on health calculations. The UI and Sound systems remain entirely optional components; if they aren't in the scene, the PlayerHealth script continues to function perfectly without throwing null reference errors or requiring complex "if-exists" checks.

Key Takeaway: Delegates allow you to invert the flow of communication, enabling objects to broadcast changes without requiring explicit knowledge of the systems reacting to them.