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

Implementing Cross-System Communication via Interfaces

Use interface-based dependency injection to allow disparate systems like UI and Combat to interact without knowing each other's concrete implementations.

In this lesson, you will learn how to use interface-based dependency injection to allow your UI and Combat systems to communicate without needing to know anything about each other's underlying code.

The Problem of Tight Coupling

When your UIManager directly references a PlayerCombat script, you create a "hard dependency." If you ever want to change how combat works or swap the player character for an AI, your UI code breaks. This is exactly what we want to avoid in advanced architecture.

The Interface Solution

Instead of referencing a concrete class, we define an IDamageable or IHealthProvider interface. By injecting an object that implements this interface into our UI, the UI only knows that "something" has health, not what that thing is.

Here is how you define the contract and inject it:

// The contract defines the behavior, not the implementation
public interface IHealthProvider {
    float CurrentHealth { get; }
    float MaxHealth { get; }
}

// The UI component depends on the interface, not the Combat system
public class HealthBarUI : MonoBehaviour {
    private IHealthProvider _healthSource;

    // Dependency Injection via method
    public void Construct(IHealthProvider healthSource) {
        _healthSource = healthSource;
    }

    void Update() {
        // The UI simply asks for values; it doesn't care if this is a Player, NPC, or Boss
        UpdateFillAmount(_healthSource.CurrentHealth / _healthSource.MaxHealth);
    }
}

Decoupled Interaction

By using an interface, your PlayerCombat script simply needs to implement IHealthProvider. When the game starts, your DI container (like VContainer or a simple bootstrap script) injects the PlayerCombat instance into the HealthBarUI.

The HealthBarUI remains completely "blind" to the combat logic. It only cares that the injected object fulfills the requirements defined in the interface. This makes your UI reusable for any system—even non-combat ones—so long as they provide health data. You have effectively turned a rigid, interconnected system into a collection of plug-and-play modules that can be tested and swapped in isolation.

Key Takeaway: By coding to an interface rather than a concrete class, you create a strict contract that decouples systems, allowing your architecture to evolve without cascading breaking changes.