docs: Move flame_forge2d examples to main examples (#1588)

This commit is contained in:
Lukas Klingsbo
2022-05-01 15:00:48 +02:00
committed by GitHub
parent 7877579868
commit 6dd0a970e6
26 changed files with 499 additions and 407 deletions

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -2,6 +2,7 @@ import 'package:dashbook/dashbook.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'stories/animations/animations.dart'; import 'stories/animations/animations.dart';
import 'stories/bridge_libraries/forge2d/flame_forge2d.dart';
import 'stories/camera_and_viewport/camera_and_viewport.dart'; import 'stories/camera_and_viewport/camera_and_viewport.dart';
import 'stories/collision_detection/collision_detection.dart'; import 'stories/collision_detection/collision_detection.dart';
import 'stories/components/components.dart'; import 'stories/components/components.dart';
@ -37,5 +38,8 @@ void main() async {
addUtilsStories(dashbook); addUtilsStories(dashbook);
addWidgetsStories(dashbook); addWidgetsStories(dashbook);
// Bridge package examples
addForge2DStories(dashbook);
runApp(dashbook); runApp(dashbook);
} }

View File

@ -4,7 +4,51 @@ import 'package:flame/components.dart';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class AnimatedBodyExample extends Forge2DGame with TapDetector {
static const String description = '''
In this example we show how to add an animated chopper, which is created
with a SpriteAnimationComponent, on top of a BodyComponent.
Tap the screen to add more choppers.
''';
AnimatedBodyExample() : super(gravity: Vector2.zero());
late Image chopper;
late SpriteAnimation animation;
@override
Future<void> onLoad() async {
chopper = await images.load('animations/chopper.png');
animation = SpriteAnimation.fromFrameData(
chopper,
SpriteAnimationData.sequenced(
amount: 4,
textureSize: Vector2.all(48),
stepTime: 0.15,
),
);
final boundaries = createBoundaries(this);
boundaries.forEach(add);
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final position = details.eventPosition.game;
final spriteSize = Vector2.all(10);
final animationComponent = SpriteAnimationComponent(
animation: animation,
size: spriteSize,
anchor: Anchor.center,
);
add(ChopperBody(position, animationComponent));
}
}
class ChopperBody extends BodyComponent { class ChopperBody extends BodyComponent {
final Vector2 position; final Vector2 position;
@ -39,40 +83,3 @@ class ChopperBody extends BodyComponent {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }
class PositionBodySample extends Forge2DGame with TapDetector {
late Image chopper;
late SpriteAnimation animation;
PositionBodySample() : super(gravity: Vector2.zero());
@override
Future<void> onLoad() async {
chopper = await images.load('chopper.png');
animation = SpriteAnimation.fromFrameData(
chopper,
SpriteAnimationData.sequenced(
amount: 4,
textureSize: Vector2.all(48),
stepTime: 0.15,
),
);
final boundaries = createBoundaries(this);
boundaries.forEach(add);
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final position = details.eventPosition.game;
final spriteSize = Vector2.all(10);
final animationComponent = SpriteAnimationComponent(
animation: animation,
size: spriteSize,
anchor: Anchor.center,
);
add(ChopperBody(position, animationComponent));
}
}

View File

@ -3,7 +3,40 @@ import 'dart:math' as math;
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class BlobExample extends Forge2DGame with TapDetector {
static const String description = '''
In this example we show the power of joints by showing interactions between
bodies tied together.
Tap the screen to add boxes that will bounce on the "blob" in the center.
''';
@override
Future<void> onLoad() async {
final worldCenter = screenToWorld(size * camera.zoom / 2);
final blobCenter = worldCenter + Vector2(0, -30);
final blobRadius = Vector2.all(6.0);
addAll(createBoundaries(this));
add(Ground(worldCenter));
final jointDef = ConstantVolumeJointDef()
..frequencyHz = 20.0
..dampingRatio = 1.0
..collideConnected = false;
await addAll([
for (var i = 0; i < 20; i++) BlobPart(i, jointDef, blobRadius, blobCenter)
]);
world.createJoint(ConstantVolumeJoint(world, jointDef));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
add(FallingBox(details.eventPosition.game));
}
}
class Ground extends BodyComponent { class Ground extends BodyComponent {
final Vector2 worldCenter; final Vector2 worldCenter;
@ -14,15 +47,16 @@ class Ground extends BodyComponent {
Body createBody() { Body createBody() {
final shape = PolygonShape(); final shape = PolygonShape();
shape.setAsBoxXY(20.0, 0.4); shape.setAsBoxXY(20.0, 0.4);
final fixtureDef = FixtureDef(shape, friction: 0.2);
final bodyDef = BodyDef(position: worldCenter.clone()); final bodyDef = BodyDef(position: worldCenter.clone());
final ground = world.createBody(bodyDef); final ground = world.createBody(bodyDef);
ground.createFixtureFromShape(shape); ground.createFixture(fixtureDef);
shape.setAsBox(0.4, 20.0, Vector2(-10.0, 0.0), 0.0); shape.setAsBox(0.4, 20.0, Vector2(-10.0, 0.0), 0.0);
ground.createFixtureFromShape(shape); ground.createFixture(fixtureDef);
shape.setAsBox(0.4, 20.0, Vector2(10.0, 0.0), 0.0); shape.setAsBox(0.4, 20.0, Vector2(10.0, 0.0), 0.0);
ground.createFixtureFromShape(shape); ground.createFixture(fixtureDef);
return ground; return ground;
} }
} }
@ -59,7 +93,7 @@ class BlobPart extends BodyComponent {
final fixtureDef = FixtureDef( final fixtureDef = FixtureDef(
shape, shape,
density: 1.0, density: 1.0,
filter: Filter()..groupIndex = -2, friction: 0.2,
); );
body.createFixture(fixtureDef); body.createFixture(fixtureDef);
jointDef.addBody(body); jointDef.addBody(body);
@ -84,29 +118,3 @@ class FallingBox extends BodyComponent {
return body; return body;
} }
} }
class BlobSample extends Forge2DGame with TapDetector {
@override
Future<void> onLoad() async {
final worldCenter = screenToWorld(size * camera.zoom / 2);
final blobCenter = worldCenter + Vector2(0, -30);
final blobRadius = Vector2.all(6.0);
addAll(createBoundaries(this));
add(Ground(worldCenter));
final jointDef = ConstantVolumeJointDef()
..frequencyHz = 20.0
..dampingRatio = 1.0
..collideConnected = false;
await addAll([
for (var i = 0; i < 20; i++) BlobPart(i, jointDef, blobRadius, blobCenter)
]);
world.createJoint(ConstantVolumeJoint(world, jointDef));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
add(FallingBox(details.eventPosition.game));
}
}

View File

@ -0,0 +1,21 @@
import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'domino_example.dart';
import 'sprite_body_example.dart';
class CameraExample extends DominoExample {
static const String description = '''
This example showcases the possibility to follow BodyComponents with the
camera. When the screen is tapped a pizza is added, which the camera will
follow. Other than that it is the same as the domino example.
''';
@override
void onTapDown(TapDownInfo details) {
final position = details.eventPosition.game;
final pizza = Pizza(position);
add(pizza);
pizza.mounted.whenComplete(() => camera.followBodyComponent(pizza));
}
}

View File

@ -5,18 +5,18 @@ import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
const TextStyle _textStyle = TextStyle(color: Colors.white, fontSize: 2); const TextStyle _textStyle = TextStyle(color: Colors.white, fontSize: 2);
class CompositionSample extends Forge2DGame with HasTappables { class CompositionExample extends Forge2DGame with HasTappables {
static const info = ''' static const description = '''
This example shows how to compose a `BodyComponent` together with a normal Flame This example shows how to compose a `BodyComponent` together with a normal
component. Click the ball to see the number increment. Flame component. Click the ball to see the number increment.
'''; ''';
CompositionSample() : super(zoom: 20, gravity: Vector2(0, 10.0)); CompositionExample() : super(zoom: 20, gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

View File

@ -3,17 +3,18 @@ import 'dart:math' as math;
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class ContactCallbacksSample extends Forge2DGame with TapDetector { class ContactCallbacksExample extends Forge2DGame with TapDetector {
static const info = ''' static const description = '''
This example shows how `BodyComponent`s can react to collisions with other This example shows how `BodyComponent`s can react to collisions with other
bodies. bodies.
Tap the screen to add balls, the white balls will give an impulse to the balls Tap the screen to add balls, the white balls will give an impulse to the
that it collides with. balls that it collides with.
'''; ''';
ContactCallbacksSample() : super(gravity: Vector2(0, 10.0));
ContactCallbacksExample() : super(gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

View File

@ -3,8 +3,49 @@ import 'dart:ui';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'boundaries.dart'; import 'sprite_body_example.dart';
import 'sprite_body_sample.dart'; import 'utils/boundaries.dart';
class DominoExample extends Forge2DGame with TapDetector {
static const description = '''
In this example we can see some domino tiles lined up.
If you tap on the screen a pizza is added which can tip the tiles over and
cause a chain reaction.
''';
DominoExample() : super(gravity: Vector2(0, 10.0));
late Image pizzaImage;
@override
Future<void> onLoad() async {
final boundaries = createBoundaries(this);
boundaries.forEach(add);
final center = screenToWorld(camera.viewport.effectiveSize / 2);
const numberOfRows = 7;
for (var i = 0; i < numberOfRows - 2; i++) {
final position = center + Vector2(0.0, 5.0 * i);
add(Platform(position));
}
const numberPerRow = 25;
for (var i = 0; i < numberOfRows; ++i) {
for (var j = 0; j < numberPerRow; j++) {
final position = center +
Vector2(-14.75 + j * (29.5 / (numberPerRow - 1)), -12.7 + 5 * i);
add(DominoBrick(position));
}
}
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final position = details.eventPosition.game;
add(Pizza(position)..renderBody = true);
}
}
class Platform extends BodyComponent { class Platform extends BodyComponent {
final Vector2 position; final Vector2 position;
@ -41,38 +82,3 @@ class DominoBrick extends BodyComponent {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }
class DominoSample extends Forge2DGame with TapDetector {
late Image pizzaImage;
DominoSample() : super(gravity: Vector2(0, 10.0));
@override
Future<void> onLoad() async {
final boundaries = createBoundaries(this);
boundaries.forEach(add);
final center = screenToWorld(camera.viewport.effectiveSize / 2);
const numberOfRows = 7;
for (var i = 0; i < numberOfRows - 2; i++) {
final position = center + Vector2(0.0, 5.0 * i);
add(Platform(position));
}
const numberPerRow = 25;
for (var i = 0; i < numberOfRows; ++i) {
for (var j = 0; j < numberPerRow; j++) {
final position = center +
Vector2(-14.75 + j * (29.5 / (numberPerRow - 1)), -12.7 + 5 * i);
add(DominoBrick(position));
}
}
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final position = details.eventPosition.game;
add(Pizza(position)..renderBody = true);
}
}

View File

@ -4,11 +4,17 @@ import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart' hide Draggable; import 'package:flutter/material.dart' hide Draggable;
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class DraggableSample extends Forge2DGame with HasDraggables { class DraggableExample extends Forge2DGame with HasDraggables {
DraggableSample() : super(gravity: Vector2.all(0.0)); static const description = '''
In this example we use Flame's normal `Draggable` mixin to give impulses to
a ball when we are dragging it around. If you are interested in dragging
bodies around, also have a look at the MouseJointExample.
''';
DraggableExample() : super(gravity: Vector2.all(0.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

View File

@ -0,0 +1,108 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import '../../../commons/commons.dart';
import 'animated_body_example.dart';
import 'blob_example.dart';
import 'camera_example.dart';
import 'composition_example.dart';
import 'contact_callbacks_example.dart';
import 'domino_example.dart';
import 'draggable_example.dart';
import 'joint_example.dart';
import 'mouse_joint_example.dart';
import 'raycast_example.dart';
import 'revolute_joint_example.dart';
import 'sprite_body_example.dart';
import 'tappable_example.dart';
import 'widget_example.dart';
String link(String example) => baseLink('bride_libraries/$example');
void addForge2DStories(Dashbook dashbook) {
dashbook.storiesOf('flame_forge2d')
..add(
'Blob example',
(DashbookContext ctx) => GameWidget(game: BlobExample()),
codeLink: link('blob_example.dart'),
info: BlobExample.description,
)
..add(
'Composition example',
(DashbookContext ctx) => GameWidget(game: CompositionExample()),
codeLink: link('composition_example.dart'),
info: CompositionExample.description,
)
..add(
'Domino example',
(DashbookContext ctx) => GameWidget(game: DominoExample()),
codeLink: link('domino_example.dart'),
info: DominoExample.description,
)
..add(
'Contact Callbacks',
(DashbookContext ctx) => GameWidget(game: ContactCallbacksExample()),
codeLink: link('contact_callbacks_example.dart'),
info: ContactCallbacksExample.description,
)
..add(
'RevoluteJoint',
(DashbookContext ctx) => GameWidget(game: RevoluteJointExample()),
codeLink: link('revolute_joint_example.dart'),
info: RevoluteJointExample.description,
)
..add(
'Sprite Bodies',
(DashbookContext ctx) => GameWidget(game: SpriteBodyExample()),
codeLink: link('sprite_body_example.dart'),
info: SpriteBodyExample.description,
)
..add(
'Animated Bodies',
(DashbookContext ctx) => GameWidget(game: AnimatedBodyExample()),
codeLink: link('animated_body_example.dart'),
info: AnimatedBodyExample.description,
)
..add(
'Tappable Body',
(DashbookContext ctx) => GameWidget(game: TappableExample()),
codeLink: link('tappable_example.dart'),
info: TappableExample.description,
)
..add(
'Draggable Body',
(DashbookContext ctx) => GameWidget(game: DraggableExample()),
codeLink: link('draggable_example.dart'),
info: DraggableExample.description,
)
..add(
'Basic joint',
(DashbookContext ctx) => GameWidget(game: JointExample()),
codeLink: link('joint_example.dart'),
info: JointExample.description,
)
..add(
'Mouse Joint',
(DashbookContext ctx) => GameWidget(game: MouseJointExample()),
codeLink: link('mouse_joint_example.dart'),
info: MouseJointExample.description,
)
..add(
'Camera',
(DashbookContext ctx) => GameWidget(game: CameraExample()),
codeLink: link('camera_example.dart'),
info: CameraExample.description,
)
..add(
'Raycasting',
(DashbookContext ctx) => GameWidget(game: RaycastExample()),
codeLink: link('raycast_example.dart'),
info: RaycastExample.description,
)
..add(
'Widgets',
(DashbookContext ctx) => const BodyWidgetExample(),
codeLink: link('widget_example.dart'),
info: WidgetExample.description,
);
}

View File

@ -0,0 +1,18 @@
//
// Generated file. Do not edit.
//
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

View File

@ -3,8 +3,32 @@ import 'dart:math';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class JointExample extends Forge2DGame with TapDetector {
static const description = '''
In this example we use a joint to keep a body with several fixtures stuck
to another body.
Tap the screen to add more of these combined bodies.
''';
JointExample() : super(gravity: Vector2(0, 10.0));
@override
Future<void> onLoad() async {
addAll(createBoundaries(this));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final ball = Ball(details.eventPosition.game);
add(ball);
add(CircleShuffler(ball));
}
}
class CircleShuffler extends BodyComponent { class CircleShuffler extends BodyComponent {
final Ball ball; final Ball ball;
@ -39,27 +63,10 @@ class CircleShuffler extends BodyComponent {
body.createFixture(fixtureDef); body.createFixture(fixtureDef);
} }
final revoluteJointDef = RevoluteJointDef() final jointDef = RevoluteJointDef()
..initialize(body, ball.body, body.position); ..initialize(body, ball.body, body.position);
world.createJoint(RevoluteJoint(jointDef));
world.createJoint(RevoluteJoint(revoluteJointDef));
return body; return body;
} }
} }
class JointSample extends Forge2DGame with TapDetector {
JointSample() : super(gravity: Vector2(0, 10.0));
@override
Future<void> onLoad() async {
addAll(createBoundaries(this));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final ball = Ball(details.eventPosition.game);
add(ball);
add(CircleShuffler(ball));
}
}

View File

@ -1,17 +1,22 @@
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'balls.dart'; import 'revolute_joint_example.dart';
import 'boundaries.dart'; import 'utils/balls.dart';
import 'circle_stress_sample.dart'; import 'utils/boundaries.dart';
class MouseJointExample extends Forge2DGame with MultiTouchDragDetector {
static const description = '''
In this example we use a `MouseJoint` to make the ball follow the mouse
when you drag it around.
''';
MouseJointExample() : super(gravity: Vector2(0, 10.0));
class MouseJointSample extends Forge2DGame with MultiTouchDragDetector {
late Ball ball; late Ball ball;
late Body groundBody; late Body groundBody;
MouseJoint? mouseJoint; MouseJoint? mouseJoint;
MouseJointSample() : super(gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
final boundaries = createBoundaries(this); final boundaries = createBoundaries(this);

View File

@ -4,80 +4,16 @@ import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart' show Colors, Paint, Canvas; import 'package:flutter/material.dart' show Colors, Paint, Canvas;
import 'boundaries.dart'; import 'utils/boundaries.dart';
const raycastSampleDescription = ''' class RaycastExample extends Forge2DGame
This example shows how raycasts can be used to find nearest and farthest fixtures.
Red ray finds the nearest fixture and blue ray finds the farthest fixture.
''';
class Box extends BodyComponent {
final Vector2 position;
Box(this.position);
@override
Body createBody() {
final shape = PolygonShape()..setAsBoxXY(2.0, 4.0);
final fixtureDef = FixtureDef(shape, userData: this);
final bodyDef = BodyDef(position: position);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class NearestBoxRayCastCallback extends RayCastCallback {
Box? box;
Vector2? nearestPoint;
Vector2? normalAtInter;
@override
double reportFixture(
Fixture fixture,
Vector2 point,
Vector2 normal,
double fraction,
) {
nearestPoint = point.clone();
normalAtInter = normal.clone();
box = fixture.userData as Box?;
// Returning fraction implies that we care only about
// fixtures that are closer to ray start point than
// the current fixture
return fraction;
}
}
class FarthestBoxRayCastCallback extends RayCastCallback {
Box? box;
Vector2? farthestPoint;
Vector2? normalAtInter;
double previousFraction = 0.0;
@override
double reportFixture(
Fixture fixture,
Vector2 point,
Vector2 normal,
double fraction,
) {
// Value of fraction is directly proportional to
// the distance of fixture from ray start point.
// So we are interested in the current fixture only if
// it has a higher fraction value than previousFraction.
if (previousFraction < fraction) {
farthestPoint = point.clone();
normalAtInter = normal.clone();
box = fixture.userData as Box?;
previousFraction = fraction;
}
return 1;
}
}
class RaycastSample extends Forge2DGame
with TapDetector, MouseMovementDetector { with TapDetector, MouseMovementDetector {
static const String description = '''
This example shows how raycasts can be used to find nearest and farthest
fixtures.
Red ray finds the nearest fixture and blue ray finds the farthest fixture.
''';
final random = Random(); final random = Random();
final redPoints = List<Vector2>.empty(growable: true); final redPoints = List<Vector2>.empty(growable: true);
@ -86,7 +22,7 @@ class RaycastSample extends Forge2DGame
Box? nearestBox; Box? nearestBox;
Box? farthestBox; Box? farthestBox;
RaycastSample() : super(gravity: Vector2.zero()); RaycastExample() : super(gravity: Vector2.zero());
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -192,3 +128,68 @@ class RaycastSample extends Forge2DGame
} }
} }
} }
class Box extends BodyComponent {
final Vector2 position;
Box(this.position);
@override
Body createBody() {
final shape = PolygonShape()..setAsBoxXY(2.0, 4.0);
final fixtureDef = FixtureDef(shape, userData: this);
final bodyDef = BodyDef(position: position);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class NearestBoxRayCastCallback extends RayCastCallback {
Box? box;
Vector2? nearestPoint;
Vector2? normalAtInter;
@override
double reportFixture(
Fixture fixture,
Vector2 point,
Vector2 normal,
double fraction,
) {
nearestPoint = point.clone();
normalAtInter = normal.clone();
box = fixture.userData as Box?;
// Returning fraction implies that we care only about
// fixtures that are closer to ray start point than
// the current fixture
return fraction;
}
}
class FarthestBoxRayCastCallback extends RayCastCallback {
Box? box;
Vector2? farthestPoint;
Vector2? normalAtInter;
double previousFraction = 0.0;
@override
double reportFixture(
Fixture fixture,
Vector2 point,
Vector2 normal,
double fraction,
) {
// Value of fraction is directly proportional to
// the distance of fixture from ray start point.
// So we are interested in the current fixture only if
// it has a higher fraction value than previousFraction.
if (previousFraction < fraction) {
farthestPoint = point.clone();
normalAtInter = normal.clone();
box = fixture.userData as Box?;
previousFraction = fraction;
}
return 1;
}
}

View File

@ -3,19 +3,51 @@ import 'dart:math';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class RevoluteJointExample extends Forge2DGame with TapDetector {
static const String description = '''
This example showcases a revolute joint, which is the spinning balls in the
center.
If you tap the screen some colourful balls are added and will
interact with the bodies tied to the revolute joint once they have fallen
down the funnel.
''';
@override
Future<void> onLoad() async {
final boundaries = createBoundaries(this);
boundaries.forEach(add);
final center = screenToWorld(camera.viewport.effectiveSize / 2);
add(CircleShuffler(center));
add(CornerRamp(center, isMirrored: true));
add(CornerRamp(center));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final tapPosition = details.eventPosition.game;
final random = Random();
List.generate(15, (i) {
final randomVector = (Vector2.random() - Vector2.all(-0.5)).normalized();
add(Ball(tapPosition + randomVector, radius: random.nextDouble()));
});
}
}
class CircleShuffler extends BodyComponent { class CircleShuffler extends BodyComponent {
final Vector2 _center;
CircleShuffler(this._center); CircleShuffler(this._center);
final Vector2 _center;
@override @override
Body createBody() { Body createBody() {
final bodyDef = BodyDef( final bodyDef = BodyDef(
type: BodyType.dynamic, type: BodyType.dynamic,
position: _center + Vector2(0.0, -25.0), position: _center + Vector2(0.0, 25.0),
); );
const numPieces = 5; const numPieces = 5;
const radius = 6.0; const radius = 6.0;
@ -53,11 +85,11 @@ class CircleShuffler extends BodyComponent {
} }
class CornerRamp extends BodyComponent { class CornerRamp extends BodyComponent {
CornerRamp(this._center, {this.isMirrored = false});
final bool isMirrored; final bool isMirrored;
final Vector2 _center; final Vector2 _center;
CornerRamp(this._center, {this.isMirrored = false});
@override @override
Body createBody() { Body createBody() {
final shape = ChainShape(); final shape = ChainShape();
@ -78,26 +110,3 @@ class CornerRamp extends BodyComponent {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }
class CircleStressSample extends Forge2DGame with TapDetector {
@override
Future<void> onLoad() async {
final boundaries = createBoundaries(this);
boundaries.forEach(add);
final center = screenToWorld(camera.viewport.effectiveSize / 2);
add(CircleShuffler(center));
add(CornerRamp(center, isMirrored: true));
add(CornerRamp(center));
}
@override
void onTapDown(TapDownInfo details) {
super.onTapDown(details);
final tapPosition = details.eventPosition.game;
final random = Random();
List.generate(15, (i) {
final randomVector = (Vector2.random() - Vector2.all(-0.5)).normalized();
add(Ball(tapPosition + randomVector, radius: random.nextDouble()));
});
}
}

View File

@ -4,10 +4,15 @@ import 'package:flame/components.dart';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class SpriteBodySample extends Forge2DGame with TapDetector { class SpriteBodyExample extends Forge2DGame with TapDetector {
SpriteBodySample() : super(gravity: Vector2(0, 10.0)); static const String description = '''
In this example we show how to add a sprite on top of a `BodyComponent`.
Tap the screen to add more pizzas.
''';
SpriteBodyExample() : super(gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

View File

@ -3,11 +3,17 @@ import 'package:flame/game.dart';
import 'package:flame/palette.dart'; import 'package:flame/palette.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'balls.dart'; import 'utils/balls.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
class TappableSample extends Forge2DGame with HasTappables { class TappableExample extends Forge2DGame with HasTappables {
TappableSample() : super(zoom: 20, gravity: Vector2(0, 10.0)); static const String description = '''
In this example we show how to use Flame's tappable mixin to react to taps
on `BodyComponent`s.
Tap the ball to give it a random impulse, or the text to add an effect to
it.
''';
TappableExample() : super(zoom: 20, gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

View File

@ -3,20 +3,21 @@ import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart' hide Transform; import 'package:flame_forge2d/flame_forge2d.dart' hide Transform;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'boundaries.dart'; import 'utils/boundaries.dart';
const widgetSampleDescription = ''' class WidgetExample extends Forge2DGame with TapDetector {
This examples shows how to render a widget on top of a Forge2D body. static const String description = '''
'''; This examples shows how to render a widget on top of a Forge2D body outside
of Flame.
''';
class WidgetSample extends Forge2DGame with TapDetector {
List<Function()> updateStates = []; List<Function()> updateStates = [];
Map<int, Body> bodyIdMap = {}; Map<int, Body> bodyIdMap = {};
List<int> addLaterIds = []; List<int> addLaterIds = [];
Vector2 screenPosition(Body body) => worldToScreen(body.worldCenter); Vector2 screenPosition(Body body) => worldToScreen(body.worldCenter);
WidgetSample() : super(zoom: 20, gravity: Vector2(0, 10.0)); WidgetExample() : super(zoom: 20, gravity: Vector2(0, 10.0));
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -38,7 +39,8 @@ class WidgetSample extends Forge2DGame with TapDetector {
final fixtureDef = FixtureDef( final fixtureDef = FixtureDef(
shape, shape,
density: 1.0, density: 1.0,
restitution: 0.95, restitution: 0.8,
friction: 0.2,
); );
body.createFixture(fixtureDef); body.createFixture(fixtureDef);
return body; return body;
@ -59,13 +61,13 @@ class WidgetSample extends Forge2DGame with TapDetector {
} }
} }
class BodyWidgetSample extends StatelessWidget { class BodyWidgetExample extends StatelessWidget {
const BodyWidgetSample({Key? key}) : super(key: key); const BodyWidgetExample({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GameWidget<WidgetSample>( return GameWidget<WidgetExample>(
game: WidgetSample(), game: WidgetExample(),
overlayBuilderMap: { overlayBuilderMap: {
'button1': (ctx, game) { 'button1': (ctx, game) {
return BodyButtonWidget(game, game.createBodyId()); return BodyButtonWidget(game, game.createBodyId());
@ -80,7 +82,7 @@ class BodyWidgetSample extends StatelessWidget {
} }
class BodyButtonWidget extends StatefulWidget { class BodyButtonWidget extends StatefulWidget {
final WidgetSample _game; final WidgetExample _game;
final int _bodyId; final int _bodyId;
const BodyButtonWidget( const BodyButtonWidget(
@ -96,7 +98,7 @@ class BodyButtonWidget extends StatefulWidget {
} }
class _BodyButtonState extends State<BodyButtonWidget> { class _BodyButtonState extends State<BodyButtonWidget> {
final WidgetSample _game; final WidgetExample _game;
final int _bodyId; final int _bodyId;
Body? _body; Body? _body;

View File

@ -40,7 +40,7 @@ class MultipleShapesExample extends FlameGame
add(screenHitbox); add(screenHitbox);
add(snowman); add(snowman);
var totalAdded = 1; var totalAdded = 1;
while (totalAdded < 100) { while (totalAdded < 1000) {
lastToAdd = nextRandomCollidable(lastToAdd, screenHitbox); lastToAdd = nextRandomCollidable(lastToAdd, screenHitbox);
final lastBottomRight = lastToAdd.toAbsoluteRect().bottomRight; final lastBottomRight = lastToAdd.toAbsoluteRect().bottomRight;
if (lastBottomRight.dx < size.x && lastBottomRight.dy < size.y) { if (lastBottomRight.dx < size.x && lastBottomRight.dy < size.y) {

View File

@ -12,6 +12,7 @@ environment:
dependencies: dependencies:
flame: ^1.1.1 flame: ^1.1.1
flame_svg: ^1.2.0 flame_svg: ^1.2.0
flame_forge2d: ^0.11.0
dashbook: 0.1.6 dashbook: 0.1.6
flutter: flutter:
sdk: flutter sdk: flutter

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,15 +0,0 @@
import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'domino_sample.dart';
import 'sprite_body_sample.dart';
class CameraSample extends DominoSample {
@override
void onTapDown(TapDownInfo details) {
final position = details.eventPosition.game;
final pizza = Pizza(position);
add(pizza);
pizza.mounted.whenComplete(() => camera.followBodyComponent(pizza));
}
}

View File

@ -1,112 +1,7 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'animated_body_sample.dart';
import 'blob_sample.dart';
import 'camera_sample.dart';
import 'circle_stress_sample.dart';
import 'composition_sample.dart';
import 'contact_callbacks_sample.dart';
import 'domino_sample.dart';
import 'draggable_sample.dart';
import 'joint_sample.dart';
import 'mouse_joint_sample.dart';
import 'raycast_sample.dart';
import 'sprite_body_sample.dart';
import 'tappable_sample.dart';
import 'widget_sample.dart';
String link(String example) =>
'https://github.com/flame-engine/flame_forge2d/tree/main/example/lib/$example';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); // There will be a new example in here after Google I/O, stay tuned!
final dashbook = Dashbook(theme: ThemeData.dark()); // If you want to see the previous examples, go to the flame_forge2d section
// of https://examples.flame-engine.org
dashbook.storiesOf('Flame with Forge2D').decorator(TopDecorator()) // The sourcecode lives here:
..add( // https://github.com/flame-engine/flame/tree/main/examples/lib/stories/bridge_libraries/forge2d
'Blob sample',
(DashbookContext ctx) => GameWidget(game: BlobSample()),
codeLink: link('blob_sample.dart'),
)
..add(
'Composition sample',
(DashbookContext ctx) => GameWidget(game: CompositionSample()),
codeLink: link('composition_sample.dart'),
info: CompositionSample.info,
)
..add(
'Domino sample',
(DashbookContext ctx) => GameWidget(game: DominoSample()),
codeLink: link('domino_sample.dart'),
)
..add(
'Contact Callbacks',
(DashbookContext ctx) => GameWidget(game: ContactCallbacksSample()),
codeLink: link('contact_callbacks_sample.dart'),
)
..add(
'Circle stress sample',
(DashbookContext ctx) => GameWidget(game: CircleStressSample()),
codeLink: link('circle_stress_sample.dart'),
)
..add(
'Sprite Bodies',
(DashbookContext ctx) => GameWidget(game: SpriteBodySample()),
codeLink: link('sprite_body_sample.dart'),
)
..add(
'PositionBodyComponent',
(DashbookContext ctx) => GameWidget(game: PositionBodySample()),
codeLink: link('animated_body_sample.dart'),
)
..add(
'Tappable Body',
(DashbookContext ctx) => GameWidget(game: TappableSample()),
codeLink: link('tappable_sample.dart'),
)
..add(
'Draggable Body',
(DashbookContext ctx) => GameWidget(game: DraggableSample()),
codeLink: link('draggable_sample.dart'),
)
..add(
'Basic joint',
(DashbookContext ctx) => GameWidget(game: JointSample()),
codeLink: link('joint_sample.dart'),
)
..add(
'Mouse Joint',
(DashbookContext ctx) => GameWidget(game: MouseJointSample()),
codeLink: link('mouse_joint_sample.dart'),
)
..add(
'Camera',
(DashbookContext ctx) => GameWidget(game: CameraSample()),
codeLink: link('camera_sample.dart'),
)
..add(
'Widget sample',
(DashbookContext ctx) => const BodyWidgetSample(),
info: widgetSampleDescription,
codeLink: link('widget_sample.dart'),
)
..add(
'Raycast sample',
(DashbookContext ctx) => GameWidget(game: RaycastSample()),
codeLink: link('raycast_sample.dart'),
info: raycastSampleDescription,
);
runApp(dashbook);
}
class TopDecorator extends Decorator {
@override
Widget decorate(Widget child) {
return Align(
child: child,
alignment: Alignment.topCenter,
);
}
} }

View File

@ -25,6 +25,3 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
assets:
- assets/images/pizza.png
- assets/images/chopper.png