Entities
This guide is currently incomplete A lot more info will be added soon to give a proper overview needed to get started with Geary.
Definitions
Notice how broad this definition is. An entity could be a zombie, a place in the world, the sound made by a player's footstep. What makes our entity unique is an identifier
that represents it, in our case a 64-bit number we call EntityId
.
These are not inherently related to each other, for instance an entity can have a location but no sprite if it is invisible. However, components are very useful together. With a sprite and location, we can render something on screen!
Tip: Each component should hold specific data, i.e. be good at one thing. This lets us choose exactly the data we need to write clean, modular code.
Syntax
Let's have a look at creating entities and giving them components.
// Create an empty entity
val entity: Entity = entity()
entity.id // Get the unique id of this entity
// Define a Location component (2)
class Location(val x: Double, val y: Double, val z: Double)
// Set(1) a location to our created entity
entity.set(Location(0.0, 10.0, 0.0))
// Do both at the same time (3)
val anotherEntity = entity {
set(Location(0.0, 0.0, 0.0))
}
// Read the data we set
entity.get<Location>() // returns Location?
- The
set
operation gives a component to our entity. We'll learn more about it shortly. - Notice this is just a regular class! We can add any class as a component, even if we didn't make it ourselves.
- This is the same as doing
#!kotlin entity().apply { ... }
Typealiases.
Geary provides typealiases like GearyEntity
for most classes so you can avoid conflicts if you already have a class named Entity
.
Entity
operations
set
gives an entity a component with data
entity.set(SomeData())
set
takes a type parameter as a key. This is useful when working with another object-oriented application. For example, in Minecraft Player
extends LivingEntity
. We can set the player as a LivingEntity
like so:
entity.set<LivingEntity>(player)
get
reads a component of the given type
val data = entity.get<SomeData>() // returns SomeData? (1)
- Notice, the returned type is nullable. We need to handle the case when our entity doesn't have this component set.
add
assigns a component type to an entity, without attaching data
entity.add<Alive>()
add
is useful for marker components that don't need to store data. Later we will explore how this feature lets us create relations to other entities.
has
checks whether an entity has a set/added component
entity.has<SomeComponent>() // returns Boolean
remove
removes a set/added component of a given type
entity.remove<SomeComponent>()
with
runs code if an entity has all requested components set
entity.with { loc: Location, sprite: Sprite, health: Health ->
println("I have all of $loc, $sprite, and $health on me!")
}
Null safety
Kotlin's null safety is extremely handy when trying to access components, because we are usually not aware of all the components an entity could have.
Null safety ensures we know what to do when a component isn't present. Here are some common use cases:
entity.get<A>() ?: return // (1)
entity.get<B>() ?: B() // (2)
entity.getOrSet<C> { C() } // (3)
- Tries to get
A
or stops if not present. - Tries to get
B
or uses a default value. - Tries to get
C
or sets and returns a default value.