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
, andreset
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' Object3D
s.
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 byaddObject3DComponent
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.