A fast, flexible and simple to use entity component system
Kiwi
Kiwi is a performant and versatile entity component system focussing on fast iteration and a nice api.
To get started, read the usage guide below. A documentation site will be set up later.
Usage
The world
The world is the main object that controls the ecs.
var world = World()
defer { world.destroy() }
world.destroy()
is used to clean up memory. This should always be executed when the
world is not needed anymore.
Keep in mind that World
is a struct, so when you pass it around be sure to use inout
.
Components
Creating components is as simple as declaring a struct:
struct Position: Component {
var x: Int
var y: Int
}
For performance reasons you might want to declare an additional property on these structs, but this is not strictly required:
struct Position: Component {
static let id: Int = UUID().hashValue
var x: Int
var y: Int
}
Entities
An entity is spawned with a set of components:
let entityId = world.spawn(Position(x: 10, y: 10))
world.spawn(Position(x: 3, y: 5), Velocity(x: 1.5, y: 0.0))
The world.spawn(_ components: Component...)
function will return the id of the newly spawned entity.
Systems
Queries
Queries can be constructed as follows:
//===================
// Immutable queries
//===================
// Query all entities having a position component
world.query(Position.self)
.get() // required in every query immutable query
.forEach { (pos: Position) in // type hint here is neccesary!
print(pos)
}
// Query all entites having a position and a velocity component and their entity ids
world.query(Position.self, Velocity.self)
.getWithIds() // Besides querying the component, also query the entity ids
.forEach { (id: EntityId, pos: Position, vel: Velocity) in
// ...
}
//===================
// Mutable queries
//===================
// Query all entities having a position and velocity component mutably
world.queryMut(Position.self, Velocity.self)
.mutate { (pos: inout Position, vel: inout Velocity) // type hints and inout keyword neccesary
pos.x += vel.x
pos.y += vel.y
}
// Query all entities having a position component mutably as well as their entity ids
world.queryMut(Position.self)
.mutateWithIds { (id: EntityId, pos: inout Position) in
// ...
}
System Groups
system groups are currently unimplemented and are planned for a future release.
Flags
Components can’t be zero-sized. This is where a flag can be used.
Defining flags
enum Flags: FlagId {
case Player
case Enemy
}
Setting flags
let id = world.spawn()
world.setFlag(entity: id, Flags.Player)
Removing a flag
world.removeFlag(entity: id, Flags.Player)
Checking wether an entity has a flag
world.hasFlag(entity: id, Flags.Player)
Fltering queries with flags
world.query(Position.self)
.getWithIds()
.filter { (id: EntityId, _: Position) in world.hasFlag(entity: id, Flags.Player) }
.forEach { (_, pos) in
print(pos)
}
The hasFlags
function is also available.
Road map
- System groups
Contributing
Contributers are welcome to open an issue requesting new features or fixes or opening a pull request for them.
License
The library is currenlty licensed under LGPLv3.