Skip to content

Commit

Permalink
docs: RopeJoint documentation and example (flame-engine#2520)
Browse files Browse the repository at this point in the history
RopeJoint documentation and example
  • Loading branch information
eukleshnin authored Apr 25, 2023
1 parent f9fd954 commit 0991c3b
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 4 deletions.
39 changes: 37 additions & 2 deletions doc/bridge_packages/flame_forge2d/joints.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Currently, Forge2D supports the following joints:
- [`GearJoint`](#gearjoint)
- [`MotorJoint`](#motorjoint)
- [`MouseJoint`](#mousejoint)
- [`PrismaticJoint`] (#prismaticjoint)
- [`PrismaticJoint`](#prismaticjoint)
- [`PulleyJoint`](#pulleyjoint)
- [`RevoluteJoint`](#revolutejoint)
- RopeJoint
- [`RopeJoint`](#ropejoint)
- WeldJoint
- WheelJoint

Expand Down Expand Up @@ -564,3 +564,38 @@ Also, you can get the joint angle and speed using the following methods:
revoluteJoint.jointAngle();
revoluteJoint.jointSpeed();
```


### `RopeJoint`

A `RopeJoint` restricts the maximum distance between two points on two bodies.

`RopeJointDef` requires two body anchor points and the maximum length.

```dart
final ropeJointDef = RopeJointDef()
..bodyA = firstBody
..localAnchorA.setFrom(firstBody.getLocalCenter())
..bodyB = secondBody
..localAnchorB.setFrom(secondBody.getLocalCenter())
..maxLength = (secondBody.worldCenter - firstBody.worldCenter).length;
world.createJoint(RopeJoint(ropeJointDef));
```

```{flutter-app}
:sources: ../../examples
:page: rope_joint
:subfolder: stories/bridge_libraries/forge2d/joints
:show: code popup
```

- `bodyA`, `bodyB`: Connected bodies
- `localAnchorA`, `localAnchorB`: Optional parameter, anchor point relative to the body's origin.
- `maxLength`: The maximum length of the rope. This must be larger than `linearSlop`, or the joint
will have no effect.

```{warning}
The joint assumes that the maximum length doesn't change during simulation.
See `DistanceJoint` if you want to dynamically control length.
```
2 changes: 2 additions & 0 deletions examples/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:examples/stories/bridge_libraries/forge2d/joints/mouse_joint.dar
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/joints/rope_joint.dart';
import 'package:examples/stories/camera_and_viewport/camera_and_viewport.dart';
import 'package:examples/stories/collision_detection/collision_detection.dart';
import 'package:examples/stories/components/components.dart';
Expand Down Expand Up @@ -48,6 +49,7 @@ void main() {
'pulley_joint': PulleyJointExample.new,
'prismatic_joint': PrismaticJointExample.new,
'revolute_joint': RevoluteJointExample.new,
'rope_joint': RopeJointExample.new,
};
final game = routes[page]?.call();
if (game != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:examples/stories/bridge_libraries/forge2d/joints/mouse_joint.dar
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/joints/rope_joint.dart';
import 'package:examples/stories/bridge_libraries/forge2d/raycast_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/revolute_joint_with_motor_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/sprite_body_example.dart';
Expand Down Expand Up @@ -159,5 +160,11 @@ void addJointsStories(Dashbook dashbook) {
(DashbookContext ctx) => GameWidget(game: RevoluteJointExample()),
codeLink: link('revolute_joint.dart'),
info: RevoluteJointExample.description,
)
.add(
'RopeJoint',
(DashbookContext ctx) => GameWidget(game: RopeJointExample()),
codeLink: link('rope_joint.dart'),
info: RopeJointExample.description,
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import 'package:examples/stories/bridge_libraries/forge2d/utils/balls.dart';
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';
import 'package:flutter/material.dart';

class RopeJointExample extends Forge2DGame with TapDetector, HasDraggables {
static const description = '''
This example shows how to use a `RopeJoint`.
Drag the box handle along the axis and observe the rope respond to the
movement
''';

double handleWidth = 6;

@override
Future<void> onLoad() async {
super.onLoad();

final handleBody = await createHandle();
createRope(handleBody);
}

Future<Body> createHandle() async {
final anchor = Vector2(size.x / 2, 5);

final box =
DraggableBox(startPosition: anchor, width: handleWidth, height: 3);
await add(box);

createPrismaticJoint(box.body, anchor);
return box.body;
}

Future<void> createRope(Body handle) async {
const length = 50;
var prevBody = handle;

for (var i = 0; i < length; i++) {
final newPosition = prevBody.worldCenter + Vector2(0, 1);
final ball = Ball(newPosition, radius: 0.5, color: Colors.white);
await add(ball);

createRopeJoint(ball.body, prevBody);
prevBody = ball.body;
}
}

void createPrismaticJoint(Body box, Vector2 anchor) {
final groundBody = world.createBody(BodyDef());

final prismaticJointDef = PrismaticJointDef()
..initialize(
box,
groundBody,
anchor,
Vector2(1, 0),
)
..enableLimit = true
..lowerTranslation = -size.x / 2 + handleWidth / 2
..upperTranslation = size.x / 2 - handleWidth / 2;

final joint = PrismaticJoint(prismaticJointDef);
world.createJoint(joint);
}

void createRopeJoint(Body first, Body second) {
final ropeJointDef = RopeJointDef()
..bodyA = first
..localAnchorA.setFrom(first.getLocalCenter())
..bodyB = second
..localAnchorB.setFrom(second.getLocalCenter())
..maxLength = (second.worldCenter - first.worldCenter).length;

world.createJoint(RopeJoint(ropeJointDef));
}
}
13 changes: 11 additions & 2 deletions examples/lib/stories/bridge_libraries/forge2d/utils/balls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ class Ball extends BodyComponent with ContactCallbacks {

final Paint _blue = BasicPalette.blue.paint();

Ball(this._position, {this.radius = 2, this.bodyType = BodyType.dynamic}) {
originalPaint = randomPaint();
Ball(
this._position, {
this.radius = 2,
this.bodyType = BodyType.dynamic,
Color? color,
}) {
if (color != null) {
originalPaint = PaletteEntry(color).paint();
} else {
originalPaint = randomPaint();
}
paint = originalPaint;
}

Expand Down

0 comments on commit 0991c3b

Please sign in to comment.