Generic Components #75
-
Hi Simon, I'm working in a system to transfer component from an entity to another something like:
The component class is this one:
This works well when there is one type of component, but I would like to generalise so multiples component can be transferred (Eg. Health, Fire, etc..). Here is the test cases so it may help to run the code:
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 8 replies
-
Hi, That's an interesting use case. I will have a look on the weekend because right now I cannot check it, sorry. My first idea is that your TransferableComponent takes a type of ComponentType<*>. That way you should be able to get any component. Since we cannot use reified in this case the correct type information will be lost and most likely you need a cast to Component<*> in your system when you retrieve the component in a generic way. You should be then able to add it. Said that, it might not be possible yet because the current functions for getting and adding components are reified and therefore typesafe. I might need to add a non typesafe variant to support your use case. Can you explain a little bit what is the purpose of your scenario here and why you do it like that? E.g. why don't you remove/add the specific components directly where you currently use your TransferComponent? |
Beta Was this translation helpful? Give feedback.
-
@ernespn: I experimented a little bit now, and I see two solutions but still I'd like to know why you do it like that instead of modifying the components directly where you currently add your Transfer component. Option 1This is similar to what you tried and as I expected, it does not work currently in Fleks. I had to add another plusAssign operator that accepts a "wildcard" ComponentType to work. The result would be this: class Damage : Component<Damage> {
override fun type(): ComponentType<Damage> = Damage
companion object : ComponentType<Damage>()
}
class Life : Component<Life> {
override fun type(): ComponentType<Life> = Life
companion object : ComponentType<Life>()
}
class Transfer(
val target: Entity,
val cmpType: ComponentType<out Component<*>>
) : Component<Transfer> {
override fun type(): ComponentType<Transfer> = Transfer
operator fun component1(): Entity = target
@Suppress("UNCHECKED_CAST")
operator fun component2(): ComponentType<Component<*>> = cmpType as ComponentType<Component<*>>
companion object : ComponentType<Transfer>()
}
class TransferSystem : IteratingSystem(family { all(Transfer) }) {
override fun onTickEntity(entity: Entity) {
val (target, type) = entity[Transfer]
val cmp = entity[type]
entity.configure {
it -= type
it -= Transfer
}
target.configure { it += cmp }
}
}
class Test {
@Test
fun testDamage() {
val world = world {
systems {
add(TransferSystem())
}
}
val e1 = world.entity()
val e2 = world.entity {
it += Damage()
it += Transfer(e1, Damage)
}
with(world) {
assertTrue(e1 hasNo Damage)
assertTrue(e2 has Damage)
world.update(0f)
assertTrue(e1 has Damage)
assertTrue(e2 hasNo Damage)
}
}
@Test
fun testLife() {
val world = world {
systems {
add(TransferSystem())
}
}
val e1 = world.entity()
val e2 = world.entity {
it += Life()
it += Transfer(e1, Life)
}
with(world) {
assertTrue(e1 hasNo Life)
assertTrue(e2 has Life)
world.update(0f)
assertTrue(e1 has Life)
assertTrue(e2 hasNo Life)
}
}
} Option 2This way works already in Fleks right now and imo it has the benefit that you are still getting the typesafety when transfering your components that way. The TransferComponent in this case contains a lambda that does the transfer stuff that you want to do. Here is how it looks: class Damage : Component<Damage> {
override fun type(): ComponentType<Damage> = Damage
companion object : ComponentType<Damage>()
}
class Life : Component<Life> {
override fun type(): ComponentType<Life> = Life
companion object : ComponentType<Life>()
}
class Transfer(
val action: EntityUpdateContext.() -> Unit
) : Component<Transfer> {
override fun type(): ComponentType<Transfer> = Transfer
companion object : ComponentType<Transfer>()
}
class TransferSystem : IteratingSystem(family { all(Transfer) }) {
override fun onTickEntity(entity: Entity) {
entity.configure {
it[Transfer].action.invoke(this)
it -= Transfer
}
}
}
class Test {
@Test
fun testDamage() {
val world = world {
systems {
add(TransferSystem())
}
}
val e1 = world.entity()
val e2 = world.entity {
it += Damage()
it += Transfer {
e1 += it[Damage]
it -= Damage
}
}
with(world) {
assertTrue(e1 hasNo Damage)
assertTrue(e2 has Damage)
world.update(0f)
assertTrue(e1 has Damage)
assertTrue(e2 hasNo Damage)
}
}
@Test
fun testLife() {
val world = world {
systems {
add(TransferSystem())
}
}
val e1 = world.entity()
val e2 = world.entity {
it += Life()
it += Transfer {
e1 += it[Life]
it -= Life
}
}
with(world) {
assertTrue(e1 hasNo Life)
assertTrue(e2 has Life)
world.update(0f)
assertTrue(e1 has Life)
assertTrue(e2 hasNo Life)
}
}
} Which option do you prefer? |
Beta Was this translation helpful? Give feedback.
@ernespn: I experimented a little bit now, and I see two solutions but still I'd like to know why you do it like that instead of modifying the components directly where you currently add your Transfer component.
Option 1
This is similar to what you tried and as I expected, it does not work currently in Fleks. I had to add another plusAssign operator that accepts a "wildcard" ComponentType to work. The result would be this: