As the Praedium game is made with the usage of custom-tailored engine, the engine is upgraded, changed and created on-demand for any needs that arise during the development.
As a plenty of these small refinements happened over time I decided to highlight some of the more interesting changes and plans for the engine itself.
Creating game objects on demand
Previously, the objects were needed to be initialized on the initial level load manually. As obviously such approach is good, sooner or later I would have to initialize new game objects on the fly, and do so correctly.
This means, that every game object will need to be created along with any components defined by the
RequireComponent attributes and every callback method would need to be called and in proper time.
Fortunately, this is actually very easy to implement, and - once again - provides a clean API, similar to the one, known from Unity Engine.
We use the already implemented
AddGameObject method and, by using reflection, simply instantiate object of type
T, which is constrained to inherit from the
Destroying game objects
Whatever is created should also expose some API to be properly destroyed. In this case, we expose another overrideable callback -
The public method
Destroy is exposed to other game objects, which could force destruction of any other instantiated game object.
While calling this method, the object processes the internal callback and after that, requests removal from the Game by calling
Game.Destroy method for now simply removes the object from the entities collection.
Finding and accessing game objects
Another small yet important thing is the ability to easily access other game objects from within the game’s logic code.
So in order to comply with it, I have introduced two simple methods.
The first one accepts a
Bramble.Core.Rect instance to indicate the area from within it will return all game objects.
And the second one is to find the game objects by name. The
Name is a new property introduced to game objects, which can be optionally set, by the developer. By default it’s an empty string.
Disabling components on the fly
If we would like to, let’s say disable a single component so it does not get updated (or does not render if it’s a renderer) I have also added a simple property with a public access in order to do so.
It is then checked during Update/Render call and if the component is not enabled, no action is committed.
I have also introduced two new renderers -
PathRenderer, although their logic is probably going to change because of several different reasons.
Let’s start with the
BoxRenderer that I used for mouse selection handling.
BoxRenderer class is very simple and uses API of the Malison library.
Unfortunately, whenever we would want to use the worldspace coordinates to draw the rectangle, Malison wouldn’t render it properly on the terminal if the coordinates would be outside of it’s bounds, so for now, the renderer does not support it.
I will need to change the Malison library in order to allow such rendering, so, as much as it hurts me to show some
TODOs in the snippets I want to highlight the issue, which will probably not be that easy to resolve.
DrawBoxOptions enumeration type here is used in order to allow simple/double line characters for the box rendering.
PathRenderer was used by me to create a nice visualisation of generated paths for my pathfinding implementation.
It’s a very straightforward renderer that accepts a collection of coordinates and draws a given character on each of them.
This renderer might be changed to support edges of lines instead of complete path of all the points and rendering it that way, but for now it was sufficient.
This pretty much sums up most of the smaller details I have until now not covered in my posts.
As you can see, the engine is slowly getting more features and starting to look better and offer more utilities commit by commit.
I also will need to upgrade the Malison library again. Not only because of the weird behaviour of box rendering outside of terminal bounds, but also to support different type of terminals, and a multi-layer support.
Layering will be sooner or later a must-have, due to more complex rendering, or separating the GUI layer from the game layer.
It will also provide some kind of Z-index support to allow rendering order manipulation.
Meanwhile, I have been playing with threads to use more CPU cores for the game loop, but for now I have not yet fully figured it. So progress in this matter is rather stale.
Other than that I have been also playing around with designing some GUI elements and soon I’ll probably drop a new creative highlight.
That’s it for today’s post. See you next time!