### 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 ```