Composing game objects (kinda like ECS)

I’m getting familiar with Phaser3 and would like to create new game objects or “entities” by composition.
For example I could have Entity class which itself does not contain almost any behavior. The engine would then add separate components to this Entity based on my configuration files.

So to create a movable character, I would create an Entity and then attach a Sprite component to it (which shows/renders the characters sprite) and Move script which contains the movement logic and which is ran in the update-method of the Entity. If you know the ECS (entity-component-system) pattern this might sound familiar to you.

I know Phaser3 isn’t really ECS engine so I was wondering which would be best method to implement the basic composition pattern while still leveraging Phaser3’s features. Looking into docs I immediately thought about using Container class as a base for my Entity and then attach Phaser Sprites, ´Images` etc. to it.

Docs however seem to warn about performance hit when using containers. Do you think this would be a problem provided I try to avoid too much of nesting when composing new entities?

Another option I guess could be to use Phaser’s GameObject as the base class for the Entity. WIth this I’m not sure how to add Sprites etc to the Entity since looks like the GameObject's api does not have add-method or anything like that?

So what do you think would be probably the best way to implement game entities with composition in Phaser?

What I’m doing to get a “sort-of ecs”, is using GameObjects as a base (could be a sprite, image, zone, depending on the needs) and mixins to add behaviours to the base class.

I would create a base class Entity that extend some GameObject, that base class has the most basic and common functionality, then create an Attack component, a Moveable component or whatever and then add those components to the base class, something like this:

class Entity extends Phaser.GameObject.Sprite  {
   // Also could extend Phaser.Physics.Arcade.Sprite if you need physics by default
}

// Create a class that extends the Entity
class Enemy extends Entity {
...
}

// Then add the behaviours as mixins:
Object.assign(Enemy.prototype, Attackable);
Object.assign(Enemy.prototype, Moveable);
// etc

It’s not a real ECS but the mixins approach served me well, and in part is how the different Phaser’s GameObjects are built.

Hope it helps :slight_smile:

1 Like

Thanks, that looks like something I wouldn’t have thought :slight_smile:

Couple of questions:

How do you create entities which do not extend Sprite but something else? Lets say you have Entities which should display Sprite, Polygon or nothing. Would this mean you would create three base-class entities, one for each of these variants?

How do you call the mixed in behaviors? Do you have ECS like system which calls them and has lists of all “attackable” entities? Or do you simply call script component in Entity’s update() or other normal methods? What does a simple behavior component look like in your system?

Hello @jackfreak, i just saw your example and i would like to ask, if you could make a real but small example about how to use this for an example maybe? :slight_smile:

Or atlease just for Atackable example also would be fine for me i guess :slight_smile:

It isn’t a real ECS as I said, just a way to attach behaviours or functionality to different entity-like instances. There is no System that manages the entities (although I guess you could create one), right now I just call the component functionality inside the update() method or wherever I need.

I’m using gameobjects as a base class, so if a need a type of entity that displays a Polygon I would use Phaser.GameObjects.Graphics, or if I need an Entity that is just a trigger area that dispatches ENTER and EXIT events when another entity gets in or out of it I would extend Phaser.GameObjects.Zone.

You could create three different base classes EntitySprite, EntityPolygon, EntityWhatever, one for each variant, or you could add the Entity as a mixin too.

Something like:

class Player extends Phaser.GameObjects.Sprite {
   constructor() {
      super();

      // Add the basic Entity as a mixin 
      // You can add the components  here in the constructor or in the class prototype
      Object.assign(this, Entity);
      Object.assign(this, Controllable);
      Object.assign(this, Collidable);

     update() {
       // Access component functionality
       const userInput = this.getInput();
       
       if(userInput.isPressingRight) {
         // Move your character to the right
       } else if(userInput.isPressingLeft) {
          // Move to the left
       }
     }
  }
}


// Then in Controllable.js
const Controllable = {
   hasControllableComponent: true,

   isPressingRight: false,
   isPressingLeft: false,

   // Hold Key objects from the Phaser Input manager 
   this._controlKeys: null,

   // you can initialize the component an pass dependencies here
   initControllableComponent: function(scene, someDependency) {
     this._controlKeys = scene.input.keyboard.addKeys({
			up: Phaser.Input.Keyboard.KeyCodes.UP,
			down: Phaser.Input.Keyboard.KeyCodes.DOWN,
			left: Phaser.Input.Keyboard.KeyCodes.LEFT,
			right: Phaser.Input.Keyboard.KeyCodes.RIGHT,
		});
  },

   getInput: function () {
       // Process which keys is the user pressing
      this.isPressingRight = this._controlKeys.right.isDown;
      this.isPressingLeft = this._controlKeys.left.isDown;
     },

   }
};

The GameObject base class acts as a Renderable component would in a real ECS. With a little more thought a Renderable component could be implemented but I think that just would be overkill.

For my current needs (and time schedule) this approach works for me, I can create different components and add them to different “entities”, a Player, an Enemy, a MoveablePlatform.

Another thing worth mentioning is that I’m using Groups to group them and manage them per type of entity. Groups are not visual groups, they are logical groups so when I need to update the enemies I would use the enemiesGroup and execute update on each child of the group:

this.movingPlatformsGroup.children.iterate(function(child) {
			child.update(time, delta);
		}, this);

// or set the auto update
this.enemiesGroup = this.add.group();
this.enemiesGroup.runChildUpdate = true; 

Sorry I can’t add more detail to my answer, I’m just starting with this too so trying different things to see what works best, also I have a deadline soon XD

4 Likes

Sorry I can’t add more detail or examples. You could take a look at how GameObjects are implemented inside Phaser3, that’s what actually gave me the idea to compose GO’s using mixins. :slight_smile:

// From Phaser source code
var Sprite = new Class({

    Extends: GameObject,

    Mixins: [
        Components.Alpha,
        Components.BlendMode,
        Components.Depth,
        Components.Flip,
        Components.GetBounds,
        Components.Mask,
        Components.Origin,
        Components.Pipeline,
        Components.ScaleMode,
        Components.ScrollFactor,
        Components.Size,
        Components.TextureCrop,
        Components.Tint,
        Components.Transform,
        Components.Visible,
        SpriteRender
    ],

    initialize:

    function Sprite (scene, x, y, texture, frame)
...
...

Hey @jackfreak, thanks for pointing out that :slight_smile:
I’ll look into that

Thanks you for example and explanation. This already gave me some ideas how to go about this using mixins. And good tip about looking into Phaser’s code about how the composition is done.

Given that the components are only data, why doesn’t simply use the setData/getData of Phaser GameObject class, to add components ?

I can’t get your example to work. I get a “Controllable is not defined” error when using Object.assign in the constructor. How are you referencing files? Are you using node.js?
I’m not using node (but js import/export) and the error comes up even if I try to import controllable.