We just released ECSY v0.4 and ECSY-THREE v0.1!

Since the initial release of ECSY, we have been focusing on API stability and bug fixing, as well as providing some features (such as component schemas) to improve the developer experience and provide better validation and descriptive errors when working in development mode.

The API layer hasn't changed that much since its original release, but we introduced some new concepts:

Component schemas: Components are now required to have a schema (unless you are defining a TagComponent). Defining a component schema is really simple; you just need to define the properties of your component, their types, and, optionally, their default values.

class Velocity extends Component {}
Velocity.schema = {
x: { type: Types.Number, default: 0 },
y: { type: Types.Number, default: 0 }
};

We provide type definitions for the basic JavaScript types, and also on ecsy-three for most of the three.js data types, but you can create your own custom types too.

Defining a schema for a component will give you some benefits for free, like:

  • Default copy, clone, and reset implementation for the component. Although you could define your own implementation for these functions for maximum performance.
  • Components pool to reuse instances to improve performance and GC stability.
  • Tooling such as validators or editors which could show specific widgets depending on the data type and value.
  • Serializers for components so we can store them in JSON or ArrayBuffers, useful for networking, tooling or multithreading.

Registering components: One of the requirements we introduced recently is that you must always register your components before using them, and that includes registering them before registering systems that use them too. This was initially done to support an optimization to Queries, but we feel that making this an explicit rule will also allow us to add further optimizations down the road, as well as just generally being easier to reason about.

Benchmarks: When building a framework like ECSY that is expected to perform many operations per frame, it’s hard to infer how a change in the implementation will impact the overall performance. So we introduced a benchmark command to run a set of tests and measure how long they take.

We can use this data to compare across different branches and get an estimate of how much specific changes are affecting performance.

ECSY-THREE

We created ecsy-three to facilitate developing applications using ECSY and three.js by providing a set of components and systems for interacting with ThreeJS from ECSY. In this release, it has gotten a major refactor, and we got rid of all the “extras” in the main repository to focus on the “core” API to make it easy to use with three.js without adding unneeded abstractions.

We introduce extended implementations of ECSY's World and Entity classes (ECSYThreeWorld and ECSYThreeEntity), which provide some helpers to interact with three.js' Object3Ds. The main helper you will be using is Entity.addObject3DComponent(Object3D, parent), which will add the Object3DComponent to the entity holding a three.js Object3D reference, as well as adding extra tag components to identify the type of Object3D we are adding.

Please note that we have been adding tag components for almost every type of Object3D in three.js (Mesh, Light, Camera, Scene, etc.), but you can still add any other you may need. For example:

// Create a three.js Mesh
let mesh = new THREE.Mesh(
new THREE.BoxBufferGeometry(),
new THREE.MeshBasicMaterial()
);

// Attach the mesh to a new ECSY entity
let entity = world.createEntity().addObject3DComponent(mesh);

If we inspect the entity, it will have the following components:

  • Object3DComponent with { value: mesh }
  • MeshTagComponent. Automatically added by addObject3DComponent

Then you can query for each one of these components in your systems and grab the Object3D by using entity.getObject3D(), which is an alias for entity.getComponent(Object3DComponent).value.

import { MeshTagComponent, Object3DComponent } from "ecsy-three";
import { System } from "ecsy";

class RandomColorSystem extends System {
execute(delta) {
this.queries.entities.results.forEach(entity => {

// This will always return a Mesh since
// we are querying for the MeshTagComponent
const mesh = entity.getObject3D();
mesh.material.color.setHex(Math.random() * 0xffffff);
});
}
}

RandomColorSystem.queries = {
entities: {
components: [MeshTagComponent, Object3DComponent]
}
};

When removing Object3D components, we also introduced entity.removeObject3DComponent(unparent), which will get rid of the Object3DComponent as well as all the tag components introduced automatically (for example, the MeshTagComponent in the previous example).

One important difference between adding or removing the Object3D components yourself and using these new helpers is that they can also handle parenting/unparenting.

For example, the following code will attach the mesh to the scene (internally, it will be doing sceneEntity.getObject3D().add(mesh)):

let entity = world.createEntity().addObject3DComponent(mesh, sceneEntity);

When removing the Object3D component we can pass true as parameter to indicate that we want to unparent this Object3D from its current parent:

entity.removeObject3DComponent(true);

// The previous line is equivalent to:
entity.getObject3D().parent.remove(entity.getObject3D());

"Initialize" helper

Most ecsy and three.js applications will need a set of common components:

  • World
  • Camera
  • Scene
  • WebGLRenderer
  • Render loop

So we added a helper function, initialize, in ecsy-three that will create all these for you. It is completely optional but is a great way to quickly bootstrap an application.

import { initialize } from "ecsy-three";

const { world, scene, camera, renderer } = initialize();

You can find a complete example using all these methods in the following glitch:

Developer tools

We updated the developer tools extension (read about them) to support the newest version of ECSY core, but it should still be backward-compatible with previous versions. We fixed the remote debugging mode that allows you to run the extension in one browser while debugging an application in another browser or device (for example, an Oculus Quest). You can grab them from your favorite browser extension store:

The community

We are so happy with how the community has been helping us build ECSY and the projects that have emerged from it.

We have a bunch of cool experiments around physics, networking, games, and boilerplates and bindings for pixijs, phaser, babylon, three.js, react, and many more!

It’s been especially useful for us to open a Discord where a lot of interesting discussions about both ECSY and ECS as a paradigm have happened.

What's next?

There is still a long road ahead, and we have a lot of features and projects in mind to keep improving the ECSY ecosystem, such as:

  • Revisit the current reactive queries implementation and design, especially the deferred removal step.
  • Continue to experiment with using ECSY in projects internally at Mozilla, like Hubs and Spoke.
  • Improving the sandbox and API examples.
  • Keep adding new systems and components for higher-level functionality: physics, teleport, networking, hands & controllers, etc.
  • Keep building new demos to showcase the features we are releasing.

Please feel free to use our GitHub repositories (ecsy, ecsy-three, ecsy-devtools) to follow the development, request new features, or file issues on bugs you find. Also, come participate in community discussions on our Discourse forum and [Discord] server.