Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
img
src Init block in World.body extension method is now optional. #132 Mar 7, 2018
README.md ktx-box2d: extension method for querying Mar 7, 2018
build.gradle Added callbacks API to Box2D bodies and fixtures building DSL. #65 May 11, 2017
gradle.properties Implemented basic ktx-box2d module features. #52 May 9, 2017

README.md

KTX: Box2D physics engine utilities

Utilities and type-safe builders for the default LibGDX 2D physics engine: Box2D.

Why?

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 builing DSL can help with that.

Guide

ktx-box2d provides some extensions and utilities that aim improve Box2D API:

  • createWorld factory method eases the construction of World instances.
  • World.body and Body extension methods provide Fixture type-safe building DSL that supports customizing fixtures with the following shapes:
    • box:PolygonShape as a box.
    • circle: CircleShape.
    • polygon: PolygonShape.
    • chain: ChainShape.
    • loop: looped ChainShape.
    • edge: looped EdgeShape.
    • fixture: a custom Shape passed as parameter.
  • FixtureDef.filter extension methods aim to simplify Filter API usage.
  • Body was extended with the following builder methods that ease creation of Joint instances:
    • gearJointWith: GearJoint.
    • ropeJointWith: RopeJoint.
    • weldJointWith: WeldJoint.
    • motorJointWith: MotorJoint.
    • mouseJointWith: MouseJoint.
    • wheelJointWith: WheelJoint.
    • pulleyJointWith: PulleyJoint.
    • distanceJointWith: DistanceJoint.
    • frictionJointWith: FrictionJoint.
    • revoluteJointWith: RevoluteJoint.
    • prismaticJointWith: PrismaticJoint.
    • jointWith: any Joint type supported by the custom JointDef passed as the method argument.
  • earthGravity is a constant that roughly matches Earth's gravity.
  • World.rayCast extension methods allow creating ray-cast callbacks with Kotlin lambda syntax.
  • World.query extension method allow creating AABB querying callbacks with Kotlin lambda syntax.

Usage examples

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
}

PolygonShape

Creating a Body with a custom 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
}

PolygonShape

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
}

CircleShape

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
}

ChainShape

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
}

ChainShape

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
}

EdgeShape

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
  }
}

CircleShape + PolygonShape + EdgeShape

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)
  }
}

CircleShape

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
}

PolygonShape + DistanceJoint

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
  }
}

Synergy

Pair this library with ktx-math for Vector2 factory methods, operator overloads and other math-related utilities.

Alternatives

  • 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.

Additional documentation

You can’t perform that action at this time.