diff --git a/assets/images/components/spaceship_railing_bg.png b/assets/images/components/spaceship_railing_bg.png new file mode 100644 index 00000000..2298f799 Binary files /dev/null and b/assets/images/components/spaceship_railing_bg.png differ diff --git a/assets/images/components/spaceship_railing_fg.png b/assets/images/components/spaceship_railing_fg.png new file mode 100644 index 00000000..e788fde0 Binary files /dev/null and b/assets/images/components/spaceship_railing_fg.png differ diff --git a/assets/images/components/spaceship_ramp.png b/assets/images/components/spaceship_ramp.png new file mode 100644 index 00000000..81498965 Binary files /dev/null and b/assets/images/components/spaceship_ramp.png differ diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index e19c607c..591f6cdb 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -2,7 +2,7 @@ export 'board.dart'; export 'bonus_word.dart'; export 'chrome_dino.dart'; export 'controlled_ball.dart'; -export 'flipper_controller.dart'; +export 'controlled_flipper.dart'; export 'flutter_forest.dart'; export 'jetpack_ramp.dart'; export 'kicker.dart'; diff --git a/lib/game/components/flipper_controller.dart b/lib/game/components/controlled_flipper.dart similarity index 100% rename from lib/game/components/flipper_controller.dart rename to lib/game/components/controlled_flipper.dart diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index aa5a9dbd..34b00dd4 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -2,45 +2,52 @@ import 'dart:math' as math; -import 'package:flame/extensions.dart'; +import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; -import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; /// A [Blueprint] which creates the [JetpackRamp]. class Jetpack extends Forge2DBlueprint { + /// {@macro spaceship} + Jetpack(); + + static const int ballPriorityInsideRamp = 4; + @override void build(_) { - final position = Vector2( - BoardDimensions.bounds.left + 40.5, - BoardDimensions.bounds.top - 31.5, - ); - addAllContactCallback([ RampOpeningBallContactCallback<_JetpackRampOpening>(), ]); final rightOpening = _JetpackRampOpening( + // TODO(ruimiguel): set Board priority when defined. + outsidePriority: 1, rotation: math.pi, ) - ..initialPosition = position + Vector2(12.9, -20) + ..initialPosition = Vector2(1.7, 19) ..layer = Layer.opening; final leftOpening = _JetpackRampOpening( outsideLayer: Layer.spaceship, + outsidePriority: Spaceship.ballPriorityWhenOnSpaceship, rotation: math.pi, ) - ..initialPosition = position + Vector2(-2.5, -20) + ..initialPosition = Vector2(-13.7, 19) ..layer = Layer.jetpack; - final jetpackRamp = JetpackRamp() - ..initialPosition = position + Vector2(5, -20.2) - ..layer = Layer.jetpack; + final jetpackRamp = JetpackRamp(); + + final jetpackRampWallFg = _JetpackRampForegroundRailing(); + + final baseRight = _JetpackBase()..initialPosition = Vector2(1.7, 20); addAll([ rightOpening, leftOpening, + baseRight, jetpackRamp, + jetpackRampWallFg, ]); } } @@ -49,42 +56,129 @@ class Jetpack extends Forge2DBlueprint { /// Represents the upper left blue ramp of the [Board]. /// {@endtemplate} class JetpackRamp extends BodyComponent with InitialPosition, Layered { - JetpackRamp() : super(priority: 2) { + JetpackRamp() : super(priority: Jetpack.ballPriorityInsideRamp - 1) { layer = Layer.jetpack; - paint = Paint() - ..color = const Color.fromARGB(255, 8, 218, 241) - ..style = PaintingStyle.stroke; } - /// Radius of the external arc. - static const _externalRadius = 18.0; - /// Width between walls of the ramp. static const width = 5.0; List _createFixtureDefs() { final fixturesDef = []; - final externalCurveShape = ArcShape( - center: initialPosition, - arcRadius: _externalRadius, - angle: math.pi, - rotation: math.pi, + final outerLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-30.95, 38), + Vector2(-32.5, 71.25), + Vector2(-14.2, 71.25), + ], + ); + + final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); + fixturesDef.add(outerLeftCurveFixtureDef); + + final outerRightCurveShape = BezierCurveShape( + controlPoints: [ + outerLeftCurveShape.vertices.last, + Vector2(4.7, 71.25), + Vector2(6.3, 40), + ], + ); + + final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); + fixturesDef.add(outerRightCurveFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + renderBody = false; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprites(); + } + + Future _loadSprites() async { + final spriteRamp = await gameRef.loadSprite( + Assets.images.components.spaceshipRamp.path, + ); + + final spriteRampComponent = SpriteComponent( + sprite: spriteRamp, + size: Vector2(38.1, 33.8), + anchor: Anchor.center, + position: Vector2(-12.2, -53.5), + ); + + final spriteRailingBg = await gameRef.loadSprite( + Assets.images.components.spaceshipRailingBg.path, + ); + final spriteRailingBgComponent = SpriteComponent( + sprite: spriteRailingBg, + size: Vector2(38.3, 35.1), + anchor: Anchor.center, + position: spriteRampComponent.position + Vector2(0, -1), ); - final externalFixtureDef = FixtureDef(externalCurveShape); - fixturesDef.add(externalFixtureDef); - final internalCurveShape = externalCurveShape.copyWith( - arcRadius: _externalRadius - width, + await addAll([ + spriteRailingBgComponent, + spriteRampComponent, + ]); + } +} + +class _JetpackRampForegroundRailing extends BodyComponent + with InitialPosition, Layered { + _JetpackRampForegroundRailing() + : super(priority: Jetpack.ballPriorityInsideRamp + 1) { + layer = Layer.jetpack; + } + + List _createFixtureDefs() { + final fixturesDef = []; + + final innerLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-24.5, 38), + Vector2(-26.3, 64), + Vector2(-13.8, 64.5), + ], ); - final internalFixtureDef = FixtureDef(internalCurveShape); - fixturesDef.add(internalFixtureDef); + + final innerLeftCurveFixtureDef = FixtureDef(innerLeftCurveShape); + fixturesDef.add(innerLeftCurveFixtureDef); + + final innerRightCurveShape = BezierCurveShape( + controlPoints: [ + innerLeftCurveShape.vertices.last, + Vector2(-1, 64.5), + Vector2(0.1, 39.5), + ], + ); + + final innerRightCurveFixtureDef = FixtureDef(innerRightCurveShape); + fixturesDef.add(innerRightCurveFixtureDef); return fixturesDef; } @override Body createBody() { + renderBody = false; + final bodyDef = BodyDef() ..userData = this ..position = initialPosition; @@ -94,6 +188,56 @@ class JetpackRamp extends BodyComponent with InitialPosition, Layered { return body; } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprites(); + } + + Future _loadSprites() async { + final spriteRailingFg = await gameRef.loadSprite( + Assets.images.components.spaceshipRailingFg.path, + ); + final spriteRailingFgComponent = SpriteComponent( + sprite: spriteRailingFg, + size: Vector2(26.1, 28.3), + anchor: Anchor.center, + position: Vector2(-12.2, -52.5), + ); + + await add(spriteRailingFgComponent); + } +} + +class _JetpackBase extends BodyComponent with InitialPosition, Layered { + _JetpackBase() { + layer = Layer.board; + } + + @override + Body createBody() { + renderBody = false; + + const baseWidth = 6; + final baseShape = BezierCurveShape( + controlPoints: [ + Vector2(initialPosition.x - baseWidth / 2, initialPosition.y), + Vector2(initialPosition.x - baseWidth / 2, initialPosition.y) + + Vector2(2, 2), + Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) + + Vector2(-2, 2), + Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) + ], + ); + final fixtureDef = FixtureDef(baseShape); + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } } /// {@template jetpack_ramp_opening} @@ -104,12 +248,15 @@ class _JetpackRampOpening extends RampOpening { /// {@macro jetpack_ramp_opening} _JetpackRampOpening({ Layer? outsideLayer, + int? outsidePriority, required double rotation, }) : _rotation = rotation, super( insideLayer: Layer.jetpack, outsideLayer: outsideLayer, orientation: RampOrientation.down, + insidePriority: Jetpack.ballPriorityInsideRamp, + outsidePriority: outsidePriority, ); final double _rotation; @@ -117,11 +264,14 @@ class _JetpackRampOpening extends RampOpening { static final Vector2 _size = Vector2(JetpackRamp.width / 4, .1); @override - Shape get shape => PolygonShape() - ..setAsBox( - _size.x, - _size.y, - initialPosition, - _rotation, - ); + Shape get shape { + renderBody = false; + return PolygonShape() + ..setAsBox( + _size.x, + _size.y, + initialPosition, + _rotation, + ); + } } diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart index c05f8aa2..5c071f4f 100644 --- a/lib/game/components/launcher_ramp.dart +++ b/lib/game/components/launcher_ramp.dart @@ -124,6 +124,7 @@ class _LauncherRampOpening extends RampOpening { super( insideLayer: Layer.launcher, orientation: RampOrientation.down, + insidePriority: 3, ); final double _rotation; diff --git a/lib/game/components/spaceship_exit_rail.dart b/lib/game/components/spaceship_exit_rail.dart index fed97e0e..e4be9b31 100644 --- a/lib/game/components/spaceship_exit_rail.dart +++ b/lib/game/components/spaceship_exit_rail.dart @@ -191,6 +191,7 @@ class SpaceshipExitRailEnd extends RampOpening { : super( insideLayer: Layer.spaceshipExitRail, orientation: RampOrientation.down, + insidePriority: 3, ) { renderBody = false; layer = Layer.spaceshipExitRail; diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 15c39f86..2710dd23 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -24,6 +24,9 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.boundary.bottom.keyName), images.load(components.Assets.images.boundary.outer.keyName), images.load(Assets.images.components.background.path), + images.load(Assets.images.components.spaceshipRamp.path), + images.load(Assets.images.components.spaceshipRailingBg.path), + images.load(Assets.images.components.spaceshipRailingFg.path), images.load(Assets.images.components.spaceshipDropTube.path), ]); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 38327df3..d5d97de0 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -93,7 +93,9 @@ class PinballGame extends Forge2DGame } Future _addPaths() async { - unawaited(addFromBlueprint(Jetpack())); + unawaited( + addFromBlueprint(Jetpack()), + ); unawaited(addFromBlueprint(Launcher())); } diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 2fe23925..7796df34 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -24,6 +24,18 @@ class $AssetsImagesComponentsGen { /// File path: assets/images/components/spaceship-drop-tube.png AssetGenImage get spaceshipDropTube => const AssetGenImage('assets/images/components/spaceship-drop-tube.png'); + + /// File path: assets/images/components/spaceship_railing_bg.png + AssetGenImage get spaceshipRailingBg => + const AssetGenImage('assets/images/components/spaceship_railing_bg.png'); + + /// File path: assets/images/components/spaceship_railing_fg.png + AssetGenImage get spaceshipRailingFg => + const AssetGenImage('assets/images/components/spaceship_railing_fg.png'); + + /// File path: assets/images/components/spaceship_ramp.png + AssetGenImage get spaceshipRamp => + const AssetGenImage('assets/images/components/spaceship_ramp.png'); } class Assets { diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 59f1037a..588c0e33 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -185,8 +185,9 @@ class SpaceshipHole extends RampOpening { : super( insideLayer: Layer.spaceship, outsideLayer: outsideLayer, - outsidePriority: outsidePriority, orientation: RampOrientation.up, + insidePriority: 4, + outsidePriority: outsidePriority, ) { renderBody = false; layer = Layer.spaceship; diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart b/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart index 0e5587ea..d31515de 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart @@ -1,21 +1,39 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:sandbox/common/common.dart'; -class BasicFlipperGame extends BasicGame { +import 'package:sandbox/stories/ball/basic.dart'; + +class BasicFlipperGame extends BasicBallGame with KeyboardEvents { + BasicFlipperGame() : super(color: Colors.blue); + static const info = ''' Basic example of how a Flipper works. '''; + static const _leftFlipperKeys = [ + LogicalKeyboardKey.arrowLeft, + LogicalKeyboardKey.keyA, + ]; + + static const _rightFlipperKeys = [ + LogicalKeyboardKey.arrowRight, + LogicalKeyboardKey.keyD, + ]; + + late Flipper leftFlipper; + late Flipper rightFlipper; + @override Future onLoad() async { await super.onLoad(); final center = screenToWorld(camera.viewport.canvasSize! / 2); - final leftFlipper = Flipper(side: BoardSide.left) + leftFlipper = Flipper(side: BoardSide.left) ..initialPosition = center - Vector2(Flipper.size.x, 0); - final rightFlipper = Flipper(side: BoardSide.right) + rightFlipper = Flipper(side: BoardSide.right) ..initialPosition = center + Vector2(Flipper.size.x, 0); await addAll([ @@ -23,4 +41,32 @@ class BasicFlipperGame extends BasicGame { rightFlipper, ]); } + + @override + KeyEventResult onKeyEvent( + RawKeyEvent event, + Set keysPressed, + ) { + final movedLeftFlipper = _leftFlipperKeys.contains(event.logicalKey); + if (movedLeftFlipper) { + if (event is RawKeyDownEvent) { + leftFlipper.moveUp(); + } else if (event is RawKeyUpEvent) { + leftFlipper.moveDown(); + } + } + + final movedRightFlipper = _rightFlipperKeys.contains(event.logicalKey); + if (movedRightFlipper) { + if (event is RawKeyDownEvent) { + rightFlipper.moveUp(); + } else if (event is RawKeyUpEvent) { + rightFlipper.moveDown(); + } + } + + return movedLeftFlipper || movedRightFlipper + ? KeyEventResult.handled + : KeyEventResult.ignored; + } } diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart b/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart index d6c5d3df..9b5802f8 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart @@ -3,10 +3,9 @@ import 'dart:async'; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/flipper/basic.dart'; -class FlipperTracingGame extends BasicGame { +class FlipperTracingGame extends BasicFlipperGame { static const info = ''' Basic example of how the Flipper body overlays the sprite. '''; @@ -14,17 +13,6 @@ class FlipperTracingGame extends BasicGame { @override Future onLoad() async { await super.onLoad(); - final center = screenToWorld(camera.viewport.canvasSize! / 2); - - final leftFlipper = Flipper(side: BoardSide.left) - ..initialPosition = center - Vector2(Flipper.size.x, 0); - final rightFlipper = Flipper(side: BoardSide.right) - ..initialPosition = center + Vector2(Flipper.size.x, 0); - - await addAll([ - leftFlipper, - rightFlipper, - ]); leftFlipper.trace(); rightFlipper.trace(); } @@ -37,7 +25,6 @@ extension on BodyComponent { body.joints.whereType().forEach( (joint) => joint.setLimits(0, 0), ); - body.setType(BodyType.static); unawaited( mounted.whenComplete(() { diff --git a/test/game/components/flipper_controller_test.dart b/test/game/components/controlled_flipper_test.dart similarity index 100% rename from test/game/components/flipper_controller_test.dart rename to test/game/components/controlled_flipper_test.dart