diff options
Diffstat (limited to 'composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt')
| -rw-r--r-- | composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt | 95 |
1 files changed, 78 insertions, 17 deletions
diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt b/composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt index bc34ac1..d7d4dda 100644 --- a/composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt +++ b/composeApp/src/commonMain/kotlin/coffee/liz/ecs/physics/CollisionTick.kt @@ -1,28 +1,89 @@ package coffee.liz.ecs.physics +import coffee.liz.ecs.Entity import coffee.liz.ecs.Rect +import coffee.liz.ecs.Vec2 import coffee.liz.ecs.World +import kotlin.math.min internal class CollisionTick<Outside>( - private val collisionResolver: CollisionResolver<Outside> -) { - fun runTick(world: World<Outside>) { - // Eh, fast enough for now. Don't need to do any fancy collision detection. There's always later to improve if - // it's that bad. - world.query(Collidable::class, Position::class).forEach { a -> - world.query(Collidable::class, Position::class).forEach { b -> - val aHitBoxes = a.get(Position::class).let { pos -> a.get(Collidable::class).hitboxes.map { - Rect(pos.vec2, it.dimensions) - }} - val bHitBoxes = b.get(Position::class).let { pos -> b.get(Collidable::class).hitboxes.map { - Rect(pos.vec2, it.dimensions) - }} - - val collisionDetected = aHitBoxes.any { a -> bHitBoxes.any { b -> a.overlaps(b) } } - if (collisionDetected) { - collisionResolver.resolveCollision(world, a, b) + private val collisionResolver: CollisionResolver<Outside>, + private val subgridDimension: Vec2 = Vec2(50f, 50f) +) : PhysicsTick<Outside> { + override fun runTick(world: World<Outside>) { + val subgridEntities = world.collidingBounds().split(subgridDimension).map { subgrid -> + subgrid.map { cell -> + world.query(Colliding::class, Position::class).filter { entity -> + entity.positionalHitBoxes().any { cell.overlaps(it) } } } } + + for (y in subgridEntities.indices) { + for (x in subgridEntities[y].indices) { + resolveCollisions(world, subgridEntities[y][x]) + } + } + } + + private fun resolveCollisions(world: World<Outside>, potentialCollisions: Collection<Entity>) { + potentialCollisions.forEach { from -> + potentialCollisions.filter { to -> + val fromColliding = from.get(Colliding::class) + val toColliding = to.get(Colliding::class) + + val canCollide = fromColliding.group.collideInto.contains(toColliding.group.collisionGroup) + if (!canCollide) { + return@filter false + } + + val fromHitBoxes = from.positionalHitBoxes() + val toHitBoxes = to.positionalHitBoxes() + + fromHitBoxes.any { a -> toHitBoxes.any { b -> a.overlaps(b) } } + }.forEach { collision -> propagateCollision(world, from, collision)} + } + } + + private fun propagateCollision(world: World<Outside>, from: Entity, to: Entity) { + collisionResolver.resolveCollision(world, from, to) + + val fromVelocity = from.takeIf { it.has(Velocity::class) }?.get(Velocity::class) ?: return + + val toVelocity = to.takeIf { it.has(Velocity::class) }?.get(Velocity::class) + ?: Velocity(Vec2(0f, 0f)) + val toColliding = to.get(Colliding::class) + + if (toColliding.propagateMovement) { + to.add(Velocity(toVelocity.velocity.plus(fromVelocity.velocity))) + } + } +} + +private fun Entity.positionalHitBoxes(): Collection<Rect> { + val pos = get(Position::class) + val hitboxes = get(Colliding::class).hitboxes + return hitboxes.map { Rect(pos.position.plus(it.topLeft), it.dimensions) } +} + +fun World<*>.collidingBounds(): Rect { + return query(Position::class, Colliding::class).map { entity -> + val topLeft = entity.get(Position::class).position + val dimensions = entity.get(Colliding::class).hitboxes.merge().dimensions + Rect(topLeft, dimensions) + }.merge() +} + +fun Collection<Rect>.merge(): Rect { + return reduce { a, b -> + val newTopLeft = Vec2( + min(a.topLeft.x, b.topLeft.x), + min(a.topLeft.y, b.topLeft.y) + ) + val dimensions = Vec2( + maxOf(a.topLeft.x + a.dimensions.x, b.topLeft.x + b.dimensions.x) - newTopLeft.x, + maxOf(a.topLeft.y + a.dimensions.y, b.topLeft.y + b.dimensions.y) - newTopLeft.y + ) + Rect(newTopLeft, dimensions) } }
\ No newline at end of file |
