DEV Community

Daniel Pottenger
Daniel Pottenger

Posted on

Extending MonoGame as a University Project

Introduction

So it's that time of year, when everyone ends University with a bang, and head on wards with a smile to their graduation.. not this time.

Covid-19 has proven to be a obstacle course when it comes to successfully completing a degree, and it hasn't been easy to go months without face-to-face contact with my dissertation supervisor, but it's over now.

Extending MonoGame

For my final year project, I decided to push MonoGame further and provide support for ease of development using an entity-component system architecture, along with support for easily getting projects rolling by developing a tool that allows game developers to easily define their custom user-interface using JSON.

It wasn't glamorous, and the implementation hasn't been anything to write home about, but designing system architecture is something I've always been extremely interested in.

A lot of game development courses these days offer some kind of module that allows students to work with game engines, but rarely do universities choose to use something like MonoGame to teach the fundamentals of game programming. This was really the motivation behind my project. I wanted to open up the use of something that doesn't offer ~quite~ as many features as Unity, or Unreal. My reasoning is that by removing all of the overhead that comes with using a commercial game engine, then you can start to understand really what's going on under the hood.

The Entity-Component System

The entity-component system has long been an ideal architecture for many game development projects, but are often developed in ways that only work with a single project. In my case, I needed to design something that was flexible.

Flexibility meant generics, so generics were predominately used throughout the project. I had generics for systems, generics for entities and also generics for components.

I also tried to stay away from base classes, and dealt solely with interfaces. This meant at least that users would be able to easily change the kind of functionality they required without having to understand what was going on in the base class.

Systems were another ball-park to tackle. They quickly became one of the most complicated things that I had to implement in the project.

Why?

So, the entity-component system aims to at least relieve some of the issues around CPU cache misses, so systems would rely on finding entities that had components that they were interested in. If this is the case, then we need some kind of repository that stored all the different types of component, while still conforming to the interface. C#'s lovely feature of Covariance and Contravariance really threw me off at this point, and I had to find alternative means for solving this problem.

I decided to settle on some kind of repository pattern, or service locator which the systems could use to find the lists of components they required. For example, in the case an entity gets given a render component, the relevant list of render components would store it, and then the system could pick it up when necessary. This caused another problem..

What if systems were responsible for many different types of components? What if, for a system to function as expected, an entity would require both a render and a physics component. I looked around some similar projects for how this kind of feature was implemented, and MOST if not all settled on the use of bit-masks. This meant that systems could very quickly determine which entities had all of the components that systems required to function.

Bit-masks

This was used in order to quickly determine what components an entity had, and what components a system wanted. For example,

Say we have a few components in the game, in this case, 4 components. This can be represented easily as a series of 4 flags, or a bit-mask:

0000

In the case above, the entity has no components, as all the flags are set to false.

If you then choose to add a render component, then it could set the corresponding flag on the entity.

0010

The entity now has a render component.

Systems can function in a similar way. You can have predefined bit-masks, that can then be used in bit-wise operations to quickly determine if a system matches the entity.

By using the AND bit-wise operator on both bit-masks, then the resulting bit-mask should be the same, so the system then knows that it can find the components related to the entity.

Conclusion

This post hasn't been as in-depth as my dissertation, in the slightest, but it helps to give a quick overview of what I developed throughout the year, while in lock-down, while also keeping my 3 children entertained.

I'm not extremely proud of what I accomplished, but it gave me the opportunity to learn a lot about what I'm interested in.

I hope you all have a great year, all things considered, and thank you for reading!

Dan

Top comments (0)