### CMake Minimum Requirements and Project Setup
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
Specifies the minimum CMake version required and defines the project name and languages. Ensure CMake version 3.22 or higher is used.
```cmake
cmake_minimum_required(VERSION 3.22)
project(boks2d_jni C CXX)
```
--------------------------------
### Complete Physics Game Loop in Kotlin
Source: https://context7.com/joaomcl/boks2d/llms.txt
This snippet shows a complete game loop for a physics simulation, including world setup, body creation, fixed timestep updates, rendering, and cleanup. It demonstrates how to manage a dynamic world with static boundaries and spawn various dynamic bodies. Ensure a fixed timestep is used for consistent physics.
```kotlin
import io.github.joaomcl.boks2d.core.*
import io.github.joaomcl.boks2d.math.*
import io.github.joaomcl.boks2d.shapes.*
import kotlin.random.Random
class PhysicsGame {
private val world = World(WorldDef(gravity = Vec2(0f, -10f)))
private val bodies = mutableListOf
()
private var accumulator = 0f
private val timeStep = 1f / 60f
private val subSteps = 4
init {
setupWorld()
}
private fun setupWorld() {
// Ground
val ground = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(8f, 0f)))
ground.createPolygonShape(ShapeDef(friction = 0.5f), Polygon.makeBox(10f, 0.5f))
// Walls
val leftWall = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(0f, 10f)))
leftWall.createPolygonShape(ShapeDef(), Polygon.makeBox(0.5f, 15f))
val rightWall = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(16f, 10f)))
rightWall.createPolygonShape(ShapeDef(), Polygon.makeBox(0.5f, 15f))
// Spawn initial bodies
repeat(50) { spawnBody() }
}
private fun spawnBody() {
val x = Random.nextFloat() * 14f + 1f
val y = Random.nextFloat() * 5f + 15f
val body = world.createBody(BodyDef(
type = BodyType.Dynamic,
position = Vec2(x, y)
))
if (Random.nextBoolean()) {
body.createCircleShape(
ShapeDef(density = 1f, friction = 0.3f, restitution = 0.5f, enableContactEvents = true),
Circle(Vec2.Zero, 0.2f + Random.nextFloat() * 0.3f)
)
} else {
body.createPolygonShape(
ShapeDef(density = 1f, friction = 0.4f, restitution = 0.3f, enableContactEvents = true),
Polygon.makeBox(0.2f + Random.nextFloat() * 0.2f, 0.2f + Random.nextFloat() * 0.2f)
)
}
bodies.add(body)
}
fun update(deltaTime: Float) {
// Fixed timestep accumulator
accumulator += deltaTime.coerceAtMost(0.25f)
while (accumulator >= timeStep) {
world.step(timeStep, subSteps)
accumulator -= timeStep
}
// Remove fallen bodies
bodies.removeAll { body ->
if (body.position.y < -10f) {
world.destroyBody(body)
true
} else false
}
// Maintain body count
while (bodies.size < 50) {
spawnBody()
}
// Process contact events
val events = world.getContactEvents()
events.hitEvents.forEach { event ->
if (event.approachSpeed > 5f) {
println("Hard impact at ${event.point}: ${event.approachSpeed} m/s")
}
}
}
fun render() {
bodies.forEach { body ->
val pos = body.position
val angle = body.angle
// Draw body at (pos.x, pos.y) rotated by angle
}
}
fun destroy() {
world.destroy()
}
}
// Usage
fun main() {
val game = PhysicsGame()
var lastTime = System.currentTimeMillis()
while (true) {
val currentTime = System.currentTimeMillis()
val deltaTime = (currentTime - lastTime) / 1000f
lastTime = currentTime
game.update(deltaTime)
game.render()
Thread.sleep(16) // ~60 FPS
}
game.destroy()
}
```
--------------------------------
### Pre-Solve Collision Callback for One-Way Platforms
Source: https://context7.com/joaomcl/boks2d/llms.txt
Implement custom collision logic before the physics response is calculated. This example shows how to create one-way platforms, disabling collision if the player is moving upwards into the platform.
```kotlin
// Pre-solve callback - called before collision response
world.setPreSolveCallback {
// Implement one-way platforms
val platform = if (shapeA.userData == PLATFORM_ID) shapeA else
if (shapeB.userData == PLATFORM_ID) shapeB else null
val player = if (shapeA.userData == PLAYER_ID) shapeA else
if (shapeB.userData == PLAYER_ID) shapeB else null
if (platform != null && player != null) {
// Disable collision if player is below platform
if (player.body.linearVelocity.y > 0) {
return@setPreSolveCallback false
}
}
true // Normal collision
}
```
--------------------------------
### Custom Friction Callback
Source: https://context7.com/joaomcl/boks2d/llms.txt
Define custom friction between colliding materials. This example sets a very low friction for ice and rubber, otherwise calculates the geometric mean.
```kotlin
// Custom friction/restitution callbacks
world.setFrictionCallback {
// Ice + rubber = low friction
if (materialIdA == ICE_MATERIAL || materialIdB == ICE_MATERIAL) {
return@setFrictionCallback 0.05f
}
// Default: geometric mean
kotlin.math.sqrt(frictionA * frictionB)
}
```
--------------------------------
### Custom Restitution Callback
Source: https://context7.com/joaomcl/boks2d/llms.txt
Set custom restitution (bounciness) for material combinations. This example makes two bouncy materials extremely bouncy, otherwise returns the maximum restitution.
```kotlin
world.setRestitutionCallback {
// Super bouncy combination
if (materialIdA == BOUNCY_MATERIAL && materialIdB == BOUNCY_MATERIAL) {
return@setRestitutionCallback 1.0f
}
// Default: max
maxOf(restitutionA, restitutionB)
}
```
--------------------------------
### Identify Objects with userData
Source: https://context7.com/joaomcl/boks2d/llms.txt
Use the userData property to associate shapes with your game objects for handling collision events. This example demonstrates linking shapes to game objects and applying damage based on impact speed.
```kotlin
// Game object class
data class GameObject(val id: Long, val name: String, var health: Int)
val gameObjects = mutableMapOf()
var nextId = 1L
// Create game object and physics body
val player = GameObject(nextId++, "Player", health = 100)
gameObjects[player.id] = player
val playerBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 5f)))
val playerShape = playerBody.createCircleShape(
ShapeDef(density = 1f, enableContactEvents = true, enableHitEvents = true),
Circle(Vec2.Zero, 0.5f)
)
playerShape.userData = player.id // Link shape to game object
// Create enemy
val enemy = GameObject(nextId++, "Enemy", health = 50)
gameObjects[enemy.id] = enemy
val enemyBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(5f, 5f)))
val enemyShape = enemyBody.createCircleShape(
ShapeDef(density = 1f, enableContactEvents = true),
Circle(Vec2.Zero, 0.4f)
)
enemyShape.userData = enemy.id
// Handle collisions using userData
world.step(1f / 60f, 4)
world.getContactEvents().beginEvents.forEach { event ->
val objA = gameObjects[event.shapeA.userData]
val objB = gameObjects[event.shapeB.userData]
if (objA != null && objB != null) {
println("${objA.name} collided with ${objB.name}")
}
}
world.getContactEvents().hitEvents.forEach { event ->
val objA = gameObjects[event.shapeA.userData]
val objB = gameObjects[event.shapeB.userData]
// Apply damage based on impact speed
val damage = (event.approachSpeed * 10).toInt()
objA?.let { it.health -= damage }
objB?.let { it.health -= damage }
}
```
--------------------------------
### Initialize and Step World
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Initialize the physics world with a specified gravity and advance the simulation by calling step(). The subStepCount parameter controls accuracy versus performance.
```kotlin
val world = World(WorldDef(gravity = Vec2(0f, -10f)))
world.step(1f / 60f, subStepCount = 4)
world.destroy() // always destroy when done
```
--------------------------------
### Create and Simulate a Boks2D World
Source: https://context7.com/joaomcl/boks2d/llms.txt
Initialize a physics world with custom gravity and simulation parameters. Ensure worlds are destroyed when no longer needed to free native resources.
```kotlin
import io.github.joaomcl.boks2d.core.World
import io.github.joaomcl.boks2d.core.WorldDef
import io.github.joaomcl.boks2d.math.Vec2
// Create a world with custom gravity (y-axis points up)
val world = World(WorldDef(
gravity = Vec2(0f, -10f), // Standard Earth gravity
enableSleep = true, // Bodies sleep when idle (saves CPU)
enableContinuous = true, // Prevent fast objects tunneling
restitutionThreshold = 1f, // Speed above which bouncing occurs
hitEventThreshold = 1f // Speed above which hit events fire
))
// Game loop - call once per frame with fixed timestep
val timeStep = 1f / 60f // 60 FPS physics
val subSteps = 4 // Sub-stepping for accuracy
while (gameRunning) {
world.step(timeStep, subSteps)
// Read body positions and update graphics here
}
// Cleanup when done
world.destroy()
```
--------------------------------
### Create Revolute Joints for Hinges and Wheels
Source: https://context7.com/joaomcl/boks2d/llms.txt
Demonstrates creating revolute joints to simulate hinges for doors or motorized wheels. Configure limits, motors, and control their properties at runtime.
```kotlin
import io.github.joaomcl.boks2d.joints.RevoluteJointDef
import kotlin.math.PI
// Create bodies for a door
val wallBody = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(0f, 2f)))
wallBody.createPolygonShape(ShapeDef(), Polygon.makeBox(0.2f, 2f))
val doorBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(1f, 2f)))
doorBody.createPolygonShape(ShapeDef(density = 1f), Polygon.makeBox(1f, 2f))
// Create hinge joint
val doorJoint = world.createRevoluteJoint(RevoluteJointDef(
bodyA = wallBody,
bodyB = doorBody,
localAnchorA = Vec2(0.2f, 0f), // Hinge point on wall
localAnchorB = Vec2(-1f, 0f), // Hinge point on door
enableLimit = true,
lowerAngle = 0f, // Can't go past closed
upperAngle = (PI / 2).toFloat(), // Opens 90 degrees max
enableMotor = false
))
// Create motorized wheel
val wheelBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(5f, 1f)))
wheelBody.createCircleShape(ShapeDef(density = 1f, friction = 0.9f), Circle(Vec2.Zero, 0.5f))
val axleBody = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(5f, 1f)))
val wheelJoint = world.createRevoluteJoint(RevoluteJointDef(
bodyA = axleBody,
bodyB = wheelBody,
localAnchorA = Vec2.Zero,
localAnchorB = Vec2.Zero,
enableMotor = true,
motorSpeed = 10f, // Radians per second
maxMotorTorque = 100f // Newton-meters
))
// Control motor at runtime
wheelJoint.revoluteJointSetMotorSpeed(20f)
wheelJoint.revoluteJointEnableMotor(true)
val currentAngle = wheelJoint.revoluteJointGetAngle()
```
--------------------------------
### Create Static and Dynamic Bodies
Source: https://github.com/joaomcl/boks2d/blob/main/Module.md
Create a static ground body and a dynamic ball body with specified shapes and physical properties. Static bodies do not move, while dynamic bodies are affected by the physics engine.
```kotlin
// static body - never moves (ground, walls, obstacles)
val ground = world.createBody(BodyDef(type = BodyType.Static))
ground.createPolygonShape(
ShapeDef(friction = 0.5f),
Polygon.makeBox(halfWidth = 10f, halfHeight = 0.5f)
)
// dynamic body - moved by the physics engine
val ball = world.createBody(BodyDef(
type = BodyType.Dynamic,
position = Vec2(0f, 10f)
))
ball.createCircleShape(
ShapeDef(density = 1f, friction = 0.3f, restitution = 0.6f),
Circle(center = Vec2.Zero, radius = 0.5f)
)
```
--------------------------------
### Create and Control a Wheel Joint in Kotlin
Source: https://context7.com/joaomcl/boks2d/llms.txt
Implement realistic vehicle suspension by combining revolute and prismatic joints. Configure spring stiffness, damping, and motor for driving and braking.
```kotlin
import io.github.joaomcl.boks2d.joints.WheelJointDef
// Create car chassis
val chassisBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 2f)))
chassisBody.createPolygonShape(ShapeDef(density = 1f), Polygon.makeBox(2f, 0.5f))
// Create wheel
val wheelBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(-1.5f, 1f)))
wheelBody.createCircleShape(ShapeDef(density = 1f, friction = 0.9f), Circle(Vec2.Zero, 0.4f))
// Connect wheel to chassis
val wheelJoint = world.createWheelJoint(WheelJointDef(
bodyA = chassisBody,
bodyB = wheelBody,
localAnchorA = Vec2(-1.5f, -0.5f),
localAnchorB = Vec2.Zero,
localAxisA = Vec2(0f, 1f), // Suspension direction
enableSpring = true,
hertz = 4f, // Suspension stiffness
dampingRatio = 0.7f, // Suspension damping
enableLimit = true,
lowerTranslation = -0.25f, // Suspension compression
upperTranslation = 0.25f, // Suspension extension
enableMotor = true,
motorSpeed = 0f,
maxMotorTorque = 50f
))
// Drive controls
fun accelerate() {
wheelJoint.wheelJointSetMotorSpeed(-30f) // Negative = forward
wheelJoint.wheelJointEnableMotor(true)
}
fun brake() {
wheelJoint.wheelJointSetMotorSpeed(0f)
wheelJoint.wheelJointSetMaxMotorTorque(200f) // High torque = strong braking
}
fun coast() {
wheelJoint.wheelJointEnableMotor(false)
}
```
--------------------------------
### Implement Mouse Joint for Dragging
Source: https://context7.com/joaomcl/boks2d/llms.txt
Create a MouseJointDef to drag a dynamic body towards a target point. Requires a static ground body as an anchor. Adjust stiffness and damping for desired behavior. Max force should be proportional to the body's mass.
```kotlin
import io.github.joaomcl.boks2d.joints.MouseJointDef
// Need a static body as anchor (required by Box2D)
val groundBody = world.createBody(BodyDef(type = BodyType.Static))
// Body to drag
val dragBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 5f)))
dragBody.createPolygonShape(ShapeDef(density = 1f), Polygon.makeBox(0.5f, 0.5f))
// Create mouse joint when user clicks/touches
var mouseJoint: Joint? = null
fun onMouseDown(mousePos: Vec2) {
// Find body under cursor using raycast or AABB query
val hit = world.castRayClosest(mousePos, Vec2(0f, -0.01f))
if (hit.hit && hit.shape?.body?.type == BodyType.Dynamic) {
mouseJoint = world.createMouseJoint(MouseJointDef(
bodyA = groundBody,
bodyB = hit.shape!!.body,
target = mousePos,
hertz = 5f, // Stiffness
dampingRatio = 0.7f, // Damping
maxForce = 1000f * hit.shape!!.body.mass // Proportional to mass
))
}
}
fun onMouseMove(mousePos: Vec2) {
mouseJoint?.mouseJointSetTarget(mousePos)
}
fun onMouseUp() {
mouseJoint?.let { world.destroyJoint(it) }
mouseJoint = null
}
```
--------------------------------
### Create and Use Sensor Shapes in Boks2D
Source: https://context7.com/joaomcl/boks2d/llms.txt
Demonstrates how to create sensor shapes for trigger zones and detect when other shapes enter or leave them. Ensure `enableSensorEvents` is set to true for event detection.
```kotlin
// Create a trigger zone
val triggerBody = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(10f, 2f)))
val triggerShape = triggerBody.createPolygonShape(
ShapeDef(
isSensor = true, // No collision response
enableSensorEvents = true // Required for events
),
Polygon.makeBox(halfWidth = 2f, halfHeight = 2f)
)
triggerShape.userData = 1L // Mark as trigger zone
// Player that can enter the trigger
val playerBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 2f)))
val playerShape = playerBody.createCircleShape(
ShapeDef(density = 1f, enableSensorEvents = true),
Circle(Vec2.Zero, 0.5f)
)
playerShape.userData = 2L // Mark as player
// Check sensor events after step
world.step(1f / 60f, 4)
val sensorEvents = world.getSensorEvents()
sensorEvents.beginEvents.forEach { event ->
println("${event.visitorShape.userData} entered sensor ${event.sensorShape.userData}")
}
sensorEvents.endEvents.forEach { event ->
if (event.sensorShape.isValid && event.visitorShape.isValid) {
println("${event.visitorShape.userData} left sensor ${event.sensorShape.userData}")
}
}
// Query current overlaps directly
val overlappingShapes = triggerShape.getSensorOverlaps()
println("Currently ${overlappingShapes.size} shapes in trigger zone")
```
--------------------------------
### Read Body State After Step
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
After stepping the simulation, read the position and angle of dynamic bodies to update their visual representation in your renderer.
```kotlin
world.step(1f / 60f, 4)
val position: Vec2 = ball.position // world-space center
val angle: Float = ball.angle // radians
```
--------------------------------
### Import Kandy Plotting Library
Source: https://github.com/joaomcl/boks2d/blob/main/benchmark/results.ipynb
Imports the Kandy plotting library, which is used for generating visualizations.
```python
%use kandy
```
--------------------------------
### Create Distance Joints for Rods and Springs
Source: https://context7.com/joaomcl/boks2d/llms.txt
Implement rigid distance constraints (like rods) or spring constraints (like bungee cords) using distance joints. Configure length, spring properties, and adjust them dynamically.
```kotlin
import io.github.joaomcl.boks2d.joints.DistanceJointDef
// Create two bodies
val anchorBody = world.createBody(BodyDef(type = BodyType.Static, position = Vec2(0f, 10f)))
val swingBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(3f, 7f)))
swingBody.createCircleShape(ShapeDef(density = 1f), Circle(Vec2.Zero, 0.3f))
// Rigid distance constraint (like a rod)
val rigidJoint = world.createDistanceJoint(DistanceJointDef(
bodyA = anchorBody,
bodyB = swingBody,
localAnchorA = Vec2.Zero,
localAnchorB = Vec2.Zero,
length = 5f, // Fixed distance
enableSpring = false // Rigid constraint
))
// Spring constraint (like a bungee)
val springBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(5f, 5f)))
springBody.createCircleShape(ShapeDef(density = 1f), Circle(Vec2.Zero, 0.3f))
val springJoint = world.createDistanceJoint(DistanceJointDef(
bodyA = anchorBody,
bodyB = springBody,
localAnchorA = Vec2(5f, 0f),
localAnchorB = Vec2.Zero,
length = 3f,
enableSpring = true,
hertz = 2f, // Oscillation frequency
dampingRatio = 0.5f // 0 = no damping, 1 = critical damping
))
// Adjust spring at runtime
springJoint.distanceJointSetSpringHertz(4f)
springJoint.distanceJointSetSpringDampingRatio(0.3f)
```
--------------------------------
### Create and Configure Weld Joint
Source: https://context7.com/joaomcl/boks2d/llms.txt
Use WeldJointDef to rigidly connect two bodies. Soften the weld using linear and angular hertz and damping. Break the joint if constraint force exceeds a threshold.
```kotlin
import io.github.joaomcl.boks2d.joints.WeldJointDef
// Create compound body from multiple shapes
val mainBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 10f)))
mainBody.createPolygonShape(ShapeDef(density = 1f), Polygon.makeBox(1f, 0.5f))
val attachedBody = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(1.5f, 10f)))
attachedBody.createCircleShape(ShapeDef(density = 0.5f), Circle(Vec2.Zero, 0.5f))
// Weld bodies together
val weldJoint = world.createWeldJoint(WeldJointDef(
bodyA = mainBody,
bodyB = attachedBody,
localAnchorA = Vec2(1f, 0f),
localAnchorB = Vec2(-0.5f, 0f),
referenceAngle = 0f
))
// Make weld slightly flexible (soft weld)
weldJoint.weldJointSetLinearHertz(10f) // Linear softness
weldJoint.weldJointSetAngularHertz(10f) // Angular softness
weldJoint.weldJointSetLinearDampingRatio(0.5f)
weldJoint.weldJointSetAngularDampingRatio(0.5f)
// Break joint under stress (implement in game logic)
if (weldJoint.constraintForce.length() > 1000f) {
world.destroyJoint(weldJoint)
}
```
--------------------------------
### Read Body State After Simulation Step
Source: https://context7.com/joaomcl/boks2d/llms.txt
After each simulation step, read body positions and angles to update game graphics. This snippet shows how to access properties like position, angle, velocity, mass, and bounding box.
```kotlin
world.step(1f / 60f, 4)
// Read position and rotation for rendering
val position: Vec2 = body.position // World-space center
val angle: Float = body.angle // Radians
val rotation: Rot = body.rotation // Rotation object
// Velocity information
val velocity: Vec2 = body.linearVelocity
val angularVel: Float = body.angularVelocity
// Mass properties
val mass: Float = body.mass
val inertia: Float = body.inertia
val center: Vec2 = body.worldCenter
// Transform between local and world coordinates
val localPoint = body.getLocalPoint(Vec2(5f, 5f)) // World to local
val worldPoint = body.getWorldPoint(Vec2(0f, 1f)) // Local to world
val localVec = body.getLocalVector(Vec2(1f, 0f)) // World to local direction
val worldVec = body.getWorldVector(Vec2(0f, 1f)) // Local to world direction
// Velocity at a specific point on the body
val pointVelocity = body.getWorldPointVelocity(Vec2(1f, 5f))
// Bounding box
val bounds: AABB = body.aabb
```
--------------------------------
### Create Dynamic Ball Body
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Create a dynamic body for objects that will be moved by the physics engine, such as a ball. It is defined with initial position, density, friction, and restitution.
```kotlin
// dynamic body - moved by the physics engine
val ball = world.createBody(BodyDef(
type = BodyType.Dynamic,
position = Vec2(0f, 10f)
))
ball.createCircleShape(
ShapeDef(density = 1f, friction = 0.3f, restitution = 0.6f),
Circle(center = Vec2.Zero, radius = 0.5f)
)
```
--------------------------------
### Define Collision Categories
Source: https://github.com/joaomcl/boks2d/blob/main/Module.md
Define reusable bitmasks for different types of game objects. Use `shl` for readable bit definitions.
```kotlin
object Category {
val CHARACTER = 1uL shl 0 // ...0001
val OBSTACLE = 1uL shl 1 // ...0010
val PICKUP = 1uL shl 2 // ...0100
}
```
--------------------------------
### Perform Raycasting in Boks2D
Source: https://context7.com/joaomcl/boks2d/llms.txt
Shows how to cast rays to detect the closest shape or all shapes along a line. Raycasting is useful for line-of-sight checks and targeting.
```kotlin
// Cast ray and get closest hit
val origin = Vec2(0f, 10f)
val direction = Vec2(0f, -20f) // Direction and max length
val result = world.castRayClosest(
origin = origin,
translation = direction,
filter = QueryFilter.Default
)
if (result.hit) {
println("Hit shape at ${result.point}")
println("Surface normal: ${result.normal}")
println("Distance fraction: ${result.fraction}") // 0-1
println("Shape: ${result.shape}")
// Calculate actual hit distance
val hitDistance = direction.length() * result.fraction
}
// Cast ray with callback for all hits
world.castRay(
origin = origin,
translation = direction,
filter = QueryFilter.Default
) { shape, point, normal, fraction ->
println("Hit ${shape} at $point")
// Return value controls ray clipping:
// - Return fraction to clip ray at this hit
// - Return 1.0 to continue without clipping
// - Return 0.0 to terminate immediately
fraction // Continue to find closer hits
}
// Filter raycasts by category
val enemyOnlyFilter = QueryFilter(
maskBits = Category.ENEMY // Only detect enemies
)
val enemyHit = world.castRayClosest(origin, direction, enemyOnlyFilter)
```
--------------------------------
### Plot JVM Performance Data
Source: https://github.com/joaomcl/boks2d/blob/main/benchmark/results.ipynb
Generates a line plot for JVM performance data. The plot visualizes average microseconds per step against the number of bodies, with different libraries distinguished by color. A horizontal line indicates the 60 fps limit.
```kotlin
// JVM
plot {
layout.title = "JVM (Apple Silicon M4)"
line {
x(jvmRows.map { it.bodies }) { axis.name = "bodies" }
y(jvmRows.map { it.avgUsPerStep }) { axis.name = "avg µs/step" }
color(jvmRows.map { it.library }) {
legend.name = "Library"
scale = categorical(
"boks2d" to Color.hex("#4CAF50"),
"kbox2d" to Color.hex("#FB8C00"),
"dyn4j" to Color.hex("#E53935")
)
}
}
hLine {
yIntercept.constant(fps60)
color = Color.GREY
type = LineType.DASHED
}
text {
x(listOf(700))
y(listOf(fps60 * 1.08))
label(listOf("16.6ms (60 fps limit)"))
}
}
```
--------------------------------
### Create Static Ground Body
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Create a static body to represent ground or walls. This body will not be affected by physics forces. It uses a Polygon shape defined as a box.
```kotlin
// static body - never moves (ground, walls, obstacles)
val ground = world.createBody(BodyDef(type = BodyType.Static))
ground.createPolygonShape(
ShapeDef(friction = 0.5f),
Polygon.makeBox(halfWidth = 10f, halfHeight = 0.5f)
)
```
--------------------------------
### Simulate Explosions with Radial Impulses in Kotlin
Source: https://context7.com/joaomcl/boks2d/llms.txt
Apply radial impulses to dynamic bodies within a specified radius to simulate explosions. Control the effect's falloff and magnitude, and filter targets using category masks.
```kotlin
import io.github.joaomcl.boks2d.core.ExplosionDef
// Create explosion at point
world.explode(ExplosionDef(
position = Vec2(5f, 2f),
radius = 10f, // Max effect radius
falloff = 5f, // Full impulse within this radius, then linear falloff
impulsePerLength = 50f, // Impulse magnitude
maskBits = ULong.MAX_VALUE // Affect all categories
))
// Selective explosion (only affects enemies)
world.explode(ExplosionDef(
position = Vec2(0f, 0f),
radius = 8f,
falloff = 3f,
impulsePerLength = 100f,
maskBits = Category.ENEMY // Only affects enemy category
))
```
--------------------------------
### Box2D Build Configuration
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
Configures Box2D to be built as a static library and disables unit tests, testbed, and samples. This optimizes the build for the JNI wrapper.
```cmake
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(BOX2D_BUILD_UNIT_TESTS OFF CACHE BOOL "" FORCE)
set(BOX2D_BUILD_TESTBED OFF CACHE BOOL "" FORCE)
set(BOX2D_SAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${BOX2D_DIR} box2d-build)
```
--------------------------------
### Add Boks2D Dependency to Gradle
Source: https://context7.com/joaomcl/boks2d/llms.txt
Include the Boks2D library in your Gradle build file to enable cross-platform physics simulations.
```kotlin
// build.gradle.kts
dependencies {
implementation("io.github.joaomcl:boks2d:")
}
```
--------------------------------
### Read Platform Performance Data
Source: https://github.com/joaomcl/boks2d/blob/main/benchmark/results.ipynb
Reads performance data from CSV files for a given platform. It expects CSV files to contain at least two columns: number of bodies and average microseconds per step. The data is then sorted by library and number of bodies.
```kotlin
import java.io.File
data class Row(val library: String, val bodies: Int, val avgUsPerStep: Long)
fun readPlatform(runDir: File, platform: String): List =
File(runDir, platform).listFiles { f -> f.extension == "csv" }
?.flatMap {
val lib = it.nameWithoutExtension
it.readLines().drop(1).mapNotNull { line ->
val parts = line.split(",")
if (parts.size >= 2) Row(lib, parts[0].trim().toInt(), parts[1].trim().toLong()) else null
}
}?.sortedWith(compareBy({ it.library }, { it.bodies }))
?: emptyList()
val latestRun = File("results").listFiles { f -> f.isDirectory && !f.name.startsWith(".") }
?.maxByOrNull { it.name }
?: error("No run directories found in results/")
val jvmRows = readPlatform(latestRun, "jvm")
val iosRows = readPlatform(latestRun, "ios-simulator")
val fps60 = 16_666.0 // microseconds per frame at 60 fps
println("Run: ${latestRun.name}")
```
--------------------------------
### Add Boks2D Dependency
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Add the Boks2D dependency to your project's build.gradle.kts file. Replace `` with the latest release version.
```kotlin
dependencies {
implementation("io.github.joaomcl:boks2d:")
}
```
--------------------------------
### Query World with AABB and Shape
Source: https://context7.com/joaomcl/boks2d/llms.txt
Query the physics world for shapes overlapping a given axis-aligned bounding box or a more precise shape proxy. The callbacks allow processing found shapes and controlling the query continuation.
```kotlin
import io.github.joaomcl.boks2d.collision.AABB
import io.github.joaomcl.boks2d.collision.ShapeProxy
// Query axis-aligned bounding box
val searchArea = AABB(
lowerBound = Vec2(-5f, 0f),
upperBound = Vec2(5f, 10f)
)
val foundShapes = mutableListOf()
world.overlapAABB(searchArea, QueryFilter.Default) { shape ->
foundShapes.add(shape)
true // Continue searching (return false to stop)
}
println("Found ${foundShapes.size} shapes in area")
// Query with a shape proxy (more precise than AABB)
val circleProxy = ShapeProxy.circle(
center = Vec2(0f, 5f),
radius = 2f
)
world.overlapShape(circleProxy, QueryFilter.Default) { shape ->
println("Shape overlaps test circle: $shape")
true // Continue
}
// Shape cast - sweep a shape through the world
world.castShape(
proxy = circleProxy,
translation = Vec2(10f, 0f), // Sweep direction
filter = QueryFilter.Default
) { shape, point, normal, fraction ->
println("Sweep hit $shape at $point")
fraction
}
```
--------------------------------
### Box2D Directory Check
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
Verifies that the BOX2D_DIR variable is defined, which is necessary for locating the Box2D library. An error is raised if it's not set.
```cmake
if(NOT DEFINED BOX2D_DIR)
message(FATAL_ERROR "BOX2D_DIR must be defined")
endif()
```
--------------------------------
### Plot iOS Simulator Performance Data
Source: https://github.com/joaomcl/boks2d/blob/main/benchmark/results.ipynb
Generates a line plot for iOS simulator performance data. The plot visualizes average microseconds per step against the number of bodies, with different libraries distinguished by color. A horizontal line indicates the 60 fps limit.
```kotlin
// iOS simulator
plot {
layout.title = "iOS simulator (Apple Silicon M4)"
line {
x(iosRows.map { it.bodies }) { axis.name = "bodies" }
y(iosRows.map { it.avgUsPerStep }) { axis.name = "avg µs/step" }
color(iosRows.map { it.library }) {
legend.name = "Library"
scale = categorical(
"boks2d" to Color.hex("#4CAF50"),
"kbox2d" to Color.hex("#E53935")
)
}
}
hLine {
yIntercept.constant(fps60)
color = Color.GREY
type = LineType.DASHED
}
text {
x(listOf(2900))
y(listOf(fps60 * 1.9))
label(listOf("16.6ms (60 fps)"))
}
}
```
--------------------------------
### Target Include Directories for Boks2D
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
Specifies the include directories for the Boks2D library, including JNI headers and Box2D headers. This ensures the compiler can find necessary header files.
```cmake
target_include_directories(boks2d PRIVATE
${JNI_INCLUDE_DIRS}
${BOX2D_DIR}/include
)
```
--------------------------------
### Create and Move Kinematic Bodies
Source: https://context7.com/joaomcl/boks2d/llms.txt
Use kinematic bodies for non-simulated objects like platforms. They can be moved by directly setting their velocity or by using target transforms for smooth interpolation.
```kotlin
// Create a moving platform
val platform = world.createBody(BodyDef(
type = BodyType.Kinematic,
position = Vec2(0f, 5f)
))
platform.createPolygonShape(
ShapeDef(friction = 0.5f),
Polygon.makeBox(halfWidth = 2f, halfHeight = 0.3f)
)
// Move platform by setting velocity directly
platform.linearVelocity = Vec2(2f, 0f) // Move right at 2 m/s
// Or use target transform for smooth interpolation
platform.setTargetTransform(
Transform(position = Vec2(10f, 5f), rotation = Rot.Identity),
timeStep = 1f / 60f
)
```
--------------------------------
### Enable and Process Contact Events
Source: https://context7.com/joaomcl/boks2d/llms.txt
Enable contact events on shapes that need them for collision notifications. Process begin, end, and hit events after each simulation step.
```kotlin
// Enable contact events on shapes that need them
val ground = world.createBody(BodyDef(type = BodyType.Static))
ground.createPolygonShape(
ShapeDef(enableContactEvents = true),
Polygon.makeBox(10f, 0.5f)
)
val ball = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 10f)))
ball.createCircleShape(
ShapeDef(
density = 1f,
restitution = 0.7f,
enableContactEvents = true, // Required for begin/end events
enableHitEvents = true // Required for impact events
),
Circle(Vec2.Zero, 0.5f)
)
// Process events after each step
world.step(1f / 60f, 4)
val events = world.getContactEvents()
// New contacts this frame
events.beginEvents.forEach { event ->
println("Contact started: ${event.shapeA} <-> ${event.shapeB}")
}
// Ended contacts this frame
events.endEvents.forEach { event ->
// Shapes may be destroyed - check validity first
if (event.shapeA.isValid && event.shapeB.isValid) {
println("Contact ended: ${event.shapeA} <-> ${event.shapeB}")
}
}
// High-speed impacts (above hitEventThreshold)
events.hitEvents.forEach { event ->
println("Impact at ${event.point}")
println("Normal: ${event.normal}")
println("Speed: ${event.approachSpeed} m/s")
}
```
--------------------------------
### Destroy Bodies Safely After Step
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Safely destroy bodies that have moved below a certain threshold after the simulation step has completed. Avoid destroying bodies within callbacks that fire during the step.
```kotlin
world.step(1f / 60f, 4)
// safe! step() has already returned
bodies.removeAll { body ->
(body.position.y < -20f).also { if (it) world.destroyBody(body) }
}
```
--------------------------------
### Create Static Bodies in Boks2D
Source: https://context7.com/joaomcl/boks2d/llms.txt
Define static bodies for immovable objects like ground, walls, and platforms. These bodies have zero mass and are not affected by physics forces.
```kotlin
import io.github.joaomcl.boks2d.core.BodyDef
import io.github.joaomcl.boks2d.core.BodyType
import io.github.joaomcl.boks2d.shapes.ShapeDef
import io.github.joaomcl.boks2d.shapes.Polygon
// Create ground plane
val ground = world.createBody(BodyDef(
type = BodyType.Static,
position = Vec2(0f, -1f)
))
ground.createPolygonShape(
ShapeDef(friction = 0.5f),
Polygon.makeBox(halfWidth = 50f, halfHeight = 1f)
)
// Create walls
val leftWall = world.createBody(BodyDef(
type = BodyType.Static,
position = Vec2(-25f, 10f)
))
leftWall.createPolygonShape(
ShapeDef(friction = 0.3f),
Polygon.makeBox(halfWidth = 0.5f, halfHeight = 20f)
)
// Create rotated platform
val platform = world.createBody(BodyDef(
type = BodyType.Static,
position = Vec2(5f, 3f),
rotation = Rot.fromAngle(0.3f) // Tilted ~17 degrees
))
platform.createPolygonShape(
ShapeDef(friction = 0.4f),
Polygon.makeBox(halfWidth = 3f, halfHeight = 0.2f)
)
```
--------------------------------
### Configure Collision Filtering with Category and Mask Bits
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Control which shapes collide using categoryBits and maskBits. A collision occurs only if both shapes accept each other based on these bits. Use ULong for bits and shl for readable definitions.
```kotlin
object Category {
val CHARACTER = 1uL shl 0 // ...0001
val OBSTACLE = 1uL shl 1 // ...0010
val PICKUP = 1uL shl 2 // ...0100
}
// character: collides with obstacles and pickups
val characterShape = characterBody.createCircleShape(
ShapeDef(filter = Filter(
categoryBits = Category.CHARACTER,
maskBits = Category.OBSTACLE or Category.PICKUP
)),
Circle(Vec2.Zero, 0.5f)
)
// obstacle: collides with characters only (obstacles don't push pickups around)
val obstacleShape = obstacleBody.createPolygonShape(
ShapeDef(filter = Filter(
categoryBits = Category.OBSTACLE,
maskBits = Category.CHARACTER
)),
Polygon.makeBox(1f, 1f)
)
// pickup (coin, power-up): collides with characters only
val pickupShape = pickupBody.createCircleShape(
ShapeDef(filter = Filter(
categoryBits = Category.PICKUP,
maskBits = Category.CHARACTER
)),
Circle(Vec2.Zero, 0.3f)
)
```
--------------------------------
### Android-Specific Library Linking
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
For Android builds, links the Boks2D library with the log library. This is necessary for Android logging functionalities.
```cmake
if(ANDROID)
target_link_libraries(boks2d PRIVATE log)
endif()
```
--------------------------------
### Configure Obstacle Shape Filter
Source: https://github.com/joaomcl/boks2d/blob/main/Module.md
Configure an obstacle shape to collide only with characters. Obstacles will not interact with pickups.
```kotlin
// obstacle: collides with characters only (obstacles don't push pickups around)
val obstacleShape = obstacleBody.createPolygonShape(
ShapeDef(filter = Filter(
categoryBits = Category.OBSTACLE,
maskBits = Category.CHARACTER
)),
Polygon.makeBox(1f, 1f)
)
```
--------------------------------
### Enable Specific Body Awake
Source: https://github.com/joaomcl/boks2d/blob/main/README.md
Ensure a specific body remains awake and active in the simulation by setting its isAwake property to true. This overrides global sleep settings for that body.
```kotlin
// per body
ball.isAwake = true
```
--------------------------------
### Platform-Specific Target Properties
Source: https://github.com/joaomcl/boks2d/blob/main/src/jvmCommonMain/cpp/CMakeLists.txt
Sets platform-specific properties for the Boks2D library, such as prefixes and suffixes for shared libraries (.dylib, .so, .dll). This ensures correct library naming across different operating systems.
```cmake
if(APPLE)
set_target_properties(boks2d PROPERTIES
PREFIX "lib"
SUFFIX ".dylib"
)
elseif(UNIX)
set_target_properties(boks2d PROPERTIES
PREFIX "lib"
SUFFIX ".so"
)
elseif(WIN32)
set_target_properties(boks2d PROPERTIES
PREFIX ""
SUFFIX ".dll"
)
endif()
```
--------------------------------
### Apply Forces and Impulses to Bodies
Source: https://context7.com/joaomcl/boks2d/llms.txt
Apply continuous forces or instantaneous impulses to bodies to affect their motion. Forces can be applied at the center of mass or a specific point, and can cause rotation. Torques apply rotational force directly. Direct velocity control is also available.
```kotlin
val body = world.createBody(BodyDef(type = BodyType.Dynamic, position = Vec2(0f, 5f)))
body.createCircleShape(ShapeDef(density = 1f), Circle(Vec2.Zero, 0.5f))
// Apply force at center of mass (no rotation)
body.applyForceToCenter(Vec2(100f, 0f), wake = true)
// Apply force at a point (may cause rotation)
body.applyForce(
force = Vec2(50f, 50f),
point = Vec2(0.5f, 5f), // World coordinates
wake = true
)
// Apply torque (rotational force)
body.applyTorque(10f, wake = true)
// Instant velocity change at center
body.applyLinearImpulseToCenter(Vec2(0f, 20f), wake = true)
// Instant velocity change at a point
body.applyLinearImpulse(
impulse = Vec2(10f, 0f),
point = body.worldCenter,
wake = true
)
// Instant angular velocity change
body.applyAngularImpulse(5f, wake = true)
// Direct velocity control
body.linearVelocity = Vec2(5f, 0f)
body.angularVelocity = 2f // radians/second
```