Utilities and type-safe builders for the default libGDX 2D physics engine: Box2D.
Box2D API, being a direct port from C++, can be difficult to work with - especially for beginners. Bodies and fixtures construction code readability could certainly be improved. Kotlin type-safe building DSL can help with that.
ktx-box2d provides some extensions and utilities that aim improve Box2D API:
createWorldfactory method eases the construction ofWorldinstances.World.bodyandBodyextension methods provideFixturetype-safe building DSL that supports customizing fixtures with the following shapes:box:PolygonShapeas a box.circle:CircleShape.polygon:PolygonShape.chain:ChainShape.loop: loopedChainShape.edge: loopedEdgeShape.fixture: a customShapepassed as parameter. Note that in contrary to other fixture factory methods, the passed customShapewill not be disposed of unless thedisposeOfShapeparameter is set totrue.
FixtureDef.filterextension methods aim to simplifyFilterAPI usage.Bodywas extended with the following builder methods that ease creation ofJointinstances:gearJointWith:GearJoint.ropeJointWith:RopeJoint.weldJointWith:WeldJoint.motorJointWith:MotorJoint.mouseJointWith:MouseJoint.wheelJointWith:WheelJoint.pulleyJointWith:PulleyJoint.distanceJointWith:DistanceJoint.frictionJointWith:FrictionJoint.revoluteJointWith:RevoluteJoint.prismaticJointWith:PrismaticJoint.jointWith: anyJointtype supported by the customJointDefpassed as the method argument.
earthGravityis a constant that roughly matches Earth's gravity.World.rayCastextension methods allow creating ray-cast callbacks with Kotlin lambda syntax.World.queryextension method allow creating AABB querying callbacks with Kotlin lambda syntax.
Creating a new Box2D World without gravity:
import ktx.box2d.createWorld
val world = createWorld()Creating a new Box2D World with a custom gravity:
import ktx.box2d.createWorld
import ktx.box2d.earthGravity
import com.badlogic.gdx.math.Vector2
val world = createWorld(gravity = Vector2(0f, -10f))
val earth = createWorld(gravity = earthGravity)Creating a Body with a PolygonShape box Fixture:
import ktx.box2d.body
// Building body from scratch:
val body = world.body {
box(width = 2f, height = 1f) {
density = 40f
}
}
// Adding box polygon fixture to an existing body:
val fixture = body.box(width = 2f, height = 1f) {
density = 40f
}Creating a Body with a customized PolygonShape Fixture:
import ktx.box2d.body
import com.badlogic.gdx.math.Vector2
// Building body from scratch:
val body = world.body {
polygon(Vector2(-1f, -1f), Vector2(0f, 1f), Vector2(1f, -1f)) {
density = 40f
}
}
// Adding polygon fixture to an existing body:
val fixture = body.polygon(Vector2(-1f, -1f), Vector2(0f, 1f), Vector2(1f, -1f)) {
density = 40f
}Creating a Body with a CircleShape Fixture:
import ktx.box2d.body
// Building body from scratch:
val body = world.body {
circle(radius = 1f) {
restitution = 0.5f
}
}
// Adding circle fixture to an existing body:
val fixture = body.circle(radius = 1f) {
restitution = 0.5f
}Creating a Body with a ChainShape Fixture:
import ktx.box2d.body
import com.badlogic.gdx.math.Vector2
// Building body from scratch:
val body = world.body {
chain(Vector2(-1f, -1f), Vector2(-1f, 1f), Vector2(1f, -1f), Vector2(1f, 1f)) {
friction = 0.5f
}
}
// Adding chain fixture to an existing body:
val fixture = body.chain(Vector2(-1f, -1f), Vector2(-1f, 1f), Vector2(1f, -1f), Vector2(1f, 1f)) {
friction = 0.5f
}Creating a Body with a looped ChainShape Fixture:
import ktx.box2d.body
import com.badlogic.gdx.math.Vector2
// Building body from scratch:
val body = world.body {
loop(Vector2(-1f, -1f), Vector2(-1f, 1f), Vector2(1f, -1f), Vector2(1f, 1f)) {
friction = 0.5f
}
}
// Adding looped chain fixture to an existing body:
val fixture = body.loop(Vector2(-1f, -1f), Vector2(-1f, 1f), Vector2(1f, -1f), Vector2(1f, 1f)) {
friction = 0.5f
}Creating a Body with an EdgeShape Fixture:
import ktx.box2d.body
import com.badlogic.gdx.math.Vector2
// Building body from scratch:
val body = world.body {
edge(from = Vector2(-1f, -1f), to = Vector2(1f, 1f)) {
restitution = 1f
}
}
// Adding edge fixture to an existing body:
val fixture = body.edge(from = Vector2(-1f, -1f), to = Vector2(1f, 1f)) {
restitution = 1f
}Creating a dynamic Body with multiple Fixture instances:
import ktx.box2d.*
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType.DynamicBody
val body = world.body {
type = DynamicBody
circle(radius = 1f) {
restitution = 0.5f
filter {
categoryBits = 0x01
maskBits = 0x02
}
}
box(width = 2f, height = 2f) {
density = 40f
}
edge(from = Vector2(-1f, 1f), to = Vector2(1f, 0.5f)) {
friction = 0.7f
}
}Customizing Shape of a Fixture:
import ktx.box2d.*
val body = world.body {
// Shapes are available as the default `it` parameter of fixture
// building blocks. Of course, they can be renamed - in this
// example we renamed the CircleShape parameter to `shape`:
circle { shape ->
shape.radius = 0.5f
shape.position = Vector2(0.5f, 0.5f)
}
}Creating two Body instances joined with a DistanceJoint:
import ktx.box2d.*
val bodyA = world.body {
position.set(-1f, 0f)
box(width = 0.5f, height = 0.5f) {}
}
val bodyB = world.body {
position.set(1f, 0f)
box(width = 0.5f, height = 0.5f) {}
}
// Extension methods for all other joint types are also available.
val joint = bodyA.distanceJointWith(bodyB) {
length = 2f
}Adding callbacks invoked after creation of Body and Fixture instances:
import ktx.box2d.*
val body = world.body {
onCreate { body ->
// Will be called when the body and all of its fixtures are built.
}
circle {
onCreate { fixture ->
// Will be called when this particular fixture is created. Note
// that at invocation time the Box2D body of this fixture might
// not be fully constructed yet.
}
}
}Creating ray-casts:
import ktx.box2d.*
fun createRayCast() {
world.rayCast(startX = 0f, startY = 0f, endX = 1f, endY = 1f) { fixture, point, normal, fraction ->
// Will be called when this ray hits a fixture.
RayCast.CONTINUE
}
}Querying the world for fixtures overlapping an AABB:
import ktx.box2d.*
fun createQuery() {
world.query(lowerX = 0f, lowerY = 0f, upperX = 1f, upperY = 1f) { fixture ->
// Will be called when this query overlaps a fixture.
Query.CONTINUE
}
}Pair this library with ktx-math for Vector2 factory methods, operator overloads and other math-related
utilities.
- gdx-box2d-kotlin is the first published Kotlin Box2D utilities library
that originally inspired
ktx-box2d. - libgdx-utils-box2d is a set of Box2D utilities written in Java.








