Fritzy.io

Introducing Ape ECS (js!)

javascriptecsgamedev

cover image
Whenever someone announces an ECS library, or really anything to do with ECS, the post must describe what ECS is. These posts always get it wrong because it's impossible to explain without mixing in bits of implementation. Even the Wikipedia article falls into this trap. I will not break with tradition.

First of all, you can find Ape ECS at https://github.com/fritzy/ape-ecs and you can install it into your project with:

npm install ape-ecs

Okay, but what is ECS?

ECS stands for Entity-Component-System, which name the parts of the paradigm. It's used in game and simulation development.

Entities are uniquely identified and are defined by which Components or Component instances are associated with them (ugh, we're already getting into implementation).

Components are uniquely identified instances of data-types with association to an Entity.

Systems do work on Components.

The idea is that you keep your logic separate from your data, unlike Object-Oriented Programming where you encapsulate your data with your logic. But how? Well, we'd have to get into implementation details.

Generally you have a System which could be a function or a class (in Ape ECS you can override the built in System class or just use a function) that has a single job that it does like Gravity.

Most implementations have a way of querying Entities and Components beyond just getting all of the entities or getting all of a Component of a given type. Minimally you can do a Join or Union to get all of the Entities that have at least a set of Component types. Some ECS implementations have more advanced queries, like Ape ECS Queries.

class Gravity extends ApeECS.System {

update(currentTick) {

const frameInfo = this.world.getEntity('GameLoop')
.getOne('FrameInfo');
const entities = this.createQuery()
.fromAll('Position', 'Vector')
.execute();
for (const entity of entities) {
const vector = entity.getOne('Vector');
vector.y += frameInfo.deltaTime * 9.807;
vector.update();
}
}
}

The magic of ECS is that our Gravity system works on anything that has a Vector and Position. We could make it as specific as we want, but what's more important is the things we don't care about. Gravity would work on any Entity that has those Components, whether it has a Sprite, AI, Weapon, Effect, whatever. We don't have to depend on a hierarchy of classes to determine whether Gravity works on it.

Why use ECS though?

If your game is complicated, dynamic, or meant to be a simulation, a lot can be gained from separating your logic and your data. For one, you have dynamic composition of your entities.

Want the player to go from running around as a human to controlling a mech? Easy, just add your PlayerControlled component to the mech entity. Want to mutate your character to have an extra arm? Just add an extra Arm component and the equipping System will deal with it just like the other two arms.

Systems interact with each other more when they're decoupled. A bunch of them might act on or inspect the same Components, essentially piping inputs and outputs to each other. This leads to high levels of data-integration, which leads to Emergent Behavior. In the example of the Gravity system, it would interact indirectly with a Movement system that used Vectors to update their corresponding Position. There's a story about many systems interacting with each other in Dwarf Fortress to kill cats that is a classic example.

There are other benefits as well -- it tends to be more performant to run one function (System) which loops through many entities than it is to loop through many entities and run a series of methods on each. It's also easy to serialize your data and save your state when your data is separate. The Blizzard Overwatch architect talked about how it made it easier to make a competitive network game using ECS.

Okay, but Why Ape ECS?

It's performant, it's flexible, and it has some neat features.

Here is what makes it stand out:

I spent a lot of my hobby time in the last year, and especially quarantine, exploring prototypes to solving this problem to find the right implementation and features set.

There are a bunch of other ECS implementations out there for JavaScript, but most of them are fairly naive or merely instructive.

You could also use Mozilla's ECSY, which isn't a bad way to go. Some of the devs have pledged to keep working on it despite being layed-off.

Ape ECS 1.0

Today, I'm launching Ape ECS 1.0. The documentation (especially the API documentation) is comprehensive. It boasts 100% test coverage. I've run it through it's paces for usage and performance. Ape ECS is ready for you to use!

I'm super happy to get this released, especially given how difficult this year has been. I'm excited to finally talk about it more broadly and call it 1.0!

I look forward to hearing your questions, suggestions, and contributions. Open issues and pull requests on Github. Feel free to poke me on Twitter @fritzy or Discord (Fritzy#5972). Please make suggestions for the documentation as well!

Update 09/27: We had our first post-1.0 pull-request!

What's next for Ape ECS?

Software is never done! I will continue to work on Ape ECS.

If you want to work on these things, please reach out!

Special Thanks

Thanks to Ben Morse for early implementation feedback, documentation suggestions, and the TypeScript definitions!

Thanks to Jaime Robles for the banner image!