Joints

Joints are used to connect two different bodies together in various ways. They help to simulate interactions between objects to create hinges, wheels, ropes, chains etc.

One Body in a joint may be of type BodyType.static. Joints between BodyType.static and/or BodyType.kinematic are allowed, but have no effect and use some processing time.

To construct a Joint, you create the corresponding subclass of JointDef with its parameters, and pass it to the typed creator method on the physics world, for example world.physicsWorld.createRevoluteJoint(revoluteJointDef). The creator returns the typed joint, and when you want to remove a joint you call joint.destroy().

Built-in joints

Currently, Forge2D supports the following joints:

The gear, pulley, rope, friction, and constant-volume joints from older Forge2D versions do not exist in Box2D v3, and therefore no longer exist in Forge2D.

DistanceJoint

A DistanceJoint constrains two points on two bodies to remain at a fixed distance from each other.

You can view this as a massless, rigid rod, and by enabling its spring it can also act as a spring/damper.

world.physicsWorld.createDistanceJoint(
  DistanceJointDef(
    bodyA: firstBody,
    bodyB: secondBody,
    length: 10,
    enableSpring: true,
    hertz: 3,
    dampingRatio: 0.2,
  ),
);
distance_joint.dart
 1import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/balls.dart';
 2import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart';
 3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
 4import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
 5import 'package:flame/components.dart';
 6import 'package:flame/events.dart';
 7import 'package:flame_forge2d/flame_forge2d.dart';
 8
 9class DistanceJointExample extends Forge2DExampleGame {
10  static const description = '''
11    This example shows how to use a `DistanceJoint`. Tap the screen to add a
12    pair of balls joined with a `DistanceJoint`.
13  ''';
14
15  DistanceJointExample() : super(world: DistanceJointWorld());
16}
17
18class DistanceJointWorld extends Forge2DWorld
19    with TapCallbacks, HasGameRef<Forge2DGame> {
20  @override
21  Future<void> onLoad() async {
22    await super.onLoad();
23    addAll(createBoundaries(gameRef));
24  }
25
26  @override
27  Future<void> onTapDown(TapDownEvent info) async {
28    super.onTapDown(info);
29    final tap = info.localPosition;
30
31    final first = Ball(tap);
32    final second = Ball(Vector2(tap.x + 3, tap.y + 3));
33    addAll([first, second]);
34
35    await Future.wait([first.loaded, second.loaded]);
36
37    final joint = physicsWorld.createDistanceJoint(
38      DistanceJointDef(
39        bodyA: first.body,
40        bodyB: second.body,
41        length: 10,
42        enableSpring: true,
43        hertz: 3,
44        dampingRatio: 0.2,
45      ),
46    );
47    add(JointRenderer(joint: joint));
48  }
49}

The most commonly used DistanceJointDef parameters:

  • localAnchorA, localAnchorB: The anchor points relative to each body’s origin.

  • length: This parameter determines the distance between the two anchor points and must be greater than 0. The default value is 1.

  • enableSpring, hertz, dampingRatio: When the spring is enabled the rod becomes soft; the higher the hertz value the stiffer the spring, and the dampingRatio defines how quickly the oscillation comes to rest, where 0 means no damping and 1 indicates critical damping.

  • enableLimit, minLength, maxLength: Restricts the distance between the bodies to a range when the spring is enabled.

  • enableMotor, motorSpeed, maxMotorForce: Drives the distance between the bodies.

Warning

Do not use a zero or short length.

FilterJoint

A FilterJoint doesn’t constrain the bodies at all; its only purpose is to disable all collision between the two connected bodies.

world.physicsWorld.createFilterJoint(
  FilterJointDef(bodyA: firstBody, bodyB: secondBody),
);

MotorJoint

A MotorJoint is used to control the relative motion between two bodies. A typical usage is to control the movement of a dynamic body with respect to the fixed point, for example to create animations.

A MotorJoint lets you control the motion of a body by specifying target position and rotation offsets. You can set the maximum motor force and torque that will be applied to reach the target position and rotation. If the body is blocked, it will stop and the contact forces will be proportional the maximum motor force and torque.

final motorJoint = world.physicsWorld.createMotorJoint(
  MotorJointDef(
    bodyA: first,
    bodyB: second,
    maxForce: 1000,
    maxTorque: 1000,
    correctionFactor: 0.1,
  ),
);
motor_joint.dart
 1import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/balls.dart';
 2import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boxes.dart';
 3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
 4import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
 5import 'package:flame/events.dart';
 6import 'package:flame_forge2d/flame_forge2d.dart';
 7
 8class MotorJointExample extends Forge2DExampleGame {
 9  static const description = '''
10    This example shows how to use a `MotorJoint`. The ball spins around the
11    center point. Tap the screen to change the direction.
12  ''';
13
14  MotorJointExample()
15    : super(gravity: Vector2.zero(), world: MotorJointWorld());
16}
17
18class MotorJointWorld extends Forge2DWorld with TapCallbacks {
19  late Ball ball;
20  late MotorJoint joint;
21  final motorSpeed = 1;
22
23  bool clockWise = true;
24
25  @override
26  Future<void> onLoad() async {
27    await super.onLoad();
28
29    final box = Box(
30      startPosition: Vector2.zero(),
31      width: 2,
32      height: 1,
33      bodyType: BodyType.static,
34      color: ExampleColors.slate,
35    );
36    add(box);
37
38    ball = Ball(Vector2(0, -5), color: ExampleColors.violet);
39    add(ball);
40
41    await Future.wait([ball.loaded, box.loaded]);
42
43    joint = createMotorJoint(ball.body, box.body);
44    add(JointRenderer(joint: joint));
45  }
46
47  @override
48  void onTapDown(TapDownEvent info) {
49    super.onTapDown(info);
50    clockWise = !clockWise;
51  }
52
53  MotorJoint createMotorJoint(Body first, Body second) {
54    return physicsWorld.createMotorJoint(
55      MotorJointDef(
56        bodyA: first,
57        bodyB: second,
58        // The target offset of the box in the ball's frame. Starting with
59        // the current offset keeps the bodies where they are, and update
60        // below drifts it from there.
61        linearOffset: first.localPoint(second.position),
62        maxForce: 1000,
63        maxTorque: 1000,
64        correctionFactor: 0.1,
65      ),
66    );
67  }
68
69  final linearOffset = Vector2.zero();
70
71  @override
72  void update(double dt) {
73    super.update(dt);
74
75    var deltaOffset = motorSpeed * dt;
76    if (clockWise) {
77      deltaOffset = -deltaOffset;
78    }
79
80    final linearOffsetX = joint.linearOffset.x + deltaOffset;
81    final linearOffsetY = joint.linearOffset.y + deltaOffset;
82    linearOffset.setValues(linearOffsetX, linearOffsetY);
83    final angularOffset = joint.angularOffset + deltaOffset;
84
85    joint.linearOffset = linearOffset;
86    joint.angularOffset = angularOffset;
87  }
88}

A MotorJointDef has these optional tuning parameters:

  • maxForce: the maximum translational force which will be applied to the joined body to reach the target position.

  • maxTorque: the maximum angular force which will be applied to the joined body to reach the target rotation.

  • correctionFactor: position correction factor in range [0, 1]. It adjusts the joint’s response to deviation from target position. A higher value makes the joint respond faster, while a lower value makes it respond slower. If the value is set too high, the joint may overcompensate and oscillate, becoming unstable. If set too low, it may respond too slowly.

The linear and angular offsets are the target distance and angle that the bodies should achieve relative to each other’s position and rotation. They can be passed to the MotorJointDef as linearOffset and angularOffset, or changed later through the setters with the same names on the MotorJoint.

For example, this code increments the angular offset of the joint every update cycle, causing the body to rotate.

@override
void update(double dt) {
  super.update(dt);

  joint.angularOffset = joint.angularOffset + motorSpeed * dt;
}

MouseJoint

The MouseJoint is used to manipulate bodies with the mouse. It attempts to drive a point on a body towards the current position of the cursor. There is no restriction on rotation.

The MouseJoint definition has a target point, maximum force, hertz, and damping ratio. The target point initially coincides with the body’s anchor point. The maximum force is used to prevent violent reactions when multiple dynamic bodies interact. You can make this as large as you like. The hertz and damping ratio are used to create a spring/damper effect similar to the distance joint.

Warning

Many users have tried to adapt the mouse joint for game play. Users often want to achieve precise positioning and instantaneous response. The mouse joint doesn’t work very well in that context. You may wish to consider using kinematic bodies instead.

final mouseJoint = world.physicsWorld.createMouseJoint(
  MouseJointDef(
    bodyA: groundBody,
    bodyB: ballBody,
    target: ballBody.position,
    maxForce: 3000 * ballBody.mass * 10,
    dampingRatio: 1,
    hertz: 5,
  ),
);
mouse_joint.dart
 1import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/balls.dart';
 2import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart';
 3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
 4import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
 5import 'package:flame/components.dart';
 6import 'package:flame/events.dart';
 7import 'package:flame_forge2d/flame_forge2d.dart';
 8
 9class MouseJointExample extends Forge2DExampleGame {
10  static const description = '''
11    In this example we use a `MouseJoint` to make the ball follow the mouse
12    when you drag it around. The line shows the joint pulling the ball
13    towards the pointer.
14  ''';
15
16  MouseJointExample()
17    : super(gravity: Vector2(0, 10.0), world: MouseJointWorld());
18}
19
20class MouseJointWorld extends Forge2DWorld
21    with DragCallbacks, HasGameRef<Forge2DGame> {
22  late Ball ball;
23  late Body groundBody;
24  MouseJoint? mouseJoint;
25  MouseJointRenderer? _jointRenderer;
26
27  @override
28  Future<void> onLoad() async {
29    await super.onLoad();
30    final boundaries = createBoundaries(gameRef);
31    addAll(boundaries);
32
33    groundBody = createBody(BodyDef());
34    ball = Ball(Vector2.zero(), radius: 5, color: ExampleColors.amber);
35    add(ball);
36  }
37
38  @override
39  void onDragStart(DragStartEvent info) {
40    super.onDragStart(info);
41    if (mouseJoint != null) {
42      return;
43    }
44    final joint = physicsWorld.createMouseJoint(
45      MouseJointDef(
46        bodyA: groundBody,
47        bodyB: ball.body,
48        target: ball.body.position,
49        maxForce: 3000 * ball.body.mass * 10,
50        dampingRatio: 0.1,
51        hertz: 5,
52      ),
53    );
54    mouseJoint = joint;
55    _jointRenderer = MouseJointRenderer(joint: joint);
56    add(_jointRenderer!);
57  }
58
59  @override
60  void onDragUpdate(DragUpdateEvent info) {
61    mouseJoint?.target = info.localEndPosition;
62  }
63
64  @override
65  void onDragEnd(DragEndEvent info) {
66    super.onDragEnd(info);
67    _destroyMouseJoint();
68  }
69
70  @override
71  void onDragCancel(DragCancelEvent event) {
72    super.onDragCancel(event);
73    _destroyMouseJoint();
74  }
75
76  void _destroyMouseJoint() {
77    mouseJoint?.destroy();
78    mouseJoint = null;
79    _jointRenderer?.removeFromParent();
80    _jointRenderer = null;
81  }
82}
  • maxForce: This parameter defines the maximum constraint force that can be exerted to move the candidate body. Usually you will express as some multiple of the weight (multiplier mass gravity).

  • dampingRatio: This parameter defines how quickly the oscillation comes to rest. It ranges from 0 to 1, where 0 means no damping and 1 indicates critical damping.

  • hertz: This parameter defines the response speed of the body, i.e. how quickly it tries to reach the target position

  • target: The initial world target point. This is assumed to coincide with the body anchor initially. While dragging you update it through the target setter, mouseJoint.target = newPosition;.

PrismaticJoint

The PrismaticJoint provides a single degree of freedom, allowing for a relative translation of two bodies along an axis fixed in bodyA. Relative rotation is prevented.

PrismaticJointDef requires defining a line of motion using a local axis and anchor points. The definition uses local anchor points and a local axis so that the initial configuration can violate the constraint slightly.

The joint translation is zero when the local anchor points coincide in world space.

Warning

At least one body should be dynamic with a non-fixed rotation.

The PrismaticJoint definition is similar to the RevoluteJoint definition, but instead of rotation, it uses translation.

final prismaticJoint = world.physicsWorld.createPrismaticJoint(
  PrismaticJointDef(
    bodyA: dynamicBody,
    bodyB: groundBody,
    localAxisA: Vector2(1, 0),
  ),
);
prismatic_joint.dart
 1import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boxes.dart';
 2import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
 3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
 4import 'package:flame_forge2d/flame_forge2d.dart';
 5
 6class PrismaticJointExample extends Forge2DExampleGame {
 7  static const description = '''
 8    This example shows how to use a `PrismaticJoint`.
 9
10    Drag the box along the specified axis, bound between lower and upper limits.
11    Also, there's a motor enabled that's pulling the box to the lower limit.
12    The line shows the axis between the two limits.
13  ''';
14
15  final Vector2 anchor = Vector2.zero();
16  final Vector2 axis = Vector2(1, 0);
17
18  @override
19  Future<void> onLoad() async {
20    await super.onLoad();
21
22    final box = DraggableBox(
23      startPosition: anchor,
24      width: 6,
25      height: 6,
26    );
27    world.add(box);
28    await Future.wait([box.loaded]);
29
30    final joint = createJoint(box.body, anchor);
31    world.add(
32      PrismaticJointRenderer(joint: joint, anchor: anchor, axis: axis),
33    );
34  }
35
36  PrismaticJoint createJoint(Body box, Vector2 anchor) {
37    final groundBody = world.createBody(BodyDef());
38
39    return world.physicsWorld.createPrismaticJoint(
40      PrismaticJointDef(
41        bodyA: box,
42        bodyB: groundBody,
43        localAxisA: axis,
44        enableLimit: true,
45        lowerTranslation: -20,
46        upperTranslation: 20,
47        enableMotor: true,
48        motorSpeed: 1,
49        maxMotorForce: 100,
50      ),
51    );
52  }
53}
  • bodyA, bodyB: Bodies connected by the joint.

  • localAnchorA, localAnchorB: The anchor points relative to each body’s origin.

  • localAxisA: The translation axis in bodyA’s frame, along which the translation will be fixed.

Prismatic Joint Limit

You can limit the relative translation with a joint limit that specifies a lower and upper translation.

PrismaticJointDef(
  ...
  enableLimit: true,
  lowerTranslation: -20,
  upperTranslation: 20,
);
  • enableLimit: Set to true to enable translation limits

  • lowerTranslation: The lower translation limit in meters

  • upperTranslation: The upper translation limit in meters

You change the limits after the joint was created with this method:

prismaticJoint.setLimits(lower: -10, upper: 10);

Prismatic Joint Motor

You can use a motor to drive the motion or to model joint friction. A maximum motor force is provided so that infinite forces are not generated.

PrismaticJointDef(
  ...
  enableMotor: true,
  motorSpeed: 1,
  maxMotorForce: 100,
);
  • enableMotor: Set to true to enable the motor

  • motorSpeed: The desired motor speed in meters per second

  • maxMotorForce: The maximum motor force used to achieve the desired motor speed in N.

You change the motor’s speed and force after the joint was created using these setters:

prismaticJoint.motorSpeed = 2;
prismaticJoint.maxMotorForce = 200;

Also, you can get the joint translation and speed using the following getters:

prismaticJoint.translation;
prismaticJoint.speed;

RevoluteJoint

A RevoluteJoint forces two bodies to share a common anchor point, often called a hinge point. The revolute joint has a single degree of freedom: the relative rotation of the two bodies.

To create a RevoluteJoint, provide two bodies and the local anchor points that coincide at the hinge point. The definition uses local anchor points so that the initial configuration can violate the constraint slightly.

final revoluteJoint = world.physicsWorld.createRevoluteJoint(
  RevoluteJointDef(
    bodyA: firstBody,
    bodyB: secondBody,
    localAnchorA: firstBody.localPoint(anchor),
    localAnchorB: secondBody.localPoint(anchor),
  ),
);
revolute_joint.dart
 1import 'dart:math';
 2
 3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/balls.dart';
 4import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart';
 5import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
 6import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
 7import 'package:flame/components.dart';
 8import 'package:flame/events.dart';
 9import 'package:flame_forge2d/flame_forge2d.dart';
10
11class RevoluteJointExample extends Forge2DExampleGame {
12  static const description = '''
13    In this example we use a joint to keep a body with several fixtures stuck
14    to another body.
15
16    Tap the screen to add more of these combined bodies.
17  ''';
18
19  RevoluteJointExample()
20    : super(gravity: Vector2(0, 10.0), world: RevoluteJointWorld());
21}
22
23class RevoluteJointWorld extends Forge2DWorld
24    with TapCallbacks, HasGameRef<Forge2DGame> {
25  @override
26  Future<void> onLoad() async {
27    await super.onLoad();
28    addAll(createBoundaries(gameRef));
29  }
30
31  @override
32  void onTapDown(TapDownEvent info) {
33    super.onTapDown(info);
34    final ball = Ball(info.localPosition);
35    add(ball);
36    add(CircleShuffler(ball));
37  }
38}
39
40class CircleShuffler extends BodyComponent {
41  final Ball ball;
42
43  CircleShuffler(this.ball);
44
45  @override
46  Body createBody() {
47    final bodyDef = BodyDef(
48      type: BodyType.dynamic,
49      position: ball.body.position.clone(),
50    );
51    const numPieces = 5;
52    const radius = 6.0;
53    final body = world.createBody(bodyDef);
54
55    for (var i = 0; i < numPieces; i++) {
56      final xPos = radius * cos(2 * pi * (i / numPieces));
57      final yPos = radius * sin(2 * pi * (i / numPieces));
58
59      body.createShape(
60        Circle(radius: 1.2, center: Vector2(xPos, yPos)),
61        ShapeDef(
62          density: 50.0,
63          material: SurfaceMaterial(friction: 0.5, restitution: 0.4),
64        ),
65      );
66    }
67
68    final joint = world.physicsWorld.createRevoluteJoint(
69      RevoluteJointDef(bodyA: body, bodyB: ball.body),
70    );
71    world.add(JointRenderer(joint: joint));
72
73    return body;
74  }
75}

In some cases you might wish to control the joint angle. For this, the RevoluteJointDef has optional parameters that allow you to simulate a joint limit and/or a motor.

Revolute Joint Limit

You can limit the relative rotation with a joint limit that specifies a lower and upper angle.

RevoluteJointDef(
  ...
  enableLimit: true,
  lowerAngle: 0,
  upperAngle: pi / 2,
);
  • enableLimit: Set to true to enable angle limits

  • lowerAngle: The lower angle in radians

  • upperAngle: The upper angle in radians

You change the limits after the joint was created with this method:

revoluteJoint.setLimits(lower: 0, upper: pi);

Revolute Joint Motor

You can use a motor to drive the relative rotation about the shared point. A maximum motor torque is provided so that infinite forces are not generated.

RevoluteJointDef(
  ...
  enableMotor: true,
  motorSpeed: 5,
  maxMotorTorque: 100,
);
  • enableMotor: Set to true to enable the motor

  • motorSpeed: The desired motor speed in radians per second

  • maxMotorTorque: The maximum motor torque used to achieve the desired motor speed in N-m.

You change the motor’s speed and torque after the joint was created using these setters:

revoluteJoint.motorSpeed = 2;
revoluteJoint.maxMotorTorque = 200;

Also, you can get the current joint angle:

revoluteJoint.angle;

WeldJoint

A WeldJoint is used to restrict all relative motion between two bodies, effectively joining them together.

WeldJointDef requires two bodies that will be connected, and the local anchor points that coincide at the weld point:

world.physicsWorld.createWeldJoint(
  WeldJointDef(
    bodyA: firstBody,
    bodyB: secondBody,
    localAnchorA: firstBody.localPoint(anchor),
    localAnchorB: secondBody.localPoint(anchor),
  ),
);
weld_joint.dart
  1import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/balls.dart';
  2import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/boxes.dart';
  3import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/joint_renderer.dart';
  4import 'package:examples/stories/bridge_libraries/flame_forge2d/utils/style.dart';
  5import 'package:flame/components.dart';
  6import 'package:flame/events.dart';
  7import 'package:flame_forge2d/flame_forge2d.dart';
  8import 'package:flutter/material.dart';
  9
 10class WeldJointExample extends Forge2DExampleGame {
 11  static const description = '''
 12    This example shows how to use a `WeldJoint`. Tap the screen to add a
 13    ball to test the bridge built using a `WeldJoint`
 14  ''';
 15
 16  WeldJointExample() : super(world: WeldJointWorld());
 17}
 18
 19class WeldJointWorld extends Forge2DWorld
 20    with TapCallbacks, HasGameRef<Forge2DGame> {
 21  final pillarHeight = 20.0;
 22  final pillarWidth = 5.0;
 23
 24  @override
 25  Future<void> onLoad() async {
 26    await super.onLoad();
 27
 28    final leftPillar = Box(
 29      startPosition: gameRef.screenToWorld(Vector2(50, gameRef.size.y))
 30        ..y -= pillarHeight / 2,
 31      width: pillarWidth,
 32      height: pillarHeight,
 33      bodyType: BodyType.static,
 34      color: Colors.white,
 35    );
 36    final rightPillar = Box(
 37      startPosition: gameRef.screenToWorld(
 38        Vector2(gameRef.size.x - 50, gameRef.size.y),
 39      )..y -= pillarHeight / 2,
 40      width: pillarWidth,
 41      height: pillarHeight,
 42      bodyType: BodyType.static,
 43      color: Colors.white,
 44    );
 45
 46    final pillars = [leftPillar, rightPillar];
 47    addAll(pillars);
 48    await pillars.loaded;
 49
 50    createBridge(leftPillar, rightPillar);
 51  }
 52
 53  Future<void> createBridge(
 54    Box leftPillar,
 55    Box rightPillar,
 56  ) async {
 57    const sectionsCount = 10;
 58    // Vector2.zero is used here since 0,0 is in the middle and 0,0 in the
 59    // screen space then gives us the coordinates of the upper left corner in
 60    // world space.
 61    final halfSize = gameRef.screenToWorld(Vector2.zero())..absolute();
 62    final sectionWidth =
 63        ((leftPillar.center.x.abs() +
 64                    rightPillar.center.x.abs() +
 65                    pillarWidth) /
 66                sectionsCount)
 67            .ceilToDouble();
 68    Body? prevSection;
 69
 70    for (var i = 0; i < sectionsCount; i++) {
 71      final section = Box(
 72        startPosition: Vector2(
 73          sectionWidth * i - halfSize.x + sectionWidth / 2,
 74          halfSize.y - pillarHeight,
 75        ),
 76        width: sectionWidth,
 77        height: 1,
 78      );
 79      add(section);
 80      await section.loaded;
 81
 82      if (prevSection != null) {
 83        createWeldJoint(
 84          prevSection,
 85          section.body,
 86          Vector2(
 87            sectionWidth * i - halfSize.x + sectionWidth,
 88            halfSize.y - pillarHeight,
 89          ),
 90        );
 91      }
 92
 93      prevSection = section.body;
 94    }
 95  }
 96
 97  void createWeldJoint(Body first, Body second, Vector2 anchor) {
 98    final joint = physicsWorld.createWeldJoint(
 99      WeldJointDef(
100        bodyA: first,
101        bodyB: second,
102        localAnchorA: first.localPoint(anchor),
103        localAnchorB: second.localPoint(anchor),
104      ),
105    );
106    add(JointRenderer(joint: joint));
107  }
108
109  @override
110  Future<void> onTapDown(TapDownEvent info) async {
111    super.onTapDown(info);
112    final ball = Ball(info.localPosition, radius: 5);
113    add(ball);
114  }
115}
  • bodyA, bodyB: Two bodies that will be connected

  • localAnchorA, localAnchorB: Anchor points relative to each body’s origin, at which the two bodies will be welded together

The weld can also be made springy with the linearHertz, angularHertz, linearDampingRatio and angularDampingRatio parameters.

Breakable Bodies and WeldJoint

Since the Forge2D constraint solver is iterative, joints are somewhat flexible. This means that the bodies connected by a WeldJoint may bend slightly. If you want to simulate a breakable body, it’s better to create a single body with multiple shapes. When the body breaks, you can destroy a shape and recreate it on a new body instead of relying on a WeldJoint.

WheelJoint

A WheelJoint provides two degrees of freedom: translation along a spring-loaded axis fixed in bodyA, and rotation of bodyB. It is designed for vehicle suspensions.

world.physicsWorld.createWheelJoint(
  WheelJointDef(
    bodyA: chassis,
    bodyB: wheel,
    localAnchorA: chassis.localPoint(wheel.position),
    localAxisA: Vector2(0, 1),
    hertz: 4,
    dampingRatio: 0.7,
    enableMotor: true,
    maxMotorTorque: 30,
    motorSpeed: -25,
  ),
);
  • localAxisA: The suspension axis in bodyA’s frame.

  • enableSpring, hertz, dampingRatio: The suspension spring configuration.

  • enableLimit, lowerTranslation, upperTranslation: Limits the suspension travel.

  • enableMotor, motorSpeed, maxMotorTorque: Drives the wheel’s rotation.