Hello, and welcome to a new feature in KiwiJS v1.2.0! Today we’ll be looking at the upgraded Entity Component System (ECS), and how it makes everything better.
What is an ECS?
In an ECS, all objects in a game world are represented as entities; their properties are represented as components; and the game has systems to update entities and components. See more details at Wikipedia.
A pure ECS will represent the entire world in this fashion. KiwiJS uses ECS philosophy only to extend the functionality of basic game objects. However, this is still a tremendously powerful tool in your arsenal.
In practice, this means that you can add a Component
to a game object, and it will automatically go to work. You don’t have to call it; KiwiJS just knows to update components. This can be very useful for adding functionality to game objects in a one-step, it-just-works fashion.
NOTE: All Components Update
Components existed in previous versions of KiwiJS. Several game systems use components, including Animation
and ArcadePhysics
. However, due to a bug, they didn’t update automatically. A manual update was necessary for most components. Animation would update automatically thanks to a manual update within KiwiJS itself.
This is no longer the case. Any Component
on an entity will update if active.
This makes code much simpler to write and maintain. However, be warned: if you have written manual updates for components, those updates should be removed. If you keep the manual updates, components will update twice per frame.
Working with Components
Creating Components
In KiwiJS, you can use components as follows:
- Create
Component
- Add to entity
No further work is needed. The component will update automatically every frame.
You would do this as follows:
1 2 3 4 5 6 7 8 |
// Suppose there exists a Sprite, "mySprite" // Create component var component = new Kiwi.Component( mySprite, "myComponentName" ); // Add component mySprite.components.add( component ); |
However, this component does nothing just yet. How can we add functionality?
Every Component
has a blank update()
method intended precisely for this purpose. You can override the update with your own function and it will be executed every frame.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Define function var myFunction = function() { // It's always a good idea to call super, // even if you don't expect it to do anything. Kiwi.Component.prototype.update.call( this ); // This function will smoothly rotate an entity this.owner.rotation += 0.01 * this.owner.clock.rate; }; // Add function to component component.update = myFunction; |
This simple function is just the tip of the iceberg. The Animation and ArcadePhysics systems do a lot more work.
Managing Components
Sometimes you don’t want a component to function all the time. To control this, simply use its active
property. When active
is false
, the game will skip updates on that component. When set to true
, it returns to normal function.
If you want to remove a component altogether, use the entity’s component manager to remove it. Be warned, once removed in this way a component cannot be reused.
1 2 3 4 5 6 7 |
// Remove by object mySprite.components.removeComponent( component ); // Remove by name mySprite.components.removeComponentByName( "myComponent" ); |
Advanced Flow: Pre- and Post- Updates
Sometimes you may want to run some type of component apart from the main sequence of per-frame updates.
For example, consider a game in which robots jostle for control of a hill. The robot at the top of the hill every frame scores a point. You could add a component that checks to see whether a robot is at the top and if so gives a point. However, this could yield incorrect results if another robot later pushes that robot aside during the same frame.
You can run updates before and after the main update. The Component
has not one but three updates: preUpdate
, update
, and postUpdate
. preUpdate
is called just before the State updates. update
is called at the start of the State update. (If you override State.update, as is common, this will occur when you call Kiwi.State.prototype.update.call( this )
.) postUpdate
is called after the State updates.
These functions can help you structure your flow without having to write complicated control loops.
For example, consider the following implied logic.
1 2 3 4 5 6 7 8 |
component.update = function() { this.owner.jostleForPosition(); }; component.postUpdate = function() { this.owner.calculateScore(); } |
This component will process all the jostling for position in one pass, and then calculate the score. No positions will change during the scoring pass, so it will be perfectly accurate.
If you need more than these steps, we recommend that you create inactive components (so they won’t automatically update), register them to lists, and write loops to step through those lists. This is similar to the way in which components were handled in earlier versions.
Creating Custom Components
In many cases, you will wish to create entire prototypes for custom components. For example, your snail racing game will be much cooler if the snails leave mucus trails. Rather than code each snail with its own Component
, you could create a single extension of Component
and reuse that.
Here’s a template for creating custom components.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// Custom component template for KiwiJS var CustomComponent = function( params ) { // We recommend using a "params" object, as you can pass // any number of optional parameters in any order // as properties of the "params" object. // Call super with "owner" and "name" in params Kiwi.Component.call( this, params.owner, params.name ); // Assign custom parameters here }; Kiwi.extend( CustomComponent, Kiwi.Component ); CustomComponent.prototype.objType = function() { return "CustomComponent"; }; CustomComponent.prototype.preUpdate = function() { // Your custom preUpdate code here }; CustomComponent.prototype.update = function() { // Your custom update code here }; CustomComponent.prototype.postUpdate = function() { // Your custom postUpdate code here }; |
Advantages of ECS in KiwiJS
We see two big advantages to Entity Component Systems.
First, they are modular. You can apply only those components that you need. You don’t even have to check to see whether a given entity has a given component; you just update all its components, and they do the right job. We use this behind the scenes to differentiate between StaticImage
and Sprite
; the main difference is solely that Sprite
has an Animation
component attached automatically.
Second, they are low-maintenance. Have you ever written a big for
loop that steps through everything in your scene to do something vital? With ECS, you don’t have to do that. You can just add a component to all the objects, and it will automatically update. This leads to lighter update loops in your State
, and that makes for simpler code.
There are loads of other cool things you can do with ECS too, of course.
The Future
ECS is pretty exciting, and we’re looking to extend the philosophy. KiwiJS has always been about modularity and customisable game architecture. It’s safe to say that future versions will take this idea even further, allowing you to create self-perpetuating architectures faster and more conveniently than ever before.
Thank You
We hope you enjoy using the Entity Component System. Let us know how it works out, and also let us know if you have any suggestions for improving the system! We’re always looking to improve.
Next time: Set your controls for the heart of Time!
Recent Comments