featured image
ECSY

What's new in ECSY v0.4 and ECSY-THREE v0.1

     - 

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 components’ 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:

Components 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 these components too. This was initially done to support an optimization to Queries, but we feel 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 be doing a lot of 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 estimation on how much specific changes are affecting the 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 provides some helpers to interact with three.js' Object3Ds.
The main helper you will be using is Entity.addObject3DComponent(Object3D, parent) that will add the Object3DComponent to the entity holding a three.js' Object3D reference, as well as adding extra tag components to indentify 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, …) but you could 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 the entity.removeObject3DComponent(unparent) that 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 or using these new helpers is that they can handle parenting/unparenting too.

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 & 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 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 on your favourite browser extension store:

The community

We are so happy with how the community has been helping us build ECSY and the project that have emerged out of them.

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 both ECSY or 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 as for example:

  • 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, …
  • 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.