test: plunger and joint

pull/10/head
Allison Ryan 4 years ago
parent 92590b9c82
commit 7048618ce5

@ -1,6 +1,14 @@
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
/// {@template plunger}
/// Plunger body component to be pulled and released by the player to launch
/// the pinball.
///
/// The plunger body ignores gravity so the player can control its downward
/// pull.
/// {@endtemplate}
class Plunger extends BodyComponent { class Plunger extends BodyComponent {
/// {@macro plunger}
Plunger(this._position); Plunger(this._position);
final Vector2 _position; final Vector2 _position;
@ -9,24 +17,52 @@ class Plunger extends BodyComponent {
Body createBody() { Body createBody() {
final shape = PolygonShape()..setAsBoxXY(2.5, 1.5); final shape = PolygonShape()..setAsBoxXY(2.5, 1.5);
final fixtureDef = FixtureDef(shape)..friction = 0.1; final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef() final bodyDef = BodyDef()
..userData = this ..userData = this
..position = _position ..position = _position
..type = BodyType.dynamic; ..type = BodyType.dynamic
..gravityScale = 0;
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
// Unused for now - from the previous kinematic plunger implementation. /// Set a contstant downward velocity on the plunger body.
void pull() { void pull() {
body.linearVelocity = Vector2(0, -5); body.linearVelocity = Vector2(0, -7);
} }
// Unused for now - from the previous kinematic plunger implementation. /// Set an upward velocity on the plunger body. The velocity's magnitude
/// depends on how far the plunger has been pulled from its original position.
void release() { void release() {
final velocity = (_position.y - body.position.y) * 9; final velocity = (_position.y - body.position.y) * 9;
body.linearVelocity = Vector2(0, velocity); body.linearVelocity = Vector2(0, velocity);
} }
} }
/// {@template plunger_anchor_prismatic_joint_def}
/// Prismatic joint def between a [Plunger] and an anchor body given motion on
/// the vertical axis.
///
/// The [Plunger] is constrained to vertical motion between its starting
/// position and the anchor body. The anchor needs to be below the plunger for
/// this joint to function properly.
/// {@endtemplate}
class PlungerAnchorPrismaticJointDef extends PrismaticJointDef {
/// {@macro plunger_anchor_prismatic_joint_def}
PlungerAnchorPrismaticJointDef({
required Plunger plunger,
required BodyComponent anchor,
}) {
initialize(
plunger.body,
anchor.body,
anchor.body.position,
Vector2(0, -1),
);
enableLimit = true;
lowerTranslation = double.negativeInfinity;
collideConnected = true;
}
}

@ -7,7 +7,6 @@ import 'package:pinball/game/game.dart';
class PinballGame extends Forge2DGame with FlameBloc, KeyboardEvents { class PinballGame extends Forge2DGame with FlameBloc, KeyboardEvents {
late Plunger plunger; late Plunger plunger;
late PrismaticJointDef prismaticJointDef;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -19,32 +18,11 @@ class PinballGame extends Forge2DGame with FlameBloc, KeyboardEvents {
final center = screenToWorld(camera.viewport.effectiveSize / 2); final center = screenToWorld(camera.viewport.effectiveSize / 2);
await add(plunger = Plunger(Vector2(center.x, center.y - 50))); await add(plunger = Plunger(Vector2(center.x, center.y)));
prismaticJointDef = PrismaticJointDef() world.createJoint(
..initialize( PlungerAnchorPrismaticJointDef(plunger: plunger, anchor: bottomWall),
plunger.body, );
bottomWall.body,
plunger.body.position,
// Logically, I feel like this should be (0, 1), but it has to be
// negative for lowerTranslation limit to work as expected.
Vector2(0, -1),
)
..enableLimit = true
// Given the above inverted vertical axis, the lowerTranslation works as
// expected and this lets the plunger fall down 10 units before being
// stopped.
//
// Ideally, we shouldn't need to set any limits here - this is just for
// demo purposes to see how the limits work. We should be leaving this at
// 0 and altering it as the user holds the space bar. The longer they hold
// it, the lower the lowerTranslation becomes - allowing the plunger to
// slowly fall down (see key event handlers below).
..lowerTranslation = -10
// This prevents the plunger from falling through the bottom wall.
..collideConnected = true;
world.createJoint(prismaticJointDef);
} }
@override @override
@ -54,19 +32,11 @@ class PinballGame extends Forge2DGame with FlameBloc, KeyboardEvents {
) { ) {
if (event is RawKeyUpEvent && if (event is RawKeyUpEvent &&
event.data.logicalKey == LogicalKeyboardKey.space) { event.data.logicalKey == LogicalKeyboardKey.space) {
// I haven't been able to successfully pull down the plunger, so this is plunger.release();
// completely untested. I imagine we could calculate the distance between
// the prismaticJoinDef.upperTranslation (plunger starting position) and
// the ground, then use that value as a multiplier on the speed so the
// ball moves faster when you pull the plunger farther down.
prismaticJointDef.motorSpeed = 5;
} }
if (event is RawKeyDownEvent && if (event is RawKeyDownEvent &&
event.data.logicalKey == LogicalKeyboardKey.space) { event.data.logicalKey == LogicalKeyboardKey.space) {
// This was my attempt to decrement the lower limit but it doesn't seem to plunger.pull();
// render. If you debug, you can see that this value is being lowered,
// but the game isn't reflecting these value changes.
prismaticJointDef.lowerTranslation--;
} }
return KeyEventResult.handled; return KeyEventResult.handled;
} }

@ -0,0 +1,192 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/game.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(PinballGame.new);
group('Plunger', () {
flameTester.test(
'loads correctly',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
expect(game.contains(plunger), isTrue);
},
);
group('body', () {
flameTester.test(
'positions correctly',
(game) async {
final position = Vector2.all(10);
final plunger = Plunger(position);
await game.ensureAdd(plunger);
game.contains(plunger);
expect(plunger.body.position, position);
},
);
flameTester.test(
'is dynamic',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
expect(plunger.body.bodyType, equals(BodyType.dynamic));
},
);
flameTester.test(
'ignores gravity',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
expect(plunger.body.gravityScale, isZero);
},
);
});
group('first fixture', () {
flameTester.test(
'exists',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
expect(plunger.body.fixtures[0], isA<Fixture>());
},
);
flameTester.test(
'shape is a polygon',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
final fixture = plunger.body.fixtures[0];
expect(fixture.shape.shapeType, equals(ShapeType.polygon));
},
);
});
flameTester.test(
'pull sets a negative linear velocity',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
plunger.pull();
expect(plunger.body.linearVelocity.y, isNegative);
},
);
group('release', () {
flameTester.test(
'does not set a linear velocity '
'when plunger is in starting position',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
plunger.release();
expect(plunger.body.linearVelocity.y, isZero);
},
);
flameTester.test(
'sets a positive linear velocity '
'when plunger is below starting position',
(game) async {
final plunger = Plunger(Vector2.zero());
await game.ensureAdd(plunger);
plunger.body.setTransform(Vector2(0, -1), 0);
plunger.release();
expect(plunger.body.linearVelocity.y, isPositive);
},
);
});
});
group('PlungerAnchorPrismaticJointDef', () {
final plunger = Plunger(Vector2.zero())..createBody();
final anchor = Plunger(Vector2(0, -5))..createBody();
group('initializes with', () {
flameTester.test(
'plunger as bodyA',
(game) async {
final jointDef = PlungerAnchorPrismaticJointDef(
plunger: plunger,
anchor: anchor,
);
expect(jointDef.bodyA, equals(plunger));
},
);
flameTester.test(
'anchor as bodyB',
(game) async {
final jointDef = PlungerAnchorPrismaticJointDef(
plunger: plunger,
anchor: anchor,
);
game.world.createJoint(jointDef);
expect(jointDef.bodyB, equals(anchor));
},
);
flameTester.test(
'limits enabled',
(game) async {
final jointDef = PlungerAnchorPrismaticJointDef(
plunger: plunger,
anchor: anchor,
);
game.world.createJoint(jointDef);
expect(jointDef.enableLimit, isTrue);
},
);
flameTester.test(
'lower translation limit as negative infinity',
(game) async {
final jointDef = PlungerAnchorPrismaticJointDef(
plunger: plunger,
anchor: anchor,
);
game.world.createJoint(jointDef);
expect(jointDef.lowerTranslation, equals(double.negativeInfinity));
},
);
flameTester.test(
'connected body collison enabled',
(game) async {
final jointDef = PlungerAnchorPrismaticJointDef(
plunger: plunger,
anchor: anchor,
);
game.world.createJoint(jointDef);
expect(jointDef.collideConnected, isTrue);
},
);
});
});
}
Loading…
Cancel
Save