Skip to main content

Engine Architecture

Meep is highly modular. At its core it consists of system, components and plugins.

A typical game will require many systems and objects to work together to create a compelling experience for the user. To facilitate this - we use concept of entities, components and systems or ECS for short.

Meep makes no assumptions about your game, if you run it in default configurations - it will have no systems at all and no components. This helps keep install sizes down and prevent compatibility issues. It's your game - you know best what it should look and feel like.

That said - meep does come with a plethora of systems that you can import and make use of for common features like 3D meshes, particles, sounds etc.

Component

A component defines some aspect of an object in your game. For example, if you want to have combat in your game - you might create a Health component like so:

Health.ts
class Health {
current: number
maximum: number
}

Beauty of ECS is that this component can be attached to any entity in your world such as a goblin, a house or a rock.

You might want to track position of your game objects by adding a Position component

Position.ts
class Position {
x: number
y: number
}

Entity

Entities are just integer IDs used to group components together. An easy way to think of entities and components is to imagine a table, where each row is an entity and each column represents some component class

EntityHealthPosition
1
3
6

In the example above we have 3 entities: 1, 3 and 6. Entity 1 has 2 components: Health and Position. Entity 3 has just the Position. Finally, entity 6 has no component at all, it is still a valid entity though.

Entities along with their components are stored in and managed by the EntityComponentDataset, and their representation is actually very close to that of a table in a database.

System

A system defines some behavior, such as playing sounds, moving objects on screen, communicating with a game server. Here's a sample system that let the player control a character:

MovementControlSystem.ts
// component used to indicate which objects we should be able to move
class Controllable {
canWalkForward: boolean = true
canWalkBack: boolean = true
walkingSpeed: number = 10
}

class MovementControlSystem extends System<Position, Controllable> {
// this will be called by the engine for every simulation tick with `time_delta` being in seconds
update(time_delta: number): void {
// bind keyboard keys 🡐 and 🡒
const keys = engine.devices.keyboard.keys;

const right = keys.right_arrow;
const left = keys.left_arrow;

const dataset = this.entityManager.dataset;

const updateEntity = (position: Position, control: Controllable) => {
// update a single entity that has both `Position` and `Controllable`

if (right.is_down && control.canWalkForward) {
position.x += time_delta * control.walkingSpeed;
}

if (left.is_down && control.canWalkBack) {
position.x -= time_delta * control.walkingSpeed;
}

};

dataset.traverseEntities([ Position, Controllable ], updateEntity);
}
}

Systems are managed by EntityManager, they can be added and removed at runtime. This means that you can completely re-configure how your game works without having to restart the engine.