Merge branch 'chore/dialog-background' into ui/how_to_play_with_component

pull/242/head
jonathandaniels-vgv 3 years ago committed by GitHub
commit 87d7653806
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,17 +18,8 @@ class Board extends Component {
final flutterForest = FlutterForest(); final flutterForest = FlutterForest();
// TODO(alestiago): adjust positioning to real design.
// TODO(alestiago): add dino in pinball game.
final dino = ChromeDino()
..initialPosition = Vector2(
BoardDimensions.bounds.center.dx + 25,
BoardDimensions.bounds.center.dy - 10,
);
await addAll([ await addAll([
bottomGroup, bottomGroup,
dino,
flutterForest, flutterForest,
]); ]);
} }

@ -34,8 +34,10 @@ extension PinballGameAssetsX on PinballGame {
images.load( images.load(
components.Assets.images.launchRamp.backgroundRailing.keyName, components.Assets.images.launchRamp.backgroundRailing.keyName,
), ),
images.load(components.Assets.images.dino.dinoLandTop.keyName), images.load(components.Assets.images.dino.bottomWall.keyName),
images.load(components.Assets.images.dino.dinoLandBottom.keyName), images.load(components.Assets.images.dino.topWall.keyName),
images.load(components.Assets.images.dino.animatronic.head.keyName),
images.load(components.Assets.images.dino.animatronic.mouth.keyName),
images.load(components.Assets.images.dash.animatronic.keyName), images.load(components.Assets.images.dash.animatronic.keyName),
images.load(components.Assets.images.dash.bumper.a.active.keyName), images.load(components.Assets.images.dash.bumper.a.active.keyName),
images.load(components.Assets.images.dash.bumper.a.inactive.keyName), images.load(components.Assets.images.dash.bumper.a.inactive.keyName),
@ -76,13 +78,11 @@ extension PinballGameAssetsX on PinballGame {
components.Assets.images.spaceship.ramp.arrow.active5.keyName, components.Assets.images.spaceship.ramp.arrow.active5.keyName,
), ),
images.load(components.Assets.images.spaceship.rail.main.keyName), images.load(components.Assets.images.spaceship.rail.main.keyName),
images.load(components.Assets.images.spaceship.rail.foreground.keyName), images.load(components.Assets.images.spaceship.rail.exit.keyName),
images.load(components.Assets.images.androidBumper.a.lit.keyName), images.load(components.Assets.images.androidBumper.a.lit.keyName),
images.load(components.Assets.images.androidBumper.a.dimmed.keyName), images.load(components.Assets.images.androidBumper.a.dimmed.keyName),
images.load(components.Assets.images.androidBumper.b.lit.keyName), images.load(components.Assets.images.androidBumper.b.lit.keyName),
images.load(components.Assets.images.androidBumper.b.dimmed.keyName), images.load(components.Assets.images.androidBumper.b.dimmed.keyName),
images.load(components.Assets.images.chromeDino.mouth.keyName),
images.load(components.Assets.images.chromeDino.head.keyName),
images.load(components.Assets.images.sparky.computer.top.keyName), images.load(components.Assets.images.sparky.computer.top.keyName),
images.load(components.Assets.images.sparky.computer.base.keyName), images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.animatronic.keyName), images.load(components.Assets.images.sparky.animatronic.keyName),

@ -6,6 +6,7 @@ import 'package:flame/game.dart';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart'; import 'package:pinball/gen/assets.gen.dart';
@ -18,7 +19,8 @@ class PinballGame extends Forge2DGame
with with
FlameBloc, FlameBloc,
HasKeyboardHandlerComponents, HasKeyboardHandlerComponents,
Controls<_GameBallsController> { Controls<_GameBallsController>,
TapDetector {
PinballGame({ PinballGame({
required this.characterTheme, required this.characterTheme,
required this.audio, required this.audio,
@ -57,6 +59,7 @@ class PinballGame extends Forge2DGame
await addFromBlueprint(AndroidAcres()); await addFromBlueprint(AndroidAcres());
unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls())); unawaited(addFromBlueprint(DinoWalls()));
await add(ChromeDino()..initialPosition = Vector2(12.3, -6.9));
await add( await add(
GoogleWord( GoogleWord(
position: Vector2( position: Vector2(
@ -69,6 +72,61 @@ class PinballGame extends Forge2DGame
controller.attachTo(launcher.components.whereType<Plunger>().first); controller.attachTo(launcher.components.whereType<Plunger>().first);
await super.onLoad(); await super.onLoad();
} }
BoardSide? focusedBoardSide;
@override
void onTapDown(TapDownInfo info) {
if (info.raw.kind == PointerDeviceKind.touch) {
final rocket = children.whereType<RocketSpriteComponent>().first;
final bounds = rocket.topLeftPosition & rocket.size;
// NOTE(wolfen): As long as Flame does not have https://github.com/flame-engine/flame/issues/1586 we need to check it at the highest level manually.
if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.pull();
} else {
final leftSide = info.eventPosition.widget.x < canvasSize.x / 2;
focusedBoardSide = leftSide ? BoardSide.left : BoardSide.right;
final flippers = descendants().whereType<Flipper>().where((flipper) {
return flipper.side == focusedBoardSide;
});
flippers.first.moveUp();
}
}
super.onTapDown(info);
}
@override
void onTapUp(TapUpInfo info) {
final rocket = descendants().whereType<RocketSpriteComponent>().first;
final bounds = rocket.topLeftPosition & rocket.size;
if (bounds.contains(info.eventPosition.game.toOffset())) {
children.whereType<Plunger>().first.release();
} else {
_moveFlippersDown();
}
super.onTapUp(info);
}
@override
void onTapCancel() {
children.whereType<Plunger>().first.release();
_moveFlippersDown();
super.onTapCancel();
}
void _moveFlippersDown() {
if (focusedBoardSide != null) {
final flippers = descendants().whereType<Flipper>().where((flipper) {
return flipper.side == focusedBoardSide;
});
flippers.first.moveDown();
focusedBoardSide = null;
}
}
} }
class _GameBallsController extends ComponentController<PinballGame> class _GameBallsController extends ComponentController<PinballGame>
@ -115,7 +173,7 @@ class _GameBallsController extends ComponentController<PinballGame>
} }
} }
class DebugPinballGame extends PinballGame with FPSCounter, TapDetector { class DebugPinballGame extends PinballGame with FPSCounter {
DebugPinballGame({ DebugPinballGame({
required CharacterTheme characterTheme, required CharacterTheme characterTheme,
required PinballAudio audio, required PinballAudio audio,
@ -152,9 +210,11 @@ class DebugPinballGame extends PinballGame with FPSCounter, TapDetector {
@override @override
void onTapUp(TapUpInfo info) { void onTapUp(TapUpInfo info) {
add( super.onTapUp(info);
ControlledBall.debug()..initialPosition = info.eventPosition.game,
); if (info.raw.kind == PointerDeviceKind.mouse) {
add(ControlledBall.debug()..initialPosition = info.eventPosition.game);
}
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 61 KiB

@ -16,8 +16,6 @@ class $AssetsImagesGen {
$AssetsImagesBallGen get ball => const $AssetsImagesBallGen(); $AssetsImagesBallGen get ball => const $AssetsImagesBallGen();
$AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen();
$AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen();
$AssetsImagesChromeDinoGen get chromeDino =>
const $AssetsImagesChromeDinoGen();
$AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $AssetsImagesDashGen get dash => const $AssetsImagesDashGen();
$AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen();
$AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen();
@ -97,18 +95,6 @@ class $AssetsImagesBoundaryGen {
const AssetGenImage('assets/images/boundary/outer.png'); const AssetGenImage('assets/images/boundary/outer.png');
} }
class $AssetsImagesChromeDinoGen {
const $AssetsImagesChromeDinoGen();
/// File path: assets/images/chrome_dino/head.png
AssetGenImage get head =>
const AssetGenImage('assets/images/chrome_dino/head.png');
/// File path: assets/images/chrome_dino/mouth.png
AssetGenImage get mouth =>
const AssetGenImage('assets/images/chrome_dino/mouth.png');
}
class $AssetsImagesDashGen { class $AssetsImagesDashGen {
const $AssetsImagesDashGen(); const $AssetsImagesDashGen();
@ -122,13 +108,16 @@ class $AssetsImagesDashGen {
class $AssetsImagesDinoGen { class $AssetsImagesDinoGen {
const $AssetsImagesDinoGen(); const $AssetsImagesDinoGen();
/// File path: assets/images/dino/dino-land-bottom.png $AssetsImagesDinoAnimatronicGen get animatronic =>
AssetGenImage get dinoLandBottom => const $AssetsImagesDinoAnimatronicGen();
const AssetGenImage('assets/images/dino/dino-land-bottom.png');
/// File path: assets/images/dino/bottom-wall.png
AssetGenImage get bottomWall =>
const AssetGenImage('assets/images/dino/bottom-wall.png');
/// File path: assets/images/dino/dino-land-top.png /// File path: assets/images/dino/top-wall.png
AssetGenImage get dinoLandTop => AssetGenImage get topWall =>
const AssetGenImage('assets/images/dino/dino-land-top.png'); const AssetGenImage('assets/images/dino/top-wall.png');
} }
class $AssetsImagesFlipperGen { class $AssetsImagesFlipperGen {
@ -304,12 +293,24 @@ class $AssetsImagesDashBumperGen {
const $AssetsImagesDashBumperMainGen(); const $AssetsImagesDashBumperMainGen();
} }
class $AssetsImagesDinoAnimatronicGen {
const $AssetsImagesDinoAnimatronicGen();
/// File path: assets/images/dino/animatronic/head.png
AssetGenImage get head =>
const AssetGenImage('assets/images/dino/animatronic/head.png');
/// File path: assets/images/dino/animatronic/mouth.png
AssetGenImage get mouth =>
const AssetGenImage('assets/images/dino/animatronic/mouth.png');
}
class $AssetsImagesSpaceshipRailGen { class $AssetsImagesSpaceshipRailGen {
const $AssetsImagesSpaceshipRailGen(); const $AssetsImagesSpaceshipRailGen();
/// File path: assets/images/spaceship/rail/foreground.png /// File path: assets/images/spaceship/rail/exit.png
AssetGenImage get foreground => AssetGenImage get exit =>
const AssetGenImage('assets/images/spaceship/rail/foreground.png'); const AssetGenImage('assets/images/spaceship/rail/exit.png');
/// File path: assets/images/spaceship/rail/main.png /// File path: assets/images/spaceship/rail/main.png
AssetGenImage get main => AssetGenImage get main =>

@ -67,7 +67,7 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef {
_BottomBoundarySpriteComponent() _BottomBoundarySpriteComponent()
: super( : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-5.4, 55.6), position: Vector2(-5, 55.6),
); );
@override @override

@ -1,31 +1,33 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math;
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart' hide Timer; import 'package:flame_forge2d/flame_forge2d.dart' hide Timer;
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template chrome_dino} /// {@template chrome_dino}
/// Dinosaur that gobbles up a [Ball], swivel his head around, and shoots it /// Dino that swivels back and forth, opening its mouth to eat a [Ball].
/// back out. ///
/// Upon eating a [Ball], the dino rotates and spits the [Ball] out in a
/// different direction.
/// {@endtemplate} /// {@endtemplate}
class ChromeDino extends BodyComponent with InitialPosition { class ChromeDino extends BodyComponent with InitialPosition {
/// {@macro chrome_dino} /// {@macro chrome_dino}
ChromeDino() ChromeDino()
: super( : super(
// TODO(alestiago): Remove once sprites are defined.
paint: Paint()..color = Colors.blue,
priority: RenderPriority.dino, priority: RenderPriority.dino,
renderBody: false,
); );
/// The size of the dinosaur mouth. /// The size of the dinosaur mouth.
static final size = Vector2(5, 2.5); static final size = Vector2(5.5, 5);
/// Anchors the [ChromeDino] to the [RevoluteJoint] that controls its arc /// Anchors the [ChromeDino] to the [RevoluteJoint] that controls its arc
/// motion. /// motion.
Future<_ChromeDinoJoint> _anchorToJoint() async { Future<_ChromeDinoJoint> _anchorToJoint() async {
final anchor = _ChromeDinoAnchor(); // TODO(allisonryan0002): try moving to anchor after new body is defined.
final anchor = _ChromeDinoAnchor()
..initialPosition = initialPosition + Vector2(9, -4);
await add(anchor); await add(anchor);
final jointDef = _ChromeDinoAnchorRevoluteJointDef( final jointDef = _ChromeDinoAnchorRevoluteJointDef(
@ -42,9 +44,11 @@ class ChromeDino extends BodyComponent with InitialPosition {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
final joint = await _anchorToJoint(); final joint = await _anchorToJoint();
const framesInAnimation = 98;
const animationFPS = 1 / 24;
await add( await add(
TimerComponent( TimerComponent(
period: 1, period: (framesInAnimation / 2) * animationFPS,
onTick: joint._swivel, onTick: joint._swivel,
repeat: true, repeat: true,
), ),
@ -54,44 +58,17 @@ class ChromeDino extends BodyComponent with InitialPosition {
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixtureDefs = <FixtureDef>[]; final fixtureDefs = <FixtureDef>[];
// TODO(alestiago): Subject to change when sprites are added. // TODO(allisonryan0002): Update this shape to better match sprite.
final box = PolygonShape()..setAsBoxXY(size.x / 2, size.y / 2); final box = PolygonShape()
final fixtureDef = FixtureDef( ..setAsBox(
box, size.x / 2,
density: 999, size.y / 2,
friction: 0.3, initialPosition + Vector2(-4, 2),
restitution: 0.1, -_ChromeDinoJoint._halfSweepingAngle,
isSensor: true, );
); final fixtureDef = FixtureDef(box, density: 1);
fixtureDefs.add(fixtureDef); fixtureDefs.add(fixtureDef);
// FIXME(alestiago): Investigate why adding these fixtures is considered as
// an invalid contact type.
// final upperEdge = EdgeShape()
// ..set(
// Vector2(-size.x / 2, -size.y / 2),
// Vector2(size.x / 2, -size.y / 2),
// );
// final upperEdgeDef = FixtureDef(upperEdge)..density = 0.5;
// fixtureDefs.add(upperEdgeDef);
// final lowerEdge = EdgeShape()
// ..set(
// Vector2(-size.x / 2, size.y / 2),
// Vector2(size.x / 2, size.y / 2),
// );
// final lowerEdgeDef = FixtureDef(lowerEdge)..density = 0.5;
// fixtureDefs.add(lowerEdgeDef);
// final rightEdge = EdgeShape()
// ..set(
// Vector2(size.x / 2, -size.y / 2),
// Vector2(size.x / 2, size.y / 2),
// );
// final rightEdgeDef = FixtureDef(rightEdge)..density = 0.5;
// fixtureDefs.add(rightEdgeDef);
return fixtureDefs; return fixtureDefs;
} }
@ -110,13 +87,18 @@ class ChromeDino extends BodyComponent with InitialPosition {
} }
} }
/// {@template flipper_anchor}
/// [JointAnchor] positioned at the end of a [ChromeDino].
/// {@endtemplate}
class _ChromeDinoAnchor extends JointAnchor { class _ChromeDinoAnchor extends JointAnchor {
/// {@macro flipper_anchor} _ChromeDinoAnchor();
_ChromeDinoAnchor() {
initialPosition = Vector2(ChromeDino.size.x / 2, 0); // TODO(allisonryan0002): if these aren't moved when fixing the rendering, see
// if the joint can be created in onMount to resolve render syncing.
@override
Future<void> onLoad() async {
await super.onLoad();
await addAll([
_ChromeDinoMouthSprite(),
_ChromeDinoHeadSprite(),
]);
} }
} }
@ -135,22 +117,86 @@ class _ChromeDinoAnchorRevoluteJointDef extends RevoluteJointDef {
chromeDino.body.position + anchor.body.position, chromeDino.body.position + anchor.body.position,
); );
enableLimit = true; enableLimit = true;
// TODO(alestiago): Apply design angle value. lowerAngle = -_ChromeDinoJoint._halfSweepingAngle;
const angle = math.pi / 3.5; upperAngle = _ChromeDinoJoint._halfSweepingAngle;
lowerAngle = -angle / 2;
upperAngle = angle / 2;
enableMotor = true; enableMotor = true;
// TODO(alestiago): Tune this values. maxMotorTorque = chromeDino.body.mass * 255;
maxMotorTorque = motorSpeed = chromeDino.body.mass * 30; motorSpeed = 2;
} }
} }
class _ChromeDinoJoint extends RevoluteJoint { class _ChromeDinoJoint extends RevoluteJoint {
_ChromeDinoJoint(_ChromeDinoAnchorRevoluteJointDef def) : super(def); _ChromeDinoJoint(_ChromeDinoAnchorRevoluteJointDef def) : super(def);
static const _halfSweepingAngle = 0.1143;
/// Sweeps the [ChromeDino] up and down repeatedly. /// Sweeps the [ChromeDino] up and down repeatedly.
void _swivel() { void _swivel() {
setMotorSpeed(-motorSpeed); setMotorSpeed(-motorSpeed);
} }
} }
class _ChromeDinoMouthSprite extends SpriteAnimationComponent with HasGameRef {
_ChromeDinoMouthSprite()
: super(
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
angle: _ChromeDinoJoint._halfSweepingAngle,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final image = gameRef.images.fromCache(
Assets.images.dino.animatronic.mouth.keyName,
);
const amountPerRow = 11;
const amountPerColumn = 9;
final textureSize = Vector2(
image.width / amountPerRow,
image.height / amountPerColumn,
);
size = textureSize / 10;
final data = SpriteAnimationData.sequenced(
amount: (amountPerColumn * amountPerRow) - 1,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
);
animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45;
}
}
class _ChromeDinoHeadSprite extends SpriteAnimationComponent with HasGameRef {
_ChromeDinoHeadSprite()
: super(
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
angle: _ChromeDinoJoint._halfSweepingAngle,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final image = gameRef.images.fromCache(
Assets.images.dino.animatronic.head.keyName,
);
const amountPerRow = 11;
const amountPerColumn = 9;
final textureSize = Vector2(
image.width / amountPerRow,
image.height / amountPerColumn,
);
size = textureSize / 10;
final data = SpriteAnimationData.sequenced(
amount: (amountPerColumn * amountPerRow) - 1,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
);
animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45;
}
}

@ -35,51 +35,46 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()
..set( ..set(
Vector2(28.65, -35.1), Vector2(28.65, -34.3),
Vector2(29.5, -35.1), Vector2(29.5, -34.3),
); );
final topStraightFixtureDef = FixtureDef(topStraightShape);
final topCurveShape = BezierCurveShape( final topCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
topStraightShape.vertex1, topStraightShape.vertex1,
Vector2(18.8, -27), Vector2(18.8, -26.2),
Vector2(26.6, -21), Vector2(26.6, -20.2),
], ],
); );
final topCurveFixtureDef = FixtureDef(topCurveShape);
final middleCurveShape = BezierCurveShape( final middleCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
topCurveShape.vertices.last, topCurveShape.vertices.last,
Vector2(27.8, -20.1), Vector2(27.8, -19.3),
Vector2(26.8, -19.5), Vector2(26.8, -18.7),
], ],
); );
final middleCurveFixtureDef = FixtureDef(middleCurveShape);
final bottomCurveShape = BezierCurveShape( final bottomCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
middleCurveShape.vertices.last, middleCurveShape.vertices.last,
Vector2(23, -15), Vector2(23, -14.2),
Vector2(27, -15), Vector2(27, -14.2),
], ],
); );
final bottomCurveFixtureDef = FixtureDef(bottomCurveShape);
final bottomStraightShape = EdgeShape() final bottomStraightShape = EdgeShape()
..set( ..set(
bottomCurveShape.vertices.last, bottomCurveShape.vertices.last,
Vector2(31, -14.5), Vector2(31, -13.7),
); );
final bottomStraightFixtureDef = FixtureDef(bottomStraightShape);
return [ return [
topStraightFixtureDef, FixtureDef(topStraightShape),
topCurveFixtureDef, FixtureDef(topCurveShape),
middleCurveFixtureDef, FixtureDef(middleCurveShape),
bottomCurveFixtureDef, FixtureDef(bottomCurveShape),
bottomStraightFixtureDef, FixtureDef(bottomStraightShape),
]; ];
} }
@ -109,12 +104,12 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef {
await super.onLoad(); await super.onLoad();
final sprite = Sprite( final sprite = Sprite(
gameRef.images.fromCache( gameRef.images.fromCache(
Assets.images.dino.dinoLandTop.keyName, Assets.images.dino.topWall.keyName,
), ),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
position = Vector2(22.8, -38.9); position = Vector2(22.8, -38.1);
} }
} }
@ -131,17 +126,11 @@ class _DinoBottomWall extends BodyComponent with InitialPosition {
); );
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
const restitution = 1.0;
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()
..set( ..set(
Vector2(32.4, -8.8), Vector2(32.4, -8.8),
Vector2(25, -7.7), Vector2(25, -7.7),
); );
final topStraightFixtureDef = FixtureDef(
topStraightShape,
restitution: restitution,
);
final topLeftCurveShape = BezierCurveShape( final topLeftCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
@ -150,36 +139,24 @@ class _DinoBottomWall extends BodyComponent with InitialPosition {
Vector2(29.8, 13.8), Vector2(29.8, 13.8),
], ],
); );
final topLeftCurveFixtureDef = FixtureDef(
topLeftCurveShape,
restitution: restitution,
);
final bottomLeftStraightShape = EdgeShape() final bottomLeftStraightShape = EdgeShape()
..set( ..set(
topLeftCurveShape.vertices.last, topLeftCurveShape.vertices.last,
Vector2(31.9, 44.1), Vector2(31.9, 44.1),
); );
final bottomLeftStraightFixtureDef = FixtureDef(
bottomLeftStraightShape,
restitution: restitution,
);
final bottomStraightShape = EdgeShape() final bottomStraightShape = EdgeShape()
..set( ..set(
bottomLeftStraightShape.vertex2, bottomLeftStraightShape.vertex2,
Vector2(37.8, 44.1), Vector2(37.8, 44.1),
); );
final bottomStraightFixtureDef = FixtureDef(
bottomStraightShape,
restitution: restitution,
);
return [ return [
topStraightFixtureDef, FixtureDef(topStraightShape),
topLeftCurveFixtureDef, FixtureDef(topLeftCurveShape),
bottomLeftStraightFixtureDef, FixtureDef(bottomLeftStraightShape),
bottomStraightFixtureDef, FixtureDef(bottomStraightShape),
]; ];
} }
@ -203,11 +180,11 @@ class _DinoBottomWallSpriteComponent extends SpriteComponent with HasGameRef {
await super.onLoad(); await super.onLoad();
final sprite = Sprite( final sprite = Sprite(
gameRef.images.fromCache( gameRef.images.fromCache(
Assets.images.dino.dinoLandBottom.keyName, Assets.images.dino.bottomWall.keyName,
), ),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
position = Vector2(23.6, -9.5); position = Vector2(23.8, -9.5);
} }
} }

@ -24,7 +24,7 @@ abstract class RenderPriority {
static const int ballOnSpaceship = _above + spaceshipSaucer; static const int ballOnSpaceship = _above + spaceshipSaucer;
/// Render priority for the [Ball] while it's on the [SpaceshipRail]. /// Render priority for the [Ball] while it's on the [SpaceshipRail].
static const int ballOnSpaceshipRail = _below + spaceshipSaucer; static const int ballOnSpaceshipRail = _above + spaceshipRail;
/// Render priority for the [Ball] while it's on the [LaunchRamp]. /// Render priority for the [Ball] while it's on the [LaunchRamp].
static const int ballOnLaunchRamp = _above + launchRamp; static const int ballOnLaunchRamp = _above + launchRamp;
@ -87,9 +87,9 @@ abstract class RenderPriority {
static const int spaceshipRail = _above + bottomGroup; static const int spaceshipRail = _above + bottomGroup;
static const int spaceshipRailForeground = _above + spaceshipRail; static const int spaceshipRailExit = _above + ballOnSpaceshipRail;
static const int spaceshipSaucer = _above + spaceshipRail; static const int spaceshipSaucer = _above + ballOnSpaceshipRail;
static const int spaceshipSaucerWall = _above + spaceshipSaucer; static const int spaceshipSaucerWall = _above + spaceshipSaucer;

@ -2,88 +2,71 @@ import 'dart:math' as math;
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail} /// {@template spaceship_rail}
/// A [Blueprint] for the spaceship drop tube. /// A [Blueprint] for the rail exiting the [Spaceship].
/// {@endtemplate} /// {@endtemplate}
class SpaceshipRail extends Blueprint { class SpaceshipRail extends Blueprint {
/// {@macro spaceship_rail} /// {@macro spaceship_rail}
SpaceshipRail() SpaceshipRail()
: super( : super(
components: [ components: [
_SpaceshipRailRamp(), _SpaceshipRail(),
_SpaceshipRailExit(), _SpaceshipRailExit(),
_SpaceshipRailBase(radius: 0.55) _SpaceshipRailExitSpriteComponent()
..initialPosition = Vector2(-26.15, -18.65),
_SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, 12.9),
_SpaceshipRailForeground()
], ],
); );
} }
class _SpaceshipRailRamp extends BodyComponent with Layered { class _SpaceshipRail extends BodyComponent with Layered {
_SpaceshipRailRamp() _SpaceshipRail()
: super( : super(
priority: RenderPriority.spaceshipRail, priority: RenderPriority.spaceshipRail,
children: [_SpaceshipRailSpriteComponent()],
renderBody: false, renderBody: false,
children: [_SpaceshipRailRampSpriteComponent()],
) { ) {
layer = Layer.spaceshipExitRail; layer = Layer.spaceshipExitRail;
} }
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixturesDefs = <FixtureDef>[];
final topArcShape = ArcShape( final topArcShape = ArcShape(
center: Vector2(-35.5, -30.9), center: Vector2(-35.1, -30.9),
arcRadius: 2.5, arcRadius: 2.5,
angle: math.pi, angle: math.pi,
rotation: 0.2, rotation: 0.2,
); );
final topArcFixtureDef = FixtureDef(topArcShape);
fixturesDefs.add(topArcFixtureDef);
final topLeftCurveShape = BezierCurveShape( final topLeftCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
Vector2(-37.9, -30.4), Vector2(-37.6, -30.4),
Vector2(-38, -23.9), Vector2(-37.8, -23.9),
Vector2(-30.93, -18.2), Vector2(-30.93, -18.2),
], ],
); );
final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape);
fixturesDefs.add(topLeftCurveFixtureDef);
final middleLeftCurveShape = BezierCurveShape( final middleLeftCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
topLeftCurveShape.vertices.last, topLeftCurveShape.vertices.last,
Vector2(-22.6, -10.3), Vector2(-22.6, -10.3),
Vector2(-30, -0.2), Vector2(-29.5, -0.2),
], ],
); );
final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape);
fixturesDefs.add(middleLeftCurveFixtureDef);
final bottomLeftCurveShape = BezierCurveShape( final bottomLeftCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
middleLeftCurveShape.vertices.last, middleLeftCurveShape.vertices.last,
Vector2(-36, 8.6), Vector2(-35.6, 8.6),
Vector2(-32.04, 18.3), Vector2(-31.3, 18.3),
], ],
); );
final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape);
fixturesDefs.add(bottomLeftCurveFixtureDef);
final topRightStraightShape = EdgeShape() final topRightStraightShape = EdgeShape()
..set( ..set(
Vector2(-33, -31.3),
Vector2(-27.2, -21.3), Vector2(-27.2, -21.3),
Vector2(-33, -31.3),
); );
final topRightStraightFixtureDef = FixtureDef(topRightStraightShape);
fixturesDefs.add(topRightStraightFixtureDef);
final middleRightCurveShape = BezierCurveShape( final middleRightCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
@ -92,8 +75,6 @@ class _SpaceshipRailRamp extends BodyComponent with Layered {
Vector2(-25.29, 1.7), Vector2(-25.29, 1.7),
], ],
); );
final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape);
fixturesDefs.add(middleRightCurveFixtureDef);
final bottomRightCurveShape = BezierCurveShape( final bottomRightCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
@ -102,10 +83,16 @@ class _SpaceshipRailRamp extends BodyComponent with Layered {
Vector2(-26.8, 15.7), Vector2(-26.8, 15.7),
], ],
); );
final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape);
fixturesDefs.add(bottomRightCurveFixtureDef);
return fixturesDefs; return [
FixtureDef(topArcShape),
FixtureDef(topLeftCurveShape),
FixtureDef(middleLeftCurveShape),
FixtureDef(bottomLeftCurveShape),
FixtureDef(topRightStraightShape),
FixtureDef(middleRightCurveShape),
FixtureDef(bottomRightCurveShape),
];
} }
@override @override
@ -116,55 +103,47 @@ class _SpaceshipRailRamp extends BodyComponent with Layered {
} }
} }
class _SpaceshipRailRampSpriteComponent extends SpriteComponent class _SpaceshipRailSpriteComponent extends SpriteComponent with HasGameRef {
with HasGameRef { _SpaceshipRailSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-29.4, -5.7),
);
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
final sprite = await gameRef.loadSprite( final sprite = Sprite(
Assets.images.spaceship.rail.main.keyName, gameRef.images.fromCache(
Assets.images.spaceship.rail.main.keyName,
),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-29.4, -5.7);
} }
} }
class _SpaceshipRailForeground extends SpriteComponent with HasGameRef { class _SpaceshipRailExitSpriteComponent extends SpriteComponent
_SpaceshipRailForeground() with HasGameRef {
: super(priority: RenderPriority.spaceshipRailForeground); _SpaceshipRailExitSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-28, 19.4),
priority: RenderPriority.spaceshipRailExit,
);
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
final sprite = await gameRef.loadSprite( final sprite = Sprite(
Assets.images.spaceship.rail.foreground.keyName, gameRef.images.fromCache(
Assets.images.spaceship.rail.exit.keyName,
),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-28.5, 19.7);
}
}
/// Represents the ground bases of the [_SpaceshipRailRamp].
class _SpaceshipRailBase extends BodyComponent with InitialPosition {
_SpaceshipRailBase({required this.radius}) : super(renderBody: false);
final double radius;
@override
Body createBody() {
final shape = CircleShape()..radius = radius;
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef(
position: initialPosition,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }

@ -50,6 +50,7 @@ flutter:
- assets/images/baseboard/ - assets/images/baseboard/
- assets/images/boundary/ - assets/images/boundary/
- assets/images/dino/ - assets/images/dino/
- assets/images/dino/animatronic/
- assets/images/flipper/ - assets/images/flipper/
- assets/images/launch_ramp/ - assets/images/launch_ramp/
- assets/images/dash/ - assets/images/dash/
@ -60,7 +61,6 @@ flutter:
- assets/images/spaceship/rail/ - assets/images/spaceship/rail/
- assets/images/spaceship/ramp/ - assets/images/spaceship/ramp/
- assets/images/spaceship/ramp/arrow/ - assets/images/spaceship/ramp/arrow/
- assets/images/chrome_dino/
- assets/images/kicker/ - assets/images/kicker/
- assets/images/plunger/ - assets/images/plunger/
- assets/images/slingshot/ - assets/images/slingshot/

@ -12,6 +12,10 @@ class SpaceshipRailGame extends BallGame {
color: Colors.blue, color: Colors.blue,
ballPriority: RenderPriority.ballOnSpaceshipRail, ballPriority: RenderPriority.ballOnSpaceshipRail,
ballLayer: Layer.spaceshipExitRail, ballLayer: Layer.spaceshipExitRail,
imagesFileNames: [
Assets.images.spaceship.rail.main.keyName,
Assets.images.spaceship.rail.exit.keyName,
],
); );
static const description = ''' static const description = '''

@ -1,8 +1,22 @@
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class ChromeDinoGame extends Forge2DGame { class ChromeDinoGame extends BallGame {
static const description = 'Shows how a ChromeDino is rendered.'; ChromeDinoGame()
: super(
imagesFileNames: [
Assets.images.dino.animatronic.mouth.keyName,
Assets.images.dino.animatronic.head.keyName,
],
);
static const description = '''
Shows how ChromeDino is rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -10,5 +24,7 @@ class ChromeDinoGame extends Forge2DGame {
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await add(ChromeDino()); await add(ChromeDino());
await traceAllBodies();
} }
} }

@ -4,7 +4,7 @@ import 'package:sandbox/stories/chrome_dino/chrome_dino_game.dart';
void addChromeDinoStories(Dashbook dashbook) { void addChromeDinoStories(Dashbook dashbook) {
dashbook.storiesOf('Chrome Dino').addGame( dashbook.storiesOf('Chrome Dino').addGame(
title: 'Trace', title: 'Traced',
description: ChromeDinoGame.description, description: ChromeDinoGame.description,
gameBuilder: (_) => ChromeDinoGame(), gameBuilder: (_) => ChromeDinoGame(),
); );

@ -20,8 +20,8 @@ class DinoWallGame extends BallGame {
await super.onLoad(); await super.onLoad();
await images.loadAll([ await images.loadAll([
Assets.images.dino.dinoLandTop.keyName, Assets.images.dino.topWall.keyName,
Assets.images.dino.dinoLandBottom.keyName, Assets.images.dino.bottomWall.keyName,
]); ]);
await addFromBlueprint(DinoWalls()); await addFromBlueprint(DinoWalls());

@ -1,13 +1,19 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart';
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(Forge2DGame.new); final assets = [
Assets.images.dino.animatronic.mouth.keyName,
Assets.images.dino.animatronic.head.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
group('ChromeDino', () { group('ChromeDino', () {
flameTester.test( flameTester.test(
@ -20,19 +26,84 @@ void main() {
}, },
); );
flameTester.test( flameTester.testGameWidget(
'swivels', 'renders correctly',
(game) async { setUp: (game, tester) async {
// TODO(alestiago): Write golden tests to check the await game.images.loadAll(assets);
// swivel animation. await game.ensureAdd(ChromeDino());
final chromeDino = ChromeDino(); game.camera.followVector2(Vector2.zero());
await game.ensureAdd(chromeDino); await tester.pump();
},
verify: (game, tester) async {
final sweepAnimationDuration = game
.descendants()
.whereType<SpriteAnimationComponent>()
.first
.animation!
.totalDuration() /
2;
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/chrome_dino/up.png'),
);
final previousPosition = chromeDino.body.position.clone(); game.update(sweepAnimationDuration * 0.25);
game.update(64); await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/chrome_dino/middle.png'),
);
expect(chromeDino.body.position, isNot(equals(previousPosition))); game.update(sweepAnimationDuration * 0.25);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/chrome_dino/down.png'),
);
}, },
); );
group('swivels', () {
flameTester.test(
'up',
(game) async {
final chromeDino = ChromeDino();
await game.ensureAdd(chromeDino);
game.camera.followVector2(Vector2.zero());
final sweepAnimationDuration = game
.descendants()
.whereType<SpriteAnimationComponent>()
.first
.animation!
.totalDuration() /
2;
game.update(sweepAnimationDuration * 1.5);
expect(chromeDino.body.angularVelocity, isPositive);
},
);
flameTester.test(
'down',
(game) async {
final chromeDino = ChromeDino();
await game.ensureAdd(chromeDino);
game.camera.followVector2(Vector2.zero());
final sweepAnimationDuration = game
.descendants()
.whereType<SpriteAnimationComponent>()
.first
.animation!
.totalDuration() /
2;
game.update(sweepAnimationDuration * 0.5);
expect(chromeDino.body.angularVelocity, isNegative);
},
);
});
}); });
} }

@ -45,6 +45,7 @@ void main() {
); );
}, },
); );
flameTester.test( flameTester.test(
'loads correctly', 'loads correctly',
(game) async { (game) async {

@ -12,8 +12,8 @@ void main() {
group('DinoWalls', () { group('DinoWalls', () {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
final assets = [ final assets = [
Assets.images.dino.dinoLandTop.keyName, Assets.images.dino.topWall.keyName,
Assets.images.dino.dinoLandBottom.keyName, Assets.images.dino.bottomWall.keyName,
]; ];
final flameTester = FlameTester(() => TestGame(assets)); final flameTester = FlameTester(() => TestGame(assets));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 92 KiB

@ -11,13 +11,19 @@ import '../../helpers/helpers.dart';
void main() { void main() {
group('SpaceshipRail', () { group('SpaceshipRail', () {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new); final assets = [
Assets.images.spaceship.rail.main.keyName,
Assets.images.spaceship.rail.exit.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
flameTester.testGameWidget( flameTester.testGameWidget(
'renders correctly', 'renders correctly',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(SpaceshipRail()); await game.addFromBlueprint(SpaceshipRail());
await game.ready(); await game.ready();
await tester.pump();
game.camera.followVector2(Vector2.zero()); game.camera.followVector2(Vector2.zero());
game.camera.zoom = 8; game.camera.zoom = 8;

@ -20,36 +20,34 @@ class PixelatedDecoration extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const radius = BorderRadius.all(Radius.circular(12)); const radius = BorderRadius.all(Radius.circular(12));
const borderWidth = 5.0;
return DecoratedBox( return Material(
decoration: BoxDecoration( borderRadius: radius,
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage(Assets.images.dialog.background.keyName),
),
borderRadius: radius,
border: Border.all(
color: Colors.white,
width: borderWidth,
),
),
child: Padding( child: Padding(
padding: const EdgeInsets.all(borderWidth), padding: const EdgeInsets.all(5),
child: ClipRRect( child: DecoratedBox(
borderRadius: radius, decoration: BoxDecoration(
child: Column( borderRadius: radius,
children: [ image: DecorationImage(
Expanded( fit: BoxFit.fill,
child: Center( image: AssetImage(Assets.images.dialog.background.keyName),
child: _header, ),
),
child: ClipRRect(
borderRadius: radius,
child: Column(
children: [
Expanded(
child: Center(
child: _header,
),
),
Expanded(
flex: 4,
child: _body,
), ),
), ],
Expanded( ),
flex: 4,
child: _body,
),
],
), ),
), ),
), ),

@ -21,6 +21,8 @@ void main() {
Assets.images.spaceship.ramp.arrow.active3.keyName, Assets.images.spaceship.ramp.arrow.active3.keyName,
Assets.images.spaceship.ramp.arrow.active4.keyName, Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName, Assets.images.spaceship.ramp.arrow.active5.keyName,
Assets.images.spaceship.rail.main.keyName,
Assets.images.spaceship.rail.exit.keyName,
Assets.images.androidBumper.a.lit.keyName, Assets.images.androidBumper.a.lit.keyName,
Assets.images.androidBumper.a.dimmed.keyName, Assets.images.androidBumper.a.dimmed.keyName,
Assets.images.androidBumper.b.lit.keyName, Assets.images.androidBumper.b.lit.keyName,

@ -105,18 +105,6 @@ void main() {
expect(flutterForest.length, equals(1)); expect(flutterForest.length, equals(1));
}, },
); );
flameTester.test(
'one ChromeDino',
(game) async {
final board = Board();
await game.ready();
await game.ensureAdd(board);
final chromeDino = board.descendants().whereType<ChromeDino>();
expect(chromeDino.length, equals(1));
},
);
}); });
}); });
} }

@ -3,6 +3,7 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
@ -27,10 +28,10 @@ void main() {
Assets.images.boundary.bottom.keyName, Assets.images.boundary.bottom.keyName,
Assets.images.boundary.outer.keyName, Assets.images.boundary.outer.keyName,
Assets.images.boundary.outerBottom.keyName, Assets.images.boundary.outerBottom.keyName,
Assets.images.chromeDino.mouth.keyName, Assets.images.dino.animatronic.mouth.keyName,
Assets.images.chromeDino.head.keyName, Assets.images.dino.animatronic.head.keyName,
Assets.images.dino.dinoLandTop.keyName, Assets.images.dino.topWall.keyName,
Assets.images.dino.dinoLandBottom.keyName, Assets.images.dino.bottomWall.keyName,
Assets.images.dash.animatronic.keyName, Assets.images.dash.animatronic.keyName,
Assets.images.dash.bumper.a.active.keyName, Assets.images.dash.bumper.a.active.keyName,
Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.a.inactive.keyName,
@ -72,7 +73,7 @@ void main() {
Assets.images.spaceship.ramp.arrow.active4.keyName, Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName, Assets.images.spaceship.ramp.arrow.active5.keyName,
Assets.images.spaceship.rail.main.keyName, Assets.images.spaceship.rail.main.keyName,
Assets.images.spaceship.rail.foreground.keyName, Assets.images.spaceship.rail.exit.keyName,
Assets.images.sparky.bumper.a.active.keyName, Assets.images.sparky.bumper.a.active.keyName,
Assets.images.sparky.bumper.a.inactive.keyName, Assets.images.sparky.bumper.a.inactive.keyName,
Assets.images.sparky.bumper.b.active.keyName, Assets.images.sparky.bumper.b.active.keyName,
@ -229,6 +230,181 @@ void main() {
); );
}); });
}); });
group('flipper control', () {
flameTester.test('tap down moves left flipper up', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flippers = game.descendants().whereType<Flipper>().where(
(flipper) => flipper.side == BoardSide.left,
);
game.onTapDown(tapDownEvent);
expect(flippers.first.body.linearVelocity.y, isNegative);
});
flameTester.test('tap down moves right flipper up', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(game.canvasSize);
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flippers = game.descendants().whereType<Flipper>().where(
(flipper) => flipper.side == BoardSide.right,
);
game.onTapDown(tapDownEvent);
expect(flippers.first.body.linearVelocity.y, isNegative);
});
flameTester.test('tap up moves flipper down', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flippers = game.descendants().whereType<Flipper>().where(
(flipper) => flipper.side == BoardSide.left,
);
game.onTapDown(tapDownEvent);
expect(flippers.first.body.linearVelocity.y, isNegative);
final tapUpEvent = MockTapUpInfo();
when(() => tapUpEvent.eventPosition).thenReturn(eventPosition);
game.onTapUp(tapUpEvent);
await game.ready();
expect(flippers.first.body.linearVelocity.y, isPositive);
});
flameTester.test('tap cancel moves flipper down', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flippers = game.descendants().whereType<Flipper>().where(
(flipper) => flipper.side == BoardSide.left,
);
game.onTapDown(tapDownEvent);
expect(flippers.first.body.linearVelocity.y, isNegative);
game.onTapCancel();
expect(flippers.first.body.linearVelocity.y, isPositive);
});
});
group('plunger control', () {
flameTester.test('tap down moves plunger down', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2(40, 60));
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final plunger = game.descendants().whereType<Plunger>().first;
game.onTapDown(tapDownEvent);
expect(plunger.body.linearVelocity.y, equals(7));
});
flameTester.test('tap up releases plunger', (game) async {
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2(40, 60));
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final plunger = game.descendants().whereType<Plunger>().first;
game.onTapDown(tapDownEvent);
expect(plunger.body.linearVelocity.y, equals(7));
final tapUpEvent = MockTapUpInfo();
when(() => tapUpEvent.eventPosition).thenReturn(eventPosition);
game.onTapUp(tapUpEvent);
expect(plunger.body.linearVelocity.y, equals(0));
});
flameTester.test('tap cancel releases plunger', (game) async {
await game.ready();
final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2(40, 60));
final raw = MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final plunger = game.descendants().whereType<Plunger>().first;
game.onTapDown(tapDownEvent);
expect(plunger.body.linearVelocity.y, equals(7));
game.onTapCancel();
expect(plunger.body.linearVelocity.y, equals(0));
});
});
}); });
group('DebugPinballGame', () { group('DebugPinballGame', () {
@ -238,8 +414,12 @@ void main() {
final eventPosition = MockEventPosition(); final eventPosition = MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.all(10)); when(() => eventPosition.game).thenReturn(Vector2.all(10));
final raw = MockTapUpDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.mouse);
final tapUpEvent = MockTapUpInfo(); final tapUpEvent = MockTapUpInfo();
when(() => tapUpEvent.eventPosition).thenReturn(eventPosition); when(() => tapUpEvent.eventPosition).thenReturn(eventPosition);
when(() => tapUpEvent.raw).thenReturn(raw);
final previousBalls = final previousBalls =
game.descendants().whereType<ControlledBall>().toList(); game.descendants().whereType<ControlledBall>().toList();

@ -2,7 +2,7 @@ import 'package:flame/components.dart';
import 'package:flame/game.dart'; import 'package:flame/game.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 'package:flutter/foundation.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
@ -55,8 +55,14 @@ class MockRawKeyUpEvent extends Mock implements RawKeyUpEvent {
} }
} }
class MockTapDownInfo extends Mock implements TapDownInfo {}
class MockTapDownDetails extends Mock implements TapDownDetails {}
class MockTapUpInfo extends Mock implements TapUpInfo {} class MockTapUpInfo extends Mock implements TapUpInfo {}
class MockTapUpDetails extends Mock implements TapUpDetails {}
class MockEventPosition extends Mock implements EventPosition {} class MockEventPosition extends Mock implements EventPosition {}
class MockFilter extends Mock implements Filter {} class MockFilter extends Mock implements Filter {}

Loading…
Cancel
Save