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,
),
);
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 thehertzvalue the stiffer the spring, and thedampingRatiodefines 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,
),
);
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,
),
);
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 positiontarget: The initial world target point. This is assumed to coincide with the body anchor initially. While dragging you update it through thetargetsetter,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),
),
);
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 limitslowerTranslation: The lower translation limit in metersupperTranslation: 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 motormotorSpeed: The desired motor speed in meters per secondmaxMotorForce: 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),
),
);
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 limitslowerAngle: The lower angle in radiansupperAngle: 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 motormotorSpeed: The desired motor speed in radians per secondmaxMotorTorque: 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),
),
);
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 connectedlocalAnchorA,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.