Skip to content

Commit

Permalink
docs: Added a page for Joints documentation + ConstantVolumeJoint doc…
Browse files Browse the repository at this point in the history
… and example (#2362)

Added a page for Forge2D joints documentation.
Added a `ConstantVolumeJoint` documentation and example.

Will add other joint types in the next PRs.
  • Loading branch information
eukleshnin authored Feb 26, 2023
1 parent a681c70 commit 957ad24
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 5 deletions.
1 change: 1 addition & 0 deletions doc/bridge_packages/flame_forge2d/flame_forge2d.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
:hidden:
Overview <forge2d.md>
Joints <joints.md>
```
69 changes: 69 additions & 0 deletions doc/bridge_packages/flame_forge2d/joints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 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 need to create a corresponding subclass of `JointDef`
and initialize it with its parameters.

To register a `Joint` use `world.createJoint`
and later use `world.destroyJoint` when you want to remove it.


## Built-in joints

Currently, Forge2D supports the following joints:

- [`ConstantVolumeJoint`](#constantvolumejoint)
- DistanceJoint
- FrictionJoint
- GearJoint
- MotorJoint
- MouseJoint
- PrismaticJoint
- PulleyJoint
- RevoluteJoint
- RopeJoint
- WeldJoint
- WheelJoint


### `ConstantVolumeJoint`

This type of joint connects a group of bodies together and maintains a constant volume within them.
Essentially, it is a set of `DistanceJoint`s, that connects all bodies one after another.

It can for example be useful when simulating "soft-bodies".

```{flutter-app}
:sources: ../../../examples/stories/bridge_libraries/forge2d/joints/
:page: constant_volume_joint
:show: widget code infobox
:width: 200
:height: 200
```

```dart
final constantVolumeJoint = ConstantVolumeJointDef()
..frequencyHz = 10
..dampingRatio = 0.8;
bodies.forEach((body) {
constantVolumeJoint.addBody(body);
});
world.createJoint(ConstantVolumeJoint(world, constantVolumeJoint));
```

`ConstantVolumeJointDef` requires at least 3 bodies to be added using the `addBody` method.

Optional param `frequencyHz` defines the frequency of oscillation of the joint.
If it's not 0, the higher the value is, the less springy each of the compound `DistantJoint`s are.

Another optional parameter is `dampingRatio`, it defines how fast the oscillation comes to rest.
It takes values from 0 to 1, where 0 = no damping, 1 = critical damping.

11 changes: 11 additions & 0 deletions examples/lib/stories/bridge_libraries/forge2d/flame_forge2d.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:examples/stories/bridge_libraries/forge2d/contact_callbacks_exam
import 'package:examples/stories/bridge_libraries/forge2d/domino_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/draggable_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/joint_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/joints/constant_volume_joint.dart';
import 'package:examples/stories/bridge_libraries/forge2d/mouse_joint_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/raycast_example.dart';
import 'package:examples/stories/bridge_libraries/forge2d/revolute_joint_example.dart';
Expand Down Expand Up @@ -104,4 +105,14 @@ void addForge2DStories(Dashbook dashbook) {
codeLink: link('widget_example.dart'),
info: WidgetExample.description,
);
addJointsStories(dashbook);
}

void addJointsStories(Dashbook dashbook) {
dashbook.storiesOf('flame_forge2d/joints').add(
'ConstantVolumeJoint',
(DashbookContext ctx) => GameWidget(game: ConstantVolumeJointExample()),
codeLink: link('constant_volume_joint.dart'),
info: BlobExample.description,
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:math';

import 'package:examples/stories/bridge_libraries/forge2d/utils/balls.dart';
import 'package:examples/stories/bridge_libraries/forge2d/utils/boundaries.dart';
import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';

class ConstantVolumeJointExample extends Forge2DGame with TapDetector {
@override
Future<void> onLoad() async {
super.onLoad();
addAll(createBoundaries(this));
}

@override
Future<void> onTapDown(TapDownInfo details) async {
super.onTapDown(details);
final center = details.eventPosition.game;

const numPieces = 20;
const radius = 5.0;
final balls = <Ball>[];

for (var i = 0; i < numPieces; i++) {
final x = radius * cos(2 * pi * (i / numPieces));
final y = radius * sin(2 * pi * (i / numPieces));

final ball = Ball(Vector2(x + center.x, y + center.y), radius: 0.5);

add(ball);
balls.add(ball);
}

await Future.wait(balls.map((e) => e.loaded));

createJoint(balls);
}

void createJoint(List<Ball> balls) {
final constantVolumeJoint = ConstantVolumeJointDef()
..frequencyHz = 10
..dampingRatio = 0.8;

balls.forEach((ball) {
constantVolumeJoint.addBody(ball.body);
});

world.createJoint(ConstantVolumeJoint(world, constantVolumeJoint));
}
}
88 changes: 83 additions & 5 deletions packages/flame_forge2d/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,85 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/widgets.dart';

void main() {
// There will be a new example in here after Google I/O, stay tuned!
// If you want to see the previous examples, go to the flame_forge2d section
// of https://examples.flame-engine.org
// The source code lives here:
// https://github.com/flame-engine/flame/tree/main/examples/lib/stories/bridge_libraries/forge2d
runApp(GameWidget(game: Forge2DExample()));
}

class Forge2DExample extends Forge2DGame with HasTappables {
@override
Future<void> onLoad() async {
add(Ball(size / 2));
addAll(createBoundaries());

return super.onLoad();
}

List<Component> createBoundaries() {
final topLeft = Vector2.zero();
final bottomRight = screenToWorld(camera.viewport.effectiveSize);
final topRight = Vector2(bottomRight.x, topLeft.y);
final bottomLeft = Vector2(topLeft.x, bottomRight.y);

return [
Wall(topLeft, topRight),
Wall(topRight, bottomRight),
Wall(bottomLeft, bottomRight),
Wall(topLeft, bottomLeft)
];
}
}

class Ball extends BodyComponent with Tappable {
final Vector2 _position;

Ball(this._position);

@override
Body createBody() {
final shape = CircleShape();
shape.radius = 5;

final fixtureDef = FixtureDef(
shape,
restitution: 0.8,
density: 1.0,
friction: 0.4,
);

final bodyDef = BodyDef(
userData: this,
angularDamping: 0.8,
position: _position,
type: BodyType.dynamic,
);

return world.createBody(bodyDef)..createFixture(fixtureDef);
}

@override
bool onTapDown(_) {
body.applyLinearImpulse(Vector2.random() * 5000);
return false;
}
}

class Wall extends BodyComponent {
final Vector2 _start;
final Vector2 _end;

Wall(this._start, this._end);

@override
Body createBody() {
final shape = EdgeShape()..set(_start, _end);
final fixtureDef = FixtureDef(shape, friction: 0.3);
final bodyDef = BodyDef(
userData: this,
position: Vector2.zero(),
);

return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
1 change: 1 addition & 0 deletions packages/flame_forge2d/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ environment:

dependencies:
dashbook: ^0.1.6
flame: ^1.6.0
flame_forge2d: ^0.11.0
flutter:
sdk: flutter
Expand Down

0 comments on commit 957ad24

Please sign in to comment.