This site uses cookies to ensure you get the best experience. More info
Got it!

Renderers - components for rendering



The problem

In my previous post I have put to the light a - yet - small issue of rendering logic being placed inside GameObject class.

public override void Render(Malison.Core.ITerminal terminal)
{
    terminal[Position - Game.ViewPortOffset][TermColor.White, TermColor.Black].Write((int)Glyph.CharacterInversed);
}

While that definitely may not feel redundant at first, it started appearing to be an issue.

When I introduced the concept of a Viewport, and how I could change the offset to change what is currently shown on screen out of a bigger world - the Game Object suddenly had to keep track of the viewport and do the math to figure out where things need to be rendered.

Now, imagine that I would suddenly change this idea and there would be no viewport but some kind of camera class and we would not need to do it anymore. Now, that means we have to remove the offsetting math and we are done right?

Not really. Imagine that there would be dozens of game objects implemented, in each of it we do mostly the same viewport offset subtraction and what if we would have more complex rendering methods?

Not only I would be repeating myself like a mad man but also every change there would bite me in the ass very easily.

The solution

My solution to this was quite simple. Firstly, I changed the Render method of the GameObject class.

public void Render(ITerminal terminal)
{
    foreach (Component component in Components)
    {
        Renderer renderer = component as Renderer;

        if (renderer != null)
            renderer.Render(terminal);
    }
}

As you can see now, we no longer define the rendering code within specific game objects because that responsibility has moved to renderers.

What is a Renderer

The renderers are a kind of component, designed to only take care of rendering code and nothing else besides that.

The difference between normal components is that they all subclass from a new abstract class - Renderer.

public abstract class Renderer : Component
{
    public Vector2D Offset
    {
        get;
        set;
    }

    public abstract void Render(ITerminal terminal);
}

The class itself is very simple. It introduced a new property - Offset that allows to configure the offset for drawing the characters on screen, relatively to the containing game object’s position.

Another difference is that now the abstract Render method has been moved from the GameObject class to the Renderer class.

Specific renderers will now need to implement it.

There is one more thing I had to change inside the Component class.

If you would for example want to get all renderers from some game object and write code like this:

var components = gameObject.GetComponentsOfType(typeof(Renderer));

The result would be wrong. We would get no components even if the game object would certainly contain some.

The reason for that is because inside the GetComponentOfType and GetComponentsOfType methods I had been checking the type wrong.

I was only doing a simple check of type == givenType. Which was not enough in the case of supplying an abstract class.

I had to add additional check and the resulting code looks like this:

x.GetType().IsSubclassOf(type) || x.GetType() == type

Now we check if the type is a subclass or the exact given type, so the method works properly.

Example of a renderer

Let’s take a look onto an example of the most basic renderer (which I’ll be probably mostly using) - the CellRenderer.

As the name suggest, the CellRenderer is responsible for rendering a single cell or in other words - a single Character.

public class CellRenderer : Renderer
{
    public Character Character
    {
        get;
        set;
    }

    public override void Render(ITerminal terminal)
    {
        if (Game.ViewPort.Contains(GameObject.Position + Offset))
        {
            terminal[GameObject.Position + Offset - Game.ViewPortOffset].Write(Character);
        }
    }

    protected override void OnStart()
    { }

    public override void Update()
    { }
}

For that reason we introduce a Character property for setting up given character to renderer.

The Render method implementation looks basically just like the previous one, that we extracted from the GameObject class.
Difference is the addition of the Offset property to allow some configuration of relative positioning of the drawn character.

The OnStart and Update methods are empty, but had to be declared due to them being defined as abstract. I was thinking about leaving them virtual in the base class and only overriding them if a need would arise, but for now I’ll leave them be. There probably won’t be many cases of such repetition.

Now, implementing such renderer we can change our Player class to work properly again.

[RequireComponent(typeof(PlayerMovementHandler))]
[RequireComponent(typeof(CellRenderer))]
public class Player : GameObject
{
    private CellRenderer renderer;

    protected override void OnStart()
    {
        renderer = (CellRenderer)GetComponentOfType(typeof(CellRenderer));

        renderer.Character = new Character((int)Glyph.CharacterInversed, TermColor.White, TermColor.Black);
    }
}

As you can see, the Player class got a bit smaller and now only configures it’s rendering component to draw given glyph in given colors.

And the result of that is that everything works as it should!

Wrapping up

I’m happy with this refactoring as it further reduces the bloat of the GameObject class. Now, the specific implementations only need to focus on configuration of the components they contain and not care about the rendering code at all.

The CellRenderer will be probably used in the very most cases of game objects, as it is a tile based game after all, although I may introduce more complex renderers for complex, multi-tile objects.

On the other hand, such objects could utilise multiple cell renderers to get the job done.

Also, while the renderer code is relatively small for now, I will be easily able to implement some kinds of effects such as shading or color manipulation much easier when the code responsible for rendering is contained in a single place.

As you might have noticed I put a heavy focus on good programming practices and I try to keep the code clean and short, remove heavy coupling or DRY up the code when a need arises.

This approach definitely slows down the development process but allows me to give a go at well designed architecture and have some fun with that.

Another reason behind it is the fact that I’m not creating this project for income.
My main focus (besides having fun with creating a game) is to put some clean, good code up for public, while meanwhile exploring so many different low-level features I never had a chance to implement!

I hope you understand my standing on this, because I have been a little worried that people might get the wrong idea on why I’m not forcibly pushing to have as much of the game done as possible, without much care about the code.
You see, I care about the code. Especially because this project is open source and it’s made from an unusually (at least for me) low-level ground.

That’s it for today’s post. If you have any questions, feel free to drop a comment below. See you next time!