Mana Engine: Working with Components

Up until now, my posts have been focused on the systems and how they get scheduled. The other half of that is actually using the components each system has access to.

I don’t think I’ve explicitly stated it, but if you haven’t figured it out from my earlier posts, the Mana Engine is an Entity-Component-System (ECS) architecture. Being an ECS is essential to getting the scheduler to work, as systems indicate what components they can access.

Incidentally, the “entity” part of ECS is very minor in the Mana Engine. It effectively boils down to a unique integer ID that all components are mapped to. If two components are mapped to the same ID, then they are on the same Entity. There is no Entity class. There are, however, EntityViews.

An EntityView is basically a slice of a given entity. So while an Entity may have a dozen or so components bound to it, an EntityView could only be interested in a few of those components.

The World is sort of like the whole collection of components across all entities. In fact, components are stored on the World in tightly packed arrays. Since we want to be able to iterate over tuples of components, the World also needs a “view” analog that Entity/EntityView has.

Enter the WorldProxy. (I’m not quite sure why we don’t call it a WorldView, but the engine is still in development, so bear with us). World Proxies are extremely lightweight, and basically are just a way to iterate over EntityViews.

To help illustrate this, here’s an overly simple example of a system and the related components we’ll need to work on. All it’s going to do is move objects down by a gravity constant.

First we’ll declare the component type we want to work on.

And now we’ll create a Time singleton. Some other system will have write access to this singleton and update it’s values every frame. Because our Gravity system will have const access, we know it will run after the Time has been updated.

And here’s the system itself. I’m omitting some stuff like namespaces and some other methods that aren’t essential for this example. What we care about is the Update function.

I’ve commented it up pretty well. But there are some interesting “meta” things to point out here.

One, the code is very straightforward. Grab your data. Do your work on them. No dealing with deep hierarchies or needing to even think about the rest of the code in the game. You can focus in on just this system as an atomic unit.

Two, even though we haven’t written any code that deals with any multi-threaded concepts, the code will be run in parallel with many other systems in the game and will guarantee thread safety. So programmers of all skill levels can be very effective using this engine. As a bonus, the Mana Engine has some parallel-for features that can make this system go wide if it becomes a bottleneck. I’ll talk about that in a future post.

Three, if you ever mess things up, the compiler will stop you. Notice how I have to use the function GetMutable<DynamicObject() because I want mutable access. If the system only had const access to the component and I had used the method GetMutable<DynamicObject>(), it would have not even compiled because of a static_assert inside of WorldProxy.

For illustration’s sake, here is the same code without being littered with comments, and some other features being used that I didn’t want to get in the way of the example:

Pretty small and straightforward.

The above is a simple case, and often systems can be more complex. However, what we’ve found is that the engine works best when you break things down into several systems where each system does as simple of work as possible.

The reason for this is because of the engine attempting to parallelize systems. It can best do what it’s designed to when each task is tiny. It’s also easier to reason about what each system is doing when each system does just one thing. Because of this, in reality the above code is not far from code we’ve actually written in real usage.

As always, don’t hesitate to ask for clarification. You can message me on twitter (@tloch14) or comment here. I’ll be happy to answer any questions or inquiries about details.