DEV Community

Pan Chasinga
Pan Chasinga

Posted on

Entity-Component-System (ECS) in JavaScript A-Frame

ECS

I have been developing in A-Frame, an opensource WebVR framework from Mozilla. What makes A-Frame very unique from WebGL library like Three.js are — first, it’s built on top of Three.js to be more declarative via HTML DOM and attributes, second, it’s catered toward WebVR, which means it comes with VR mode out of the box, and lastly and most importantly, it adopts the Entity-Component-System (ECS) Pattern.

I have not been in the game design scene, and thus this was the first time I’ve ever been exposed to ECS. In a nutshell, it is very similar to composition as a general design pattern. The difference at the abstraction level is composition focuses on the “HAS-A” in polymorphism while ECS focuses on applying behaviors to entities.

However, in Object-Oriented Pattern (OOP), any instance of a subclass “IS-A” instance of its superclass. This Darwinian abstraction model has been very useful for decades and serving as a great polymorphic model for the tech industry until recently. Somehow, we discovered, carrying over the baggage from your ancestor(s) isn’t always optimal or necessarily easy to comprehend. Moreover, multiple inheritance (sub-classing more than one super class) is almost always a mess because it suddenly raises an existential problem for a child instance whose class inherits more than one parent.

Interface, on the other hand, is a good abstraction. It does imply a light “IS-A” relationship, but thankfully it does not let other objects inherit it or act like a super being to them. It is nothing more than a loose gatekeeper who let any instance pass through the door and become a member of the club as long as it has the right set of properties or methods. Thus this means an instance can belong to any “clubs” it is allow to do as long as when it’s there it can do stuff others do and blend in. Somehow, as a programmer, you still need to trade off the new complexity of maintaining interface code with this flexibility. Also, when you look at it from another perspective, it can feel like a shallow inheritance that doesn’t go beyond one level.

Coming back to ECS. It is quite well-known in the game design and development circle. Imagine the Street Fighter-style fighting game where you select a character to play against the other side. The entity is an empty skeleton of a human (or unhuman) fighter which may owns a set of simple behaviors like punching, kicking, or jumping and a property like the HP (Health Point). The character you choose is actually a set of predefined components / behaviors which can be applied and enhance the entities. For instance, a Chun-Li component can modify a base entity’s jump behavior to become unique to Chun-Li, add Spinning-bird Kick move, and of course, apply the texture of the character. These components can also interact with other components i.e. Spinning-bird Kick can have a default damage of -5 HP, but when interact with an entity with a Psycho Crusher component, it can inflict a humble -1 HP.

The S in the ECS, the System, is not mentioned much in A-Frame. It is mentioned as an optional service layer that centralizes persistent state and control of all its registered components, much like the service in Angular. I am currently using A-Frame system to communicate with the Angular UI component to isolate the two frameworks as much as possible.

ECS is a very flexible pattern and I can see why it’s suitable for game development. It’s focused on decoupling and usability minus the attempt to achieve the bookish abstraction other design patterns are striving for.

For instance, here is an a-box primitive entity in A-Frame, which basically renders a 3D cube on the canvas:


<a-box color="#FFF" position="0 0 0"></a-box>

Enter fullscreen mode Exit fullscreen mode

In order to make a-box sings (actually, console log a message), you can register a sing component like this one:


AFRAME.registerComponent('sing', {
  schema: {type: 'string', default: "doh re me!"},
  init: function() {
    console.log(this.data);
  }
});

Enter fullscreen mode Exit fullscreen mode

And makes a-box adopt this component:


<a-box sing="helloooo" color="#FFF" position="0 0 0"></a-box>

Enter fullscreen mode Exit fullscreen mode

Then upon loaded, you will see the little log on the browser “helloooo”.

A component has a certain life-cycle hooks that allows us to control the timing of the behavior it carries. Let’s hook into update cycle and sing a bit louder:


AFRAME.registerComponent('sing', {
  schema: {type: 'string', default: "doh re me!"},
  init: function() {
    console.log(this.data);
  }
  // update 
  update: function() {
    alert(this.data);
  }
});

Enter fullscreen mode Exit fullscreen mode

Now, try setting the sing attribute to something else, maybe nothing:


let box = document.querySelector('a-box');
box.setAttribute('sing', '');

Enter fullscreen mode Exit fullscreen mode

The window pops up an alert with the default string “doh re me!”.

This entity-component relationship can be really flexible and powerful. Imagine you can add and remove elements, change colors, positions, movement, etc. base on DOM events. We have not yet even tread into the realm of WebVR yet, and already this pattern is sticking.

Originally published here.

Top comments (3)

Collapse
 
mr_mig_by profile image
Alexey Migutsky

Thanks for the article!

I would really love to see ECS being adopted for javascript apps more.

I can see several problems to be addressed to boost the adoption:

  1. This way of modelling looks foreign for many developers
  2. There are not much materials on how to use ECS with event-driven interactive systems
  3. There is no JS framework which will give basic integrations (e.g. IndexedDB) and complete data-ui dataflow.
Collapse
 
pancy profile image
Pan Chasinga

Thanks for the message Alexey. Personally I think ECS is just a combination of composition/mixin, OOP, and actor done right. However, many users are still very adept to OOP-first so it's a little hard to wrap around.

What do you think?

Collapse
 
navjavon profile image
Navjavon

Hi! Do you have any VR app using aframe and angular?