Skip to content

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

Definition: Entity

An entity is a unique thing that holds information.

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.


Definition: Component

Individual pieces of data on an entitiy are called components.

For instance, a component could be a Location, the Sprite of a monster, the Health of a player.

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?
  1. The set operation gives a component to our entity. We'll learn more about it shortly.
  2. Notice this is just a regular class! We can add any class as a component, even if we didn't make it ourselves.
  3. This is the same as doing entity().apply { ... }
Why make an Entity class?

Notice how calling entity() returned Entity instead of a number. While it is useful to represent entities as numbers internally, if we wrote an operation like set for all numbers, someone could accidentally do 42.set(...) without ever making a 42nd entity!

So, we use Entity to write safer code and keep all the operations together. Click on the title below to see full documentation.

Tip: 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)
  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.

Can we add and set the same component?

Yes, all set components are also added ones. Adding again will not remove any data.


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)
  1. Tries to get A or stops if not present.
  2. Tries to get B or uses a default value.
  3. Tries to get C or sets and returns a default value.