Entity - rogerscg/era-engine GitHub Wiki

An entity is any object within the scene, and probably the most important piece of the ERA engine. An entity extends THREE.Object3D, giving it important properties such as position, rotation, and scale, as well as allowing for additional entities to be attached to it.

The Entity class also comes with a few features that ease development.

Building - Meshes and Physics Bodies

When creating an entity, you will need to define mesh and physics body generation functions that are eventually called by the build function. Let's say we want to create a Box entity for our scene. We'd start with:

const box = new Box().withPhysics().build();

The build function calls both generateMesh and generatePhysicsBody functions and should not be modified (unless super.build is called).

generateMesh() - Builds and returns the mesh (or root Object3D) that represents the entity visually.

generatePhysicsBody() - Builds and returns the physics body for the entity.

In our file box.js, we'd create the class to extend Entity and override the generateMesh and generatePhysicsBody base functions:

class Box extends Entity {
  /** @override */
  generateMesh() {
    // Define and return your THREE.Mesh here.
  }

  /** @override */
  generatePhysicsBody() {
    // Define and return your preferred physics engine body here.
  }
}

If you have a custom model loaded into ERA model storage (see the Models section), you can simply set the modelName property in the Box constructor and it will retrieve and set the mesh automatically.

class Box extends Entity {
  constructor() {
    super();
    this.modelName = 'cardboard-box';
  }
}

Updating - Listening to Engine ticks

Now that we've defined our Entity's appearance and physics behavior, we can give it life with the update method. In the base Entity class, the update method copies the mesh position and rotation to match the physics object. Once the super method is called, it's up to the developer to update the entity state, listen for user input, etc.

class Box extends Entity {
  ...
  
  /** @override */
  update() {
    // Push the box. We're using the Ammo physics engine in this example.
    this.physicsBody.applyCentralImpulse(new Ammo.btVector3(1, 0, 0));
  }
}

Controls

Controlling an entity is another critical part of the ERA engine. This section is tied heavily to the Controls and Bindings section of the wiki, so be sure to read up on that as well.

Within our Box class, we can defined Bindings and Actions from an object. Actions are labels for things that an entity can do, such as FORWARD, BACKWARD, FIRE, ROLL, etc. Each Action has a set of input types and keys:

const BOX_BINDINGS = {
  BACKWARD: {
    keys: {
      keyboard: 83,
      controller: '+axes1',
    }
  },
  FORWARD: {
    keys: {
      keyboard: 87,
      controller: '-axes1',
    }
  },
  ROLL: {
    keys: {
      keyboard: 32,
      controller: 'button0',
    }
  },
};

We'll also need to define a CONTROLS_ID constant for use by the Controls plugin.

const BOX_CONTROLS_ID = 'box';

Now that our bindings are defined, we can register them to the Controls plugin. Be sure to override the static GetBindings function as well as the getControlsId function. Also note the line at the bottom that actually registers the bindings to Controls.

class Box extends Entity {
   ...
   
  /** @override */
  static GetBindings() {
    return new Bindings(BOX_CONTROLS_ID).load(BOX_BINDINGS);
  }

  /** @override */
  getControlsId() {
    return BOX_CONTROLS_ID;
  }

  /** @override */
  update() {
    super.update();
    // Use the Entity method "getActionValue" to read user input.
    if (this.getActionValue(this.bindings.FORWARD)) {
      this.physicsBody.applyCentralImpulse(new Ammo.btVector3(1, 0, 0));
    }
  }
}

// Register the entity bindings to the Controls plugin.
Controls.get().registerBindings(Box);

Notes

You can merge bindings together to create your own form of controls "inheritance". For instance, let's say you have an Animal base entity and a Horse entity that extends it. You can have the basic bindings of an Animal in animal.js, then in Horse.GetBindings(), you would define it as:

static GetBindings() {
    return new Bindings(HORSE_CONTROLS_ID)
             .load(HORSE_BINDINGS)
             .merge(Animal.GetBindings());
}

You can view the Gamepad spec here

To see how you can let your users/players customize bindings, see Controls and Bindings.