diff --git a/doc/bridge_packages/flame_forge2d/joints.md b/doc/bridge_packages/flame_forge2d/joints.md index 8b23d58a4..c90228b4f 100644 --- a/doc/bridge_packages/flame_forge2d/joints.md +++ b/doc/bridge_packages/flame_forge2d/joints.md @@ -23,7 +23,7 @@ Currently, Forge2D supports the following joints: - GearJoint - [`MotorJoint`](#motorjoint) - [`MouseJoint`](#mousejoint) -- PrismaticJoint +- [`PrismaticJoint`] (#prismaticjoint) - [`PulleyJoint`](#pulleyjoint) - [`RevoluteJoint`](#revolutejoint) - RopeJoint @@ -274,6 +274,103 @@ final mouseJointDef = MouseJointDef() initially. +### `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 an axis and an anchor point. +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. +Using local anchors and a local axis helps when saving and loading a game. + +```{warning} +At least one body should by dynamic with a non-fixed rotation. +``` + +The `PrismaticJoint` definition is similar to the [`RevoluteJoint`](#revolutejoint) definition, but +instead of rotation, it uses translation. + +```{dart} +final prismaticJointDef = PrismaticJointDef() + ..initialize( + dynamicBody, + groundBody, + dynamicBody.worldCenter, + Vector2(1, 0), + ) +``` + +```{flutter-app} +:sources: ../../examples +:page: prismatic_joint +:subfolder: stories/bridge_libraries/forge2d/joints +:show: code popup +``` + +- `b1`, `b2`: Bodies connected by the joint. +- `anchor`: World anchor point, to put the axis through. Usually the center of the first body. +- `axis`: World translation axis, along which the translation will be fixed. + +In some cases you might wish to control the range of motion. For this, the `PrismaticJointDef` has +optional parameters that allow you to simulate a joint limit and/or a motor. + + +#### Prismatic Joint Limit + +You can limit the relative rotation with a joint limit that specifies a lower and upper translation. + +```dart +jointDef + ..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: + +```dart +prismaticJoint.setLimits(-10, 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. + +```dart +jointDef + ..enableMotor = true + ..motorSpeed = 1 + ..maxMotorForce = 100; +``` + +- `enableMotor`: Set to true to enable the motor +- `motorSpeed`: The desired motor speed in radians per second +- `maxMotorForce`: The maximum motor torque used to achieve the desired motor speed in N-m. + +You change the motor's speed and force after the joint was created using these methods: + +```dart +prismaticJoint.setMotorSpeed(2); +prismaticJoint.setMaxMotorForce(200); +``` + +Also, you can get the joint angle and speed using the following methods: + +```dart +prismaticJoint.getJointTranslation(); +prismaticJoint.getJointSpeed(); +``` + + ### `PulleyJoint` A `PulleyJoint` is used to create an idealized pulley. The pulley connects two bodies to the ground @@ -366,7 +463,7 @@ In some cases you might wish to control the joint angle. For this, the `Revolute optional parameters that allow you to simulate a joint limit and/or a motor. -#### Joint Limit +#### Revolute Joint Limit You can limit the relative rotation with a joint limit that specifies a lower and upper angle. @@ -388,7 +485,7 @@ revoluteJoint.setLimits(0, pi); ``` -#### Joint Motor +#### 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. diff --git a/examples/lib/main.dart b/examples/lib/main.dart index f8a67be2e..f25663aee 100644 --- a/examples/lib/main.dart +++ b/examples/lib/main.dart @@ -11,6 +11,7 @@ import 'package:examples/stories/bridge_libraries/forge2d/joints/distance_joint. import 'package:examples/stories/bridge_libraries/forge2d/joints/friction_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/motor_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/mouse_joint.dart'; +import 'package:examples/stories/bridge_libraries/forge2d/joints/prismatic_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/pulley_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/revolute_joint.dart'; import 'package:examples/stories/camera_and_viewport/camera_and_viewport.dart'; @@ -43,6 +44,7 @@ void main() { 'motor_joint': MotorJointExample.new, 'mouse_joint': MouseJointExample.new, 'pulley_joint': PulleyJointExample.new, + 'prismatic_joint': PrismaticJointExample.new, 'revolute_joint': RevoluteJointExample.new, }; final game = routes[page]?.call(); diff --git a/examples/lib/stories/bridge_libraries/forge2d/flame_forge2d.dart b/examples/lib/stories/bridge_libraries/forge2d/flame_forge2d.dart index 0b881c288..fb6402567 100644 --- a/examples/lib/stories/bridge_libraries/forge2d/flame_forge2d.dart +++ b/examples/lib/stories/bridge_libraries/forge2d/flame_forge2d.dart @@ -12,6 +12,7 @@ import 'package:examples/stories/bridge_libraries/forge2d/joints/distance_joint. import 'package:examples/stories/bridge_libraries/forge2d/joints/friction_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/motor_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/mouse_joint.dart'; +import 'package:examples/stories/bridge_libraries/forge2d/joints/prismatic_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/pulley_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/joints/revolute_joint.dart'; import 'package:examples/stories/bridge_libraries/forge2d/raycast_example.dart'; @@ -134,6 +135,12 @@ void addJointsStories(Dashbook dashbook) { codeLink: link('joints/mouse_joint.dart'), info: MouseJointExample.description, ) + .add( + 'PrismaticJoint', + (DashbookContext ctx) => GameWidget(game: PrismaticJointExample()), + codeLink: link('joints/prismatic_joint.dart'), + info: PrismaticJointExample.description, + ) .add( 'PulleyJoint', (DashbookContext ctx) => GameWidget(game: PulleyJointExample()), diff --git a/examples/lib/stories/bridge_libraries/forge2d/joints/prismatic_joint.dart b/examples/lib/stories/bridge_libraries/forge2d/joints/prismatic_joint.dart new file mode 100644 index 000000000..c8df28b5c --- /dev/null +++ b/examples/lib/stories/bridge_libraries/forge2d/joints/prismatic_joint.dart @@ -0,0 +1,63 @@ +import 'dart:ui'; + +import 'package:examples/stories/bridge_libraries/forge2d/utils/boxes.dart'; +import 'package:flame/events.dart'; +import 'package:flame/input.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; + +class PrismaticJointExample extends Forge2DGame + with TapDetector, HasDraggables { + static const description = ''' + This example shows how to use a `PrismaticJoint`. + + Drag the box along the specified axis, bound between lower and upper limits. + Also, there's a motor enabled that's pulling the box to the lower limit. + '''; + + late PrismaticJoint joint; + late Vector2 anchor = size / 2; + + @override + Future onLoad() async { + super.onLoad(); + + final box = DraggableBox(startPosition: anchor, width: 3, height: 3); + add(box); + await Future.wait([box.loaded]); + + createJoint(box.body, anchor); + } + + void createJoint(Body box, Vector2 anchor) { + final groundBody = world.createBody(BodyDef()); + + final prismaticJointDef = PrismaticJointDef() + ..initialize( + box, + groundBody, + anchor, + Vector2(1, 0), + ) + ..enableLimit = true + ..lowerTranslation = -20 + ..upperTranslation = 20 + ..enableMotor = true + ..motorSpeed = 1 + ..maxMotorForce = 100; + + joint = PrismaticJoint(prismaticJointDef); + world.createJoint(joint); + } + + @override + void render(Canvas canvas) { + super.render(canvas); + + final p1 = + worldToScreen(anchor + joint.getLocalAxisA() * joint.getLowerLimit()); + final p2 = + worldToScreen(anchor + joint.getLocalAxisA() * joint.getUpperLimit()); + + canvas.drawLine(p1.toOffset(), p2.toOffset(), debugPaint); + } +}