Blazor GameDev – part 5: composition

Hi All! Welcome back to part 5 of our Blazor 2d Gamedev series. Today we’ll see how we can move away from procedural code using composition.

Last time we saw how easy it is to load a sprite and move it across the screen. The issue with this solution is that things will get awkward very quickly as soon as we start adding more and more objects to control and render.

So the goal for today’s exercise is to keep refactoring the code from Example 3 and create a reusable structure. The output on the screen will be exactly the same, but under the hood, it’s going to be much cleaner.

So, composition over inheritance.

The classic inheritance paradigm is built on the concept of a base class that exposes “generic” functionalities. By inheriting from it, we can add more features and get different behavior based on the context.

In game programming, usually we have a situation like this (warning, pseudo-code ahead) :

class GameObject{
    void Update();
    void Render();
}

class Spaceship : GameObject {}
class Player : Spaceship {}
class Enemy : Spaceship {}
class Boss : Enemy {}
class FinalBoss : Boss {}

We have a base class for all our game objects, from which we inherit to define a Spaceship. Then we’ll have to create a specific class for the Player and another one for the Enemies. From the Enemy class we create the Boss…and so on and so forth.
Every time we need to traverse a full hierarchy of classes.

If you need more/different functionalities, you’ll need a new type. Of course, there’s more to it, but it’s enough for the sake of this article.

With composition instead, we can define the behavior of an object by attaching components to it. Every component adds more functionalities, without the need to create a brand new class that encapsulates everything.

interface IComponent{}

class GameObject{ 
    List<IComponent> Components; 
}

class PositionComponent : IComponent {}
class RenderComponent : IComponent {}
class PlayerBrainComponent : IComponent {}
class EnemyBrainComponent : IComponent {}

Every Component is atomic and encapsulates a single piece of behavior and data. All you have to do is create it and then attach it to the GameObject. The hierarchy is pretty flat and definitely easier to follow.

Going back to Example 4, the first step is to define our IComponent interface:

public interface IComponent
{
    ValueTask Update(GameContext game);

    public GameObject Owner { get; }
}

Then we can create a GameObject class:

public class GameObject
{
        public ComponentsCollection Components { get; } = new ComponentsCollection();

        public async ValueTask Update(GameContext game)
        {
            foreach (var component in this.Components)
                await component.Update(game);
        }
}

As you can see, it holds a collection of components. In our game loop, we’ll call its Update() method which in turn will update the state of its components.

The next step will be to create components to hold the position, handle rendering, and movement logic and add them to a GameObject instance.

As usual, check the rest of the code in the repo for the full view and feel free to reach out to me if you feel you need more details 🙂

The next time we’ll see how we can leverage this mechanism to handle mouse input.

Stay tuned!

Blazor GameDev – part 5: composition
Scroll to top