If you have a VR headset, go to https://mixedreality.mozilla.org/jumpy-balls/ and try the game!

Jumpy Balls is a simple game where you have to guide balls shot from a cannon towards a target. Use your controllers to drag blocks with different physical properties, making the balls bounce and reach their goal.

Behind Jumpy Balls

Developing a 3D application is a complex task. While 3D engines like three.js provide a solid foundation, there are still many different systems that must work together (eg: app states, flow, logic, collisions, physics, UI, IA, sound…), and you probably do not want to rebuild all this from scratch on each project.

Also, when creating a small experiment or simple technical demo (like those at https://threejs.org/examples), disparate parts of an application can be managed in an ad-hoc manner. But as the software grows with more interactions, a richer UI, and more user feedback, the increased complexity demands a better strategy to coordinate modules and state.

We created ECSY to help organize all of this architecture of more complex applications and ECSY-Three to ease the process when working with the three.js engine.

The technologies we used to develop Jumpy Balls were:

The ecsy-three Module

We created ecsy-three to facilitate developing applications using ECSY and three.js. It is a set of components, systems, and helpers that will (eventually) include a large number of reusable components for the most commonly used patterns when developing 3D applications.

The ecsy-three module exports an initialize() function that creates the entities that you will commonly need in a three.js application: scene, camera and renderer.

// Create a new world to hold all our entities and systems
world = new World();

// Initialize the default sets of entities and systems
let data = initialize(world, {vr: true});

And data will have the following structure, letting you access the created entities:

{
world,
entities: {
scene,
camera,
cameraRig,
renderer,
renderPass
}
}

It will also initialize internally the systems that will take care of common tasks such as updating the transforms of the objects, listening for changes on the cameras, and of course, rendering the scene.

Please notice that you can always modify that behaviour if needed and create the entities by yourself.

Once you get the initialized entities, you can modify them. For example, changing the camera position:

// Grab the initialized entities
let {scene, renderer, camera} = data.entities;

// Modify the position for the default camera
let transform = camera.getMutableComponent(Transform);
transform.position.z = 40;

You could then create a textured box using the standard three.js API:

// Create a three.js textured box
var texture = new THREE.TextureLoader().load( 'textures/crate.gif' );
var geometry = new THREE.BoxBufferGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { map: texture } );
mesh = new THREE.Mesh( geometry, material );

And to include that box into the ECSY world, we just create a new entity and attach the Object3D component to it:

var rotatingBox = world.createEntity()
.addComponent(Object3D, { value: mesh })

Once we have the entity created we can add new components to modify its behaviour, for example by creating a custom component called Rotating that will be used to identify all the objects that will be rotating on our scene:

rotatingBox.addComponent(Rotating);

And now we can implement a system that queries the entities with a Rotating component and rotate them:

class RotationSystem extends System {
execute(delta) {
this.queries.entities.results.forEach(entity => {
var rotation = entity.getMutableComponent(Transform).rotation;
rotation.x += 0.5 * delta;
rotation.y += 0.1 * delta;
});
}
}

RotationSystem.queries = {
entities: {
components: [Rotating, Transform]
}
};

For more info please visit the ecsy-three repository.

At this point in the development of ecsy-three, we have not defined yet all the components and systems that will be part of it, as we are following these goals:

  • Keeping the components and systems as simple as possible, implementing just the minimum functionality, so they can be used as small building blocks that can be combined together with each other to build more complex components versus mega components (and systems) very opinionated.
  • Add components and systems as we need them, based on our real needs of real examples, like Jumpy Balls, versus premature overengineering.
  • Verbosity over unnecessary abstraction and syntactic sugars.

This does not mean that in the future ecsy-three will not contain larger, more abstract and complex modules and syntactic sugar that accelerates programming in ecsy-three. Simply that moment has not arrived, we are still at an early stage.

What’s next

We will also bet on improving the ecosystem around Blender, so that the integration between Blender and the ecsy’s components and three.js is more comfortable and transparent, to avoid unneeded intermediate steps or “hacks” from the creation of an asset in Blender until we can use it in our application. Feel free to join the discussions at: