KorGE Physics

Some kind of games require some physics to work. KorGE provides a set of hit-testing and collision detection utilities and a port of Box2D out of the box:

Table of contents:

Hit Testing + Collision/Intersection Detection

You can define vectorial shapes associated to views to define its shape for standard out of the box collision detection and hitTesting.

The Graphics view uses its shapes for collision detecting with other shapes and views. For the rest of the views you can use the hitShape property defining

var View.hitShape: VectorPath?

Hit Testing

You can call the hitTest method with global coordinates to determine if a point is inside a View.

val View.hitTest(globalX: Double, globalY: Double)

Collision/Intersection Detection

There are two supported collision kinds/methods: GLOBAL_RECT and SHAPE. The GLOBAL_RECT just computes the global bounding box of the views and check if they are colliding. This method is the fastest available, but most of the times it is not enough. And the SHAPE method just uses the shape set in View.hitShape: VectorPath? and precisely checks if the two shapes are intersecting.

Manual checking (for example inside an addUpdater):

enum class CollisionKind { GLOBAL_RECT, SHAPE }
fun View.collidesWith(other: View, kind: CollisionKind = CollisionKind.GLOBAL_RECT): Boolean
fun View.collidesWith(otherList: List<View>, kind: CollisionKind = CollisionKind.GLOBAL_RECT): Boolean

Event-based methods:

fun View.onCollision(filter: (View) -> Boolean = { true }, root: View? = null, kind: CollisionKind = CollisionKind.GLOBAL_RECT, callback: View.(View) -> Unit): Cancellable
fun View.onDescendantCollision(root: View = this, filterSrc: (View) -> Boolean = { true }, filterDst: (View) -> Boolean = { true }, kind: CollisionKind = CollisionKind.GLOBAL_RECT, callback: View.(View) -> Unit): Cancellable

Be sure to filter out collision with the stage.

view.onCollision(filter = { it != this }) { // filters out collisions with the stage itself
	circle2.color = Colors.RED
}

If you use onCollision the collision will be triggered by the rectangular bounding box of the corresponding view.

Handy extensions for checking with shape precision:

fun View.collidesWithShape(other: View): Boolean
fun View.collidesWithShape(otherList: List<View>): Boolean
fun View.onCollisionShape(filter: (View) -> Boolean = { true }, root: View? = null, callback: View.(View) -> Unit): Cancellable

Using onCollisionShape a collision will be triggered using the hitShape of a view.

You can define your own hitShapes for costum views. Notice in the following sample animation, that the image view would originally have a rectangular boundingbox.

val planet = image(resourcesVfs["planet.png"].readBitmap())
	planet.hitShape {
		circle(planet.width/2, planet.width/2, 500.0)
	}

Video-tutorials

Box2D

You can use a port to Kotlin of the Box2D library:

korge {
...
	bundle("https://github.com/korlibs/korge-bundles.git::korge-box2d::7439e5c7de7442f2cd33a1944846d44aea31af0a##9fd9d54abd8abc4736fd3439f0904141d9b6a26e9e2f1e1f8e2ed10c51f490fd")
}

Example Project

Sample

suspend fun main() = Korge(quality = GameWindow.Quality.PERFORMANCE, title = "My Awesome Box2D Game!") {
	views.clearColor = Colors.DARKGREEN
	solidRect(300, 200, Colors.DARKCYAN)
	graphics {
		fill(Colors.DARKCYAN) {
			rect(-100, -100, 300, 200)
		}
		fill(Colors.AQUAMARINE) {
			circle(0, 0, 100)
		}
		fill(Colors.AQUAMARINE) {
			circle(100, 0, 100)
		}
		position(100, 100)
	}.interactive()
	worldView {
		position(400, 400).scale(20)

		createBody {
			setPosition(0, -10)
		}.fixture {
			shape = BoxShape(100, 20)
			density = 0f
		}.setViewWithContainer(solidRect(100, 20, Colors.RED).position(-50, -10).interactive())

		// Dynamic Body
		createBody {
			type = BodyType.DYNAMIC
			setPosition(0, 7)
		}.fixture {
			shape = BoxShape(2f, 2f)
			density = 0.5f
			friction = 0.2f
		}.setView(solidRect(2f, 2f, Colors.GREEN).anchor(.5, .5).interactive())

		createBody {
			type = BodyType.DYNAMIC
			setPosition(0.75, 13)
		}.fixture {
			shape = BoxShape(2f, 2f)
			density = 1f
			friction = 0.2f
		}.setView(graphics {
			fill(Colors.BLUE) {
				rect(-1f, -1f, 2f, 2f)
			}
		}.interactive())

		createBody {
			type = BodyType.DYNAMIC
			setPosition(0.5, 15)
		}.fixture {
			shape = CircleShape().apply { m_radius = 2f }
			density = 22f
			friction = 3f
		}.setView(graphics {
			fill(Colors.BLUE) {
				circle(0, 0, 200)
			}
			fill(Colors.DARKCYAN) {
				circle(100, 100, 20)
			}
			scale(1f / 100f)
		}.interactive())
	}
}

fun <T : View> T.interactive(): T = this.apply {
	alpha = 0.5
	onOver { alpha = 1.0 }
	onOut { alpha = 0.5 }
}