I'm a gameplay programmer
focused on developing
engaging and unique experiences!

My Resume Learn more

Data-Oriented Boid Flocking with Entity-Component-Systems in C++

One of the most recent architectural patterns to come out of the game development industry is the Entity-Component-System (ECS). ECS is a data-oriented approach to organizing gameplay implementation that takes locality of reference into account. With a basic ECS framework I built over a 5 part series, I wanted to create a boid flocking simulation due to it being a good ECS use case with all the similar components that need to be updated. You can download the final result here!

Keyboard Controls:

W/S: Forward/Backward Movement

A/D: Left/Right Movement

Q/E: Up/Down Movement

Left/Right Arrow: Rotate Camera Left/Right

Up/Down Arrow: Increase/Decrease Goal Distance

Space: Attach/Detach Goal

Page Up/Down: Increase/Decrease Cohesion Value

Home/End: Increase/Decrease Goal Value

Escape: Exit Game

Controller Input

Along with keyboard controls, I also integrated XInput for a more intuitive analog movement and interaction experience by incorporating a static library made by Sai Upadhyayula.

Xbox Controller Input:

Left Stick: Horizontal Movement

Right Stick: Rotate Camera Left/Right

Left/Right Trigger: Vertical Movement

Left/Right Shoulder: Increase/Decrease Goal Distance

A: Attach/Detach Goal

D-Pad Up/Down: Increase/Decrease Cohesion Value

D-Pad Left/Right: Increase/Decrease Goal Value

Start: Exit Game

Sai Upadhyayula's XInput System

Sai's XInput system was very easy to use because its interface was designed to closely match a preexisting Keyboard Input system I was using. This made it so that no new API was really necessary to learn. It was also simple to integrate into my solution because the static library was an encapsulated module that required no dependencies or engine changes.

Something I learned while working with XInput was the need to implement a low-level Boolean gate to differentiate between On Pressed and On Released events. This takes form as:

if (!aIsPressed && IsKeyPressed(ControllerKeyCodes::A))
{
    aIsPressed = true;
    OnAPressed();
}

if (aIsPressed && !IsKeyPressed(ControllerKeyCodes::A))
{
    aIsPressed = false;
    OnAReleased();
}

I think if Sai abstracted this pattern through their interface, the system would be even easier and more intuitive to use!

Building On Preexisting Graphics and Physics Systems

Along with my ECS and Sai's XInput systems, I also built my Boid simulation on top of a preexisting graphics and physics system. Unfortunately, this was a bit difficult due to how the concept of a game object was structured by these systems based on my past designs of their interfaces.

The main problem I ran into was submitting a game object for rendering required a mesh, effect, and transform. This made it so that those three structures were tightly coupled together. On top of that, the transform was represented by a rigid body which meant a game object's orientation was directly controlled by the physics system. Ideally, the transform, rigid body, and mesh would all be separate components, but in the end I embraced my past designs and made a Render Component that encompassed meshes, effects, and rigid bodies. This made submitting game objects to the graphics system relatively simple with a render system built using my ECS.

However, I also created a Prefab system that built on top of a Mesh and Effect Builder which allowed me to iterate on meshes and effects without having to recompile! This let me define the look of my boids and their environment more quickly.

Takeaways

In the end, I've found creating engine systems requires taking into account low-level implementations with interface design. It's easier to build high-level abstractions if the low-level interfaces you're using are well designed. This can be done up to the point where artists and designers can start using engine systems, which allows for quicker iteration cycles.

I personally will always be amazed with how much design and abstraction that is taken for granted in game engines. It was also interesting to see that conventional software development wisdom like avoiding premature optimization and object-oriented programming are not as commonly practiced in low-level engine development. However, I do find using goto a bit strange if it doesn't match an implementation's preexisting style.

I like designing implementations as I go because I've found any initial plans almost always need to be modified as more information comes in. I figure the sooner I encounter the technical limitations to an approach is the sooner I can refine the overall design of my solution. However, I still find value in first considering alternatives so that the initial step can be in the right direction.

Finally, I've found good software architecture to be flexible and behave the way you initially expect it too. Sai's XInput interface comes to mind as it was designed with previous experience and use cases in mind. On the opposite end, bad software architecture is usually inflexible and needs to be changed to account for unexpected input. My game object abstraction that tightly coupled meshes, effects, and rigid bodies comes to mind.

No Comments Yet.

Leave a comment