In my previous post I have put to the light a - yet - small issue of rendering logic being placed inside
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.
My solution to this was quite simple. Firstly, I changed the
Render method of the
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 -
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
Specific renderers will now need to implement it.
There is one more thing I had to change inside the
If you would for example want to get all renderers from some game object and write code like this:
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
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:
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
As the name suggest, the
CellRenderer is responsible for rendering a single cell or in other words - a single
For that reason we introduce a
Character property for setting up given character to renderer.
Render method implementation looks basically just like the previous one, that we extracted from the
Difference is the addition of the
Offset property to allow some configuration of relative positioning of the drawn character.
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.
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!
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.
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!