docs: RopeJoint documentation and example (#2520)

RopeJoint documentation and example
This commit is contained in:
Eugene Kleshnin
2023-04-25 09:37:31 +01:00
committed by GitHub
parent f9fd95439c
commit 0991c3bf67
5 changed files with 136 additions and 4 deletions

View File

@ -26,7 +26,7 @@ Currently, Forge2D supports the following joints:
- [`PrismaticJoint`](#prismaticjoint) - [`PrismaticJoint`](#prismaticjoint)
- [`PulleyJoint`](#pulleyjoint) - [`PulleyJoint`](#pulleyjoint)
- [`RevoluteJoint`](#revolutejoint) - [`RevoluteJoint`](#revolutejoint)
- RopeJoint - [`RopeJoint`](#ropejoint)
- WeldJoint - WeldJoint
- WheelJoint - WheelJoint
@ -564,3 +564,38 @@ Also, you can get the joint angle and speed using the following methods:
revoluteJoint.jointAngle(); revoluteJoint.jointAngle();
revoluteJoint.jointSpeed(); 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.
```

View File

@ -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/prismatic_joint.dart';
import 'package:examples/stories/bridge_libraries/forge2d/joints/pulley_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/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/camera_and_viewport/camera_and_viewport.dart';
import 'package:examples/stories/collision_detection/collision_detection.dart'; import 'package:examples/stories/collision_detection/collision_detection.dart';
import 'package:examples/stories/components/components.dart'; import 'package:examples/stories/components/components.dart';
@ -48,6 +49,7 @@ void main() {
'pulley_joint': PulleyJointExample.new, 'pulley_joint': PulleyJointExample.new,
'prismatic_joint': PrismaticJointExample.new, 'prismatic_joint': PrismaticJointExample.new,
'revolute_joint': RevoluteJointExample.new, 'revolute_joint': RevoluteJointExample.new,
'rope_joint': RopeJointExample.new,
}; };
final game = routes[page]?.call(); final game = routes[page]?.call();
if (game != null) { if (game != null) {

View File

@ -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/prismatic_joint.dart';
import 'package:examples/stories/bridge_libraries/forge2d/joints/pulley_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/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/raycast_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/revolute_joint_with_motor_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'; import 'package:examples/stories/bridge_libraries/forge2d/sprite_body_example.dart';
@ -159,5 +160,11 @@ void addJointsStories(Dashbook dashbook) {
(DashbookContext ctx) => GameWidget(game: RevoluteJointExample()), (DashbookContext ctx) => GameWidget(game: RevoluteJointExample()),
codeLink: link('revolute_joint.dart'), codeLink: link('revolute_joint.dart'),
info: RevoluteJointExample.description, info: RevoluteJointExample.description,
)
.add(
'RopeJoint',
(DashbookContext ctx) => GameWidget(game: RopeJointExample()),
codeLink: link('rope_joint.dart'),
info: RopeJointExample.description,
); );
} }

View File

@ -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));
}
}

View File

@ -14,8 +14,17 @@ class Ball extends BodyComponent with ContactCallbacks {
final Paint _blue = BasicPalette.blue.paint(); final Paint _blue = BasicPalette.blue.paint();
Ball(this._position, {this.radius = 2, this.bodyType = BodyType.dynamic}) { Ball(
this._position, {
this.radius = 2,
this.bodyType = BodyType.dynamic,
Color? color,
}) {
if (color != null) {
originalPaint = PaletteEntry(color).paint();
} else {
originalPaint = randomPaint(); originalPaint = randomPaint();
}
paint = originalPaint; paint = originalPaint;
} }