chipmunk2d_physics_ffi

A comprehensive Dart FFI binding for Chipmunk2D physics engine version 7.0.3. This package provides a complete, idiomatic Dart API for 2D physics simulation in Flutter and Dart applications.

Demo

Demo

Features

  • Complete API Coverage: 1-1 API compliance with Chipmunk2D 7.0.3
  • Idiomatic Dart API: Clean, object-oriented wrapper around Chipmunk's C API with type-safe enums and helper methods
  • Full Feature Set:
    • Dynamic, kinematic, and static bodies
    • Circle, box, segment, and polygon shapes
    • 10 joint types (Pin, Slide, Pivot, Groove, Springs, Rotary, Ratchet, Gear, Motor)
    • Collision detection and filtering with helper methods
    • Spatial queries (point, segment, bounding box) with typed result classes
    • Sleeping and activation system
    • Force and impulse application
    • Moment of inertia and area calculations
  • Cross-Platform: Supports iOS, Android, macOS, Linux, Windows, and Web
  • Performance: Direct FFI bindings with minimal overhead

Installation

Add this package to your pubspec.yaml:

dependencies:
  chipmunk2d_physics_ffi: ^1.0.2

Then run:

flutter pub get

Quick Start

import 'package:chipmunk2d_physics_ffi/chipmunk2d_physics_ffi.dart';

void main() async {
  // On web, initialize Chipmunk2D before use (no-op on native platforms)
  WidgetsFlutterBinding.ensureInitialized();
  await initializeChipmunk();
  
  // Create a physics space
  final space = Space();
  space.gravity = Vector(0, -100); // Gravity pointing down

  // Create a dynamic body
  final body = Body.dynamic(1.0, 1.0); // mass, moment
  body.position = Vector(0, 100);

  // Create a circle shape
  final shape = CircleShape(body, 10.0); // radius
  shape.friction = 0.7;
  shape.elasticity = 0.5;

  // Add to space
  space.addBody(body);
  space.addShape(shape);

  // Create a static ground
  final ground = space.staticBody;
  final groundShape = BoxShape(ground, 1000, 20);
  space.addShape(groundShape);

  // Simulate
  for (var i = 0; i < 60; i++) {
    space.step(1.0 / 60.0); // 60 FPS
    print('Body position: ${body.position}');
  }

  // Cleanup
  shape.dispose();
  body.dispose();
  space.dispose();
}

Core Concepts

Space

The Space is the simulation container. It manages all bodies, shapes, and constraints, and steps the simulation forward in time.

final space = Space();
space.gravity = Vector(0, -100);
space.iterations = 10; // Solver iterations
space.damping = 0.9; // Global damping
space.step(1.0 / 60.0); // Step simulation

Body

A Body represents a rigid body in the simulation. Bodies can be:

  • Dynamic: Affected by forces, gravity, and collisions
  • Kinematic: User-controlled, not affected by forces
  • Static: Immovable, used for ground/walls
// Dynamic body
final body = Body.dynamic(1.0, 1.0); // mass, moment
body.position = Vector(0, 0);
body.velocity = Vector(10, 0);
body.angle = 0.5; // radians

// Apply forces
body.applyForceAtWorldPoint(Vector(0, -100), body.position);
body.applyImpulseAtWorldPoint(Vector(10, 0), body.position);

// Coordinate conversion
final worldPoint = body.localToWorld(Vector(5, 0));
final localPoint = body.worldToLocal(worldPoint);

Shape

Shapes define the collision geometry of bodies. Supported shape types:

  • CircleShape: Perfect for balls, wheels, etc.
  • BoxShape: Rectangles with optional rounded corners
  • SegmentShape: Line segments for walls, platforms
  • PolyShape: Convex polygons for complex shapes
// Circle
final circle = CircleShape(body, 10.0, offset: Vector(0, 0));
circle.friction = 0.7;
circle.elasticity = 0.5;
circle.sensor = false; // Set to true for trigger zones

// Box
final box = BoxShape(body, 50, 30, radius: 2.0); // width, height, corner radius

// Segment (line)
final segment = SegmentShape(body, Vector(0, 0), Vector(100, 0), 2.0);

// Polygon
final vertices = [
  Vector(0, 0),
  Vector(50, 0),
  Vector(50, 30),
  Vector(0, 30),
];
final poly = PolyShape(body, vertices, radius: 1.0);

Constraints (Joints)

Constraints connect two bodies together. Available joint types:

  • PinJoint: Fixed distance between two anchor points
  • SlideJoint: Distance constrained to min/max range
  • PivotJoint: Bodies pivot around a common point
  • GrooveJoint: Pivot constrained to a groove
  • DampedSpring: Spring with damping
  • DampedRotarySpring: Rotational spring
  • RotaryLimitJoint: Limits angular rotation
  • RatchetJoint: One-way rotational constraint
  • GearJoint: Synchronized rotation
  • SimpleMotor: Constant velocity rotation
// Pin joint
final joint = PinJoint(bodyA, bodyB, anchorA, anchorB);
joint.distance = 50.0;
space.addConstraint(joint);

// Spring
final spring = DampedSpring(
  bodyA, bodyB,
  anchorA, anchorB,
  100.0, // rest length
  1000.0, // stiffness
  10.0, // damping
);
space.addConstraint(spring);

API Overview

Body API

// Properties
body.position        // Vector
body.velocity        // Vector
body.angle           // double (radians)
body.angularVelocity // double (radians/sec)
body.mass            // double
body.moment          // double
body.centerOfGravity // Vector
body.force           // Vector
body.torque          // double
body.rotation        // Vector
body.type            // BodyType (BodyType.dynamic, BodyType.kinematic, BodyType.static)
body.isSleeping      // bool

// Methods
body.activate()
body.sleep()
body.localToWorld(Vector)
body.worldToLocal(Vector)
body.applyForceAtWorldPoint(Vector force, Vector point)
body.applyImpulseAtWorldPoint(Vector impulse, Vector point)
body.getVelocityAtWorldPoint(Vector point)

Shape API

// Properties
shape.friction          // double
shape.elasticity        // double
shape.filter            // ShapeFilter
shape.mass              // double
shape.density           // double
shape.moment            // double
shape.area              // double
shape.centerOfGravity   // Vector
shape.boundingBox       // BoundingBox
shape.sensor            // bool
shape.surfaceVelocity   // Vector
shape.collisionType     // int
shape.body              // Body?

// Collision Filtering
final filter = ShapeFilter.all(); // Collide with everything (default)
final filter = ShapeFilter.none(); // Collide with nothing
final filter = ShapeFilter.category(0x1); // Only collide with category 1
final filter = ShapeFilter.excludeCategory(0x2); // Collide with all except category 2
final filter = ShapeFilter.group(1); // Group that doesn't collide with itself

// CircleShape specific
circle.offset  // Vector
circle.radius // double

// SegmentShape specific
segment.endpointA  // Vector
segment.endpointB  // Vector
segment.normal     // Vector
segment.radius     // double
segment.setNeighbors(Vector prev, Vector next)

// PolyShape specific
poly.vertexCount      // int
poly.getVertex(int)   // Vector
poly.radius           // double

Space API

// Properties
space.gravity                // Vector
space.iterations             // int
space.damping                // double
space.idleSpeedThreshold     // double
space.sleepTimeThreshold     // double
space.collisionSlop          // double
space.collisionBias          // double
space.collisionPersistence   // int
space.currentTimeStep        // double
space.isLocked               // bool
space.staticBody             // Body

// Methods
space.addBody(Body)
space.removeBody(Body)
space.addShape(Shape)
space.removeShape(Shape)
space.addConstraint(Constraint)
space.removeConstraint(Constraint)
space.containsBody(Body)     // bool
space.containsShape(Shape)   // bool
space.containsConstraint(Constraint) // bool
space.step(double dt)
space.reindexStatic()
space.reindexShape(Shape)
space.reindexShapesForBody(Body)

Query Information

The library provides Dart classes for query results:

// Point query information
final info = PointQueryInfo(
  point: Vector(10, 20),
  distance: 5.0,
  gradient: Vector(1, 0),
  shape: shape, // optional
);

// Segment query information
final segInfo = SegmentQueryInfo(
  point: Vector(15, 25),
  normal: Vector(0, 1),
  alpha: 0.5,
  shape: shape, // optional
);

Utility Functions

Moment Calculations

import 'package:chipmunk2d_physics_ffi/chipmunk2d_physics_ffi.dart';

// Calculate moment of inertia
final moment = momentForCircle(1.0, 0.0, 10.0, Vector.zero);
final boxMoment = momentForBox(1.0, 50.0, 30.0);
final polyMoment = momentForPoly(1.0, vertices, Vector.zero, 0.0);

// Calculate areas
final area = areaForCircle(0.0, 10.0);
final segmentArea = areaForSegment(Vector(0, 0), Vector(100, 0), 2.0);

// Calculate centroid
final centroid = centroidForPoly(vertices);

// Convex hull
final hull = convexHull(points, tolerance: 0.0);

Bounding Box

final bb = BoundingBox(
  left: -10,
  bottom: -10,
  right: 10,
  top: 10,
);

// Or create from extents
final bb2 = BoundingBox.forExtents(Vector(0, 0), 10, 10);
final bb3 = BoundingBox.forCircle(Vector(0, 0), 10);

// Operations
bb.intersects(other);
bb.containsPoint(Vector(5, 5));
bb.merge(other);
bb.expand(Vector(15, 15));

Performance Tips

  1. Use fast getters: For hot loops, use positionX/positionY instead of position to avoid Vector allocation
  2. Batch operations: Add/remove multiple objects before stepping
  3. Sleeping: Enable sleeping for inactive bodies to improve performance
  4. Spatial queries: Use spatial queries efficiently - they're fast but avoid calling them every frame for many objects
  5. Reindexing: Only call reindexStatic() when you actually move static shapes

Platform Support

This package supports:

  • ✅ iOS
  • ✅ Android
  • ✅ macOS
  • ✅ Linux
  • ✅ Windows
  • ✅ Web

Web Support

On web, you must:

  1. Call initializeChipmunk() before using any Chipmunk2D functions:
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Required on web, no-op on native platforms
  await initializeChipmunk();
  
  runApp(MyApp());
}
  1. Include the _dynamicImport helper in your web/index.html file:
<script>
  // Required: Dart's js_interop cannot directly call the top-level import() function.
  // This wrapper exposes import() as a window property so Dart can access it.
  // Without this, the Chipmunk2D WASM module cannot be loaded on web.
  window._dynamicImport = (path) => import(path);
</script>

The WASM module is automatically included in your Flutter web build - no additional setup required!

Chipmunk2D Version

This package is built against Chipmunk2D 7.0.3. For API documentation and examples, refer to the official Chipmunk2D documentation.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This package is licensed under the MIT License, matching Chipmunk2D's license. See the LICENSE file for details.