Skip to content

Implementing new enemies or actors

Nikolai Wuttke edited this page Aug 14, 2019 · 4 revisions

RigelEngine is based on the Entity-Component-System architecture. Initially, I applied this to enemies as well, creating a pair of component and system for each type of enemy. In the long run, this proved quite cumbersome, and it doesn't map well to the original Duke Nukem II engine, which is based on updating individual game objects one by one (classic game object architecture). So I decided to implement enemies using a single component/system pair, and provide a way to stick different types of "behaviors" into the same type of component. This mechanism is called BehaviorController.

Writing new BehaviorController-based actors/game objects

To implement a behavior controller, you need to declare a struct which has at least an update function:

namespace behaviors {

struct MyNewEnemy {
  void update(
    GlobalDependencies& dependencies,
    GlobalState& state,
    bool isOnScreen,
    entityx::Entity entity);
};

}

The update function will be called once every frame. It's meant to modify other components of the entity, like Sprite, to achieve what it needs.

Optionally, it may also have an onCollision handler (called when it collides with the world) and/or an onKilled handler (called when killed by the player):

namespace behaviors {

struct MyNewEnemy {
  void update(
    GlobalDependencies& dependencies,
    GlobalState& state,
    bool isOnScreen,
    entityx::Entity entity);

  // Optional
  void onCollision(
    GlobalDependencies& d,
    GlobalState&,
    const engine::events::CollidedWithWorld&,
    entityx::Entity entity);

  // Optional
  void onKilled(
    GlobalDependencies& dependencies,
    GlobalState& state,
    const base::Point<float>& inflictorVelocity,
    entityx::Entity entity);
};

}

Note: It's not necessary to inherit from anything. As long as your struct/class provides the right function signatures, they will get used.

Once you have that defined, add an entry to entity_configuration.ipp to create an entity for the actor ID you're working on implementing, and then assign your behavior controller into a BehaviorController component:

  entity.assign<BehaviorController>(behaviors::MyNewEnemy{});

For an example, look at enemies\boss_episode_1.hpp.

Using state machines

Movement, collision detection etc.