Here’s where I left off. Everything is made up of behaviors.
interface ICastSpell { void Behave(Spell spell); } class DungeonMonster { public IMove MoveBehavior {get; set; } public IAttack AttackBehavior {get; set; } public ICastSpell SpellBehavior {get; set } public void Move(Direction direction) { MoveBehavior.Behave(direction); } public void Attack(Direction direction) { AttackBehavior.Behave(direction); } public void CastSpell(Spell spell) { SpellBehavior.Behave(spell); } } class LightMold: DungeonMonster { public LightMold() { MoveBehavior = new Immobile(); AttackBehavior = new Defenseless(); SpellBehavior = new EvilSpellCast(); //LightMolds cast evil spells apparently. } } class Goblin: DungeonMonster { public Goblin() { MoveBehavior = new Mobile(); AttackBehavior = new Defensive(); SpellBehavior = new NoSpells(); //Goblins have no magical aptitude } } |
Remember, the idea is composition. Composition should provide flexibility. I plan to do it through the use of entities. So here’s my implementation of an entity:
type EntityId = EntityId of int type EntityType = | Person | Door | Stairs | Object type Entity = { Id : EntityId Type : EntityType } |
It is an integer (technically a single case discriminated union) with a bit of immutable meta-data.
An entity has nothing. All info about an entity is contained in components.
type PositionComponent = { EntityId : EntityId Position : Vector2i Mobile : bool } type PlayerComponent = { EntityId : EntityId IsHumanControlled : bool } type ResourceComponent = { EntityId : EntityId ResourceAmount : double } |
Entities and components are linked together only by the entity’s ID. Certainly a far departure from Object-Oriented convention. But does this remind you of anything?
Relational databases are fond of using an integer as a primary key to make nice foreign key. So the entity and components above could look like this
This has a couple benefits:
- Composition of behavior through whether an entity has a component(s) or not.
- No longer have to maintain giant object hierarchies
- Systems can operate on isolated information. Example: Field-of-view, collision detection, and spatial operations in general should only need to operate on a collection of PositionComponents. They don’t care what other components an entity may have.
- Serialization is much easier. This is important when loading or saving game state, sending game state over network, and logging.
There are other benefits as well, but those will become apparent over time and examples. Of course there are drawbacks too, but those will become apparent with time as well.