log #1: the entity system
Time interval:
2022-12-30/2023-01-14
I started out by working on the entity system for my game. It had to support the following things:
- collisions
- interactions (walk up to an entity and press a key)
- should be simple to use
- shouldn’t require annoying boilerplate
At first I went with a design similar to what I used in the unfinished RAIDER rewrite. It basically tried to emulate inheritance using umka’s interfaces:
// the "base class"
type EntPs* = struct {
...
}
type Ent* = interface {
getPs(): ^EntPs
draw()
interact()
...
}
This worked well, but had one major issue - it was pain to work with and required a lot of boiler plate, since every entity would have to implement lot of (often empty) methods. For this reason I decided to abandon this idea and try an event driven design instead. I extended tophat’s signals and got to work.
This worked out well, especially since most entities only do something when interacted with. Each entity also has a tag. The tag is a bitfield, which says what the entity supports etc. For example there is a tag for collisions or interactions.
I also added support for items, which can be picked up and then used. Example of a pocket watch item:
adding proped support
I also implemented PropEd definitions for the entities. I had to make some tweaks to how PropEd loads resources to make it usable as a library, but overall it was a simple task and I definitely plan on adding more PropEd defs later down the road.
loading entities from a file
Last thing I added was the ability to load entities from a file. I chose TOML as the format I will be using in the game (i use toml.um by skejeton to parse it. You can define basic properties of entities like this:
[watch]
t = 4
i = "watch"
[watch.s]
x = 2.0
y = 2.0
When the entity is loaded, it is available as res.ents["watch"]
. The image
is specified as a string. This is possible because all loaded images are also
accessible by name from a map. You can also specify an init function for an
entity.
res.registerEnt("box", function(e: world.Ent, ctx: any): world.Ent {
printf("initializing the box entity\n")
return e
}