For example, let's say that the player walks into an enemy and in response, a chain of events should occur (such as: his score goes up, the enemy dies, a cinematic starts, etc.)
I see two "proper" ways of accomplishing this:
Method 1:
There exists a global message bus in the game. Any system can emit events to it, and any other system can listen for some events from it. For example, the system handling the players score would look like this:
messageBus.listen("playerhitsenemy", e => {
score += e.monsterType;
});
and the collision system may look like this:
messageBus.emit("playerhitsenemy", { monsterType: 5 });
Similarly, other systems can listen for the same event, such that when something happens, the CPU immediately moves from processing the current system to the other system processing this event. That is, the emit
call is basically just a list of callbacks that are immediately invoked and sent the message.
Method 2:
The second method, instead, is to encode events / messages as entities themselves. That is, when an event occurs (such as the player hitting something in the collision detection system), this would happen:
let newEntity = new Entity();
newEntity.components.push(new MessageComponent());
newEntity.components.push(new MessagePlayerHitsEnemyComponent(5));
entityAdmin.spawnNewEntitiy(newEntity);
That is, a new "message" entity is created, and it is given the generic Message
component and the concrete event component type as well, MessagePlayerHitsEnemy
.
Then, all systems interested in these messages would have something like this in their execute
function:
for (let entity of entityAdmin.query(MessagePlayerHitsEnemyComponent) {
let monsterType = entity.getComponent<MessagePlayerHitsEnemyComponent>();
score += monsterType;
}
And then at the end of the frame there would be a system whose responsibility was deleting all messages, like so:
for (let entity of entityAdmin.query(MessageComponent) {
entityAdmin.remove(entity);
}
I cannot decide which is better. The first method seems simpler and probably more efficient in Javascript, considering it's just immediately invoking all the requested functions. However, the second one seems more "correct", in each system is iteratively processed. That is, first the collision system runs in its entirety, then the scoring system, etc. There is no "jumping around" from system to system during execution. Each system processes everything in its entirety, and then the second system processes everything it's interested in, etc.
So, in essence, the first method seems simpler and more efficient, while the latter method seems more "correct", pure, verbose, debuggable, and extendable. Which one should I pick?