Entity component system (ecs) for Kotlin and Java
repositories {
mavenCentral()
maven {
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
}
}
dependencies {
implementation("de.fridolin1:lecs4k:0.7-SNAPSHOT")
}//the engine is the core of the ecs
val engine = Lecs4kEngine()
//create and register an entity in one step (recommended)
val entity = engine.createEntity()
//create and register an entity in two steps
val entity2 = Entity()
engine.addEntity(entity2)//define a component
data class PositionComponent(val x: Float, val y: Float): EntityComponent()//add the component
entity.add(PositionComponent(4f, 2f))
//get the component
val position = entity.get(PositionComponent::class)
println("Position: ${position.x}|${position.y}") //would print "Position: 4.0|2.0"
//remove a component
entity.remove(PositionComponent::class)
println(entity.get(PositionComponent::class)) //would print "null"A Tag are a special kind of component. It's a short form of a normal component containing an enum.
To define your tags:
enum class LivingTag : CompInterface by entityTag<LivingTestTag>() {
LIVING, DEAD, DYING;
}You can use your tag like a normal component. But be aware that you can't have 2 tags of the same class.
entity.add(LivingTestTag.LIVING)
println(entity.contains(LivingTag::class)) //prints "true"
println(entity.get(LivingTag::class)) //prints "LIVING"
entity.add(LivingTestTag.DEAD)
println(entity.get(LivingTag::class)) //prints "DEAD"Define your EntityComponents::
data class PositionComponent(val x: Float, val y: Float): EntityComponent()
data class VelocityComponent(val vx: Float, val vy: Float): EntityComponent()Design your EntitySystem:
class MovementSystem: EntitySystem() {
lateinit var entities: Collection<Entity>
override fun addedToEngine() {
entities = engine.family().all(PositionComponent::class, VelocityComponent::class).get().entities
}
override fun update(delta: Float) {
for (entity in entities) {
val velocity = entity.get(VelocityComponent::class)!!
val position = entity.get(PositionComponent::class)!!
position.x += velocity.vx * delta
position.y += velocity.vy * delta
}
}
}There are 3 methods on engine.family() to filter you entities. Every entity, that matches these criteria are automatically in this family:
- all: The entity must have all the specified components
- one: The entity must have at least on of these components
- none: The entity mustn't have these components
Note: If one of these parameters is empty, it will be ignored.
Register the system and update the engine:
//create the engine and register the EntitySystem
val engine = Lecs4kEngine()
engine.addSystem(MovementSystem())
//create your entities and add the components
val entity = engine.createEntity()
entity.add(PositionComponent(4f, 2f))
entity.add(VelocityComponent(0.75f, 0.25f))
//let the engine process
//the delta parameter is the time in seconds since the last update.
val delta = 0.1
engine.update(delta)
//in libGDX, you can get your delta with:
val delta = Gdx.graphics.getDeltaTime()
engine.update(delta)Define your listener:
class EntityEventHandler: EntityListener() {
//Triggers before the entity is added to the engine
override fun entityAdded(entity: Entity, engine: Lecs4kEngine) {
//Do cool stuff
}
//Triggers after the entity was removed from the engine
override fun entityRemoved(entity: Entity, engine: Lecs4kEngine) {
//Do cooler stuff
}
//Triggers before the component is added to the entity
override fun componentAdd(entity: Entity, component: EntityComponent, engine: Lecs4kEngine) {
//Do the coolest stuff
}
//Triggers after the component was removed from the entity
override fun componentRemove(entity: Entity, component: EntityComponent, engine: Lecs4kEngine) {
//but don't destroy physics with stuff that's as cool as 0 Kelvin
}
}Register the listener:
engine.listeners.add(EntityEventHandler())Define your listener:
class FamilyUpdateHandler: FamilyListener() {
//Triggers after the entity was added to the family
override fun entityAdded(entity: Entity, engine: Lecs4kEngine) {
//do nice stuff
}
//Triggers after the entity was removed from the family
override fun entityRemoved(entity: Entity, engine: Lecs4kEngine) {
//do even more nice stuff
}
}Register the listener:
val family = engine.family().all(PositionComponent::class, VelocityComponent::class).get()
family.listeners.add(FamilyUpdateHandler()) Coming soon. If you have a time machine, I would be glad if you can send me the tutorial for java that I'm going to put here.
If you want to use Java but don't know anything about Kotlin, you can:
- check out this article: https://kotlinlang.org/docs/java-to-kotlin-interop.html
- ask the chatbot of your choice
Since Kotlin gets compiled to Java Byte Code too, these 2 languages are compatible.
This project is under the lgpl license (see the "LICENSE" file). Therefore, you are free to copy and edit and share the project, as long as you keep the original license. Furtheremore, you are free to use this project as a libery in any commercial or closed-source project. Read the license for more informations.
If you have questions, feature requests or bug reports, please create an issue.
If you have to contact me for other reasons, use the email fridolin.dev@tuta.com.