-
Notifications
You must be signed in to change notification settings - Fork 20
Family
In case you need to iterate over entities with a specific component configuration
that is not part of a system then this is possible via the family
function
of a world
.
A family
keeps track of entities with a specific config and allows sorting
and iteration over these entities. Family
is used internally
by an IteratingSystem
. You can access it via the family
property.
The following example shows how to
get a family
for entities with a MoveComponent but without a DeadComponent:
fun main() {
val world = configureWorld {}
val e1 = world.entity {
it += Move(speed = 70f)
}
val e2 = world.entity {
it += Move(speed = 50f)
it += Dead()
}
val e3 = world.entity {
it += Move(speed = 30f)
}
// get family for entities with a MoveComponent and without a DeadComponent
val family = world.family { all(Move).none(Dead) }
// you can sort entities of a family
family.sort(compareEntityBy(Move))
// And you can iterate over entities of a family.
// In this example it will iterate in following order:
// 1) e3
// 2) e1
family.forEach { entity ->
// family also supports the typical entity extension functions like get, has, configure, ...
entity.configure {
// update entity components
}
}
}
Families also support FamilyHook
s that allow you to react when an entity gets added to, or removed from a family
. Such hooks can be created via the world's configuration.
Here is an example:
fun main() {
val world = configureWorld {
families {
// hook that reacts on entities that have a MoveComponent and do not have a DeadComponent
val family = family { all(Move).none(Dead) }
onAdd(family) { entity ->
}
onRemove(family) { entity ->
}
}
}
// this will trigger the 'onAdd' hook
val entity1 = world.entity {
it += Move()
}
// this will trigger the 'onRemove' hook
world -= entity1
// this will NOT trigger any hook because the entity is never part of the family
val entity2 = world.entity {
it += Move()
it += Dead() // <-- that's the reason
}
world -= entity2
}
Since Fleks 2.6 there is also a new way to create a FamilyHook
for an IteratingSystem
. There are two interfaces:
-
FamilyOnAdd
: a system with this interface must implement aonAddEntity
method. -
FamilyOnRemove
: a system with this interface must implement aonRemoveEntity
method.
Systems that are implementing any of those two interfaces are detected automatically during world configuration and a family hook for the system's family will automatically be created. It is possible to have multiple systems iterate over the same family. Their onAddEntity
/ onRemoveEntity
methods are called in the order of the systems of the world. If you also have a family hook defined in the world's configuration block then the logic of this hook will be called before the systems' hooks.
It is important to note that this feature does not add anything new. You can achieve the same thing with the world's family hook configuration. This is just a convenience feature to make it easier to define a hook for a system's family.
data class Move(var speed: Float) : Component<Move> {
override fun type() = Move
companion object : ComponentType<Move>()
}
class System1 : IteratingSystem(family { all(Move) }), FamilyOnAdd {
override fun onAddEntity(entity: Entity) {
// ...
}
}
class System2 : IteratingSystem(family { all(Move) }), FamilyOnAdd {
override fun onAddEntity(entity: Entity) {
// ...
}
}
fun main() {
val world = configureWorld {
systems {
add(System1())
add(System2())
}
}
// creating an entity will call System1's onAddEntity first, and then System2's onAddEntity
world.entity { it += Move(speed = 3f) }
}