diff --git a/.gitignore b/.gitignore index bd315f72..eeb2b0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,5 @@ app.*.map.json !.idea/codeStyles/ !.idea/dictionaries/ !.idea/runConfigurations/ + +.firebase diff --git a/README.md b/README.md index b51926b5..b91a1c98 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,33 @@ Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info } ``` +### Deploy application to Firebase hosting + +Follow the following steps to deploy the application. + +## Firebase CLI + +Install and authenticate with [Firebase CLI tools](https://firebase.google.com/docs/cli) + +## Build the project using the desired environment + +```bash +# Development +$ flutter build web --release --target lib/main_development.dart + +# Staging +$ flutter build web --release --target lib/main_staging.dart + +# Production +$ flutter build web --release --target lib/main_production.dart +``` + +## Deploy + +```bash +$ firebase deploy +``` + [coverage_badge]: coverage_badge.svg [flutter_localizations_link]: https://api.flutter.dev/flutter/flutter_localizations/flutter_localizations-library.html [internationalization_link]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization diff --git a/assets/images/components/ball.png b/assets/images/components/ball.png new file mode 100644 index 00000000..af80811b Binary files /dev/null and b/assets/images/components/ball.png differ diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..0d25a779 --- /dev/null +++ b/firebase.json @@ -0,0 +1,10 @@ +{ + "hosting": { + "public": "build/web", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + } +} diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index a9cd64ce..da1364d2 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -1,22 +1,34 @@ -import 'package:flame_forge2d/body_component.dart'; +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:forge2d/forge2d.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/theme/theme.dart'; -class Ball extends BodyComponent { +class Ball extends PositionBodyComponent { Ball({ required Vector2 position, - }) : _position = position; + }) : _position = position, + super(size: ballSize); + + static final ballSize = Vector2.all(2); final Vector2 _position; + static const spritePath = 'components/ball.png'; + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite(spritePath); + positionComponent = SpriteComponent(sprite: sprite, size: ballSize); + } + @override Body createBody() { paint = Paint() ..color = gameRef.read().state.theme.characterTheme.ballColor; - final shape = CircleShape()..radius = 2; + final shape = CircleShape()..radius = ballSize.x / 2; final fixtureDef = FixtureDef(shape)..density = 1; diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index 7bf58273..b784b8cb 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -3,6 +3,9 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/components/components.dart'; +/// {@template wall} +/// A continuos generic and [BodyType.static] barrier that divides a game area. +/// {@endtemplate} class Wall extends BodyComponent { Wall({ required this.start, @@ -29,6 +32,12 @@ class Wall extends BodyComponent { } } +/// {@template bottom_wall} +/// [Wall] located at the bottom of the board. +/// +/// Collisions with [BottomWall] are listened by +/// [BottomWallBallContactCallback]. +/// {@endtemplate} class BottomWall extends Wall { BottomWall(Forge2DGame game) : super( @@ -40,6 +49,9 @@ class BottomWall extends Wall { ); } +/// {@template bottom_wall_ball_contact_callback} +/// Listens when a [Ball] falls into a [BottomWall]. +/// {@endtemplate} class BottomWallBallContactCallback extends ContactCallback { @override void begin(Ball ball, BottomWall wall, Contact contact) { diff --git a/lib/game/game.dart b/lib/game/game.dart index 253dcc9f..ad02533d 100644 --- a/lib/game/game.dart +++ b/lib/game/game.dart @@ -1,4 +1,5 @@ export 'bloc/game_bloc.dart'; export 'components/components.dart'; +export 'game_assets.dart'; export 'pinball_game.dart'; export 'view/view.dart'; diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart new file mode 100644 index 00000000..964aeda1 --- /dev/null +++ b/lib/game/game_assets.dart @@ -0,0 +1,11 @@ +import 'package:pinball/game/game.dart'; + +/// Add methods to help loading and caching game assets. +extension PinballGameAssetsX on PinballGame { + /// Pre load the initial assets of the game. + Future preLoadAssets() async { + await Future.wait([ + images.load(Ball.spritePath), + ]); + } +} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 5b5d7885..7c701c09 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,10 +1,14 @@ +import 'dart:async'; + import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; class PinballGame extends Forge2DGame with FlameBloc { void spawnBall() { - add(Ball(position: ballStartingPosition)); + add( + Ball(position: ballStartingPosition), + ); } // TODO(erickzanardo): Change to the plumber position diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 28834907..02f5b34c 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -23,9 +23,26 @@ class PinballGamePage extends StatelessWidget { } } -class PinballGameView extends StatelessWidget { +class PinballGameView extends StatefulWidget { const PinballGameView({Key? key}) : super(key: key); + @override + State createState() => _PinballGameViewState(); +} + +class _PinballGameViewState extends State { + late PinballGame _game; + + @override + void initState() { + super.initState(); + + // TODO(erickzanardo): Revisit this when we start to have more assets + // this could expose a Stream (maybe even a cubit?) so we could show the + // the loading progress with some fancy widgets. + _game = PinballGame()..preLoadAssets(); + } + @override Widget build(BuildContext context) { return BlocListener( @@ -39,7 +56,7 @@ class PinballGameView extends StatelessWidget { ); } }, - child: GameWidget(game: PinballGame()), + child: GameWidget(game: _game), ); } } diff --git a/pubspec.lock b/pubspec.lock index 7bf08da4..861dae5b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -140,21 +140,21 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-releasecandidate.1" + version: "1.1.0-releasecandidate.2" flame_bloc: dependency: "direct main" description: name: flame_bloc url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-releasecandidate.1" + version: "1.2.0-releasecandidate.2" flame_forge2d: dependency: "direct main" description: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0-releasecandidate.1" + version: "0.9.0-releasecandidate.2" flame_test: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6c3bd98e..8738f2bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,9 +9,9 @@ environment: dependencies: bloc: ^8.0.2 equatable: ^2.0.3 - flame: ^1.1.0-releasecandidate.1 - flame_bloc: ^1.2.0-releasecandidate.1 - flame_forge2d: ^0.9.0-releasecandidate.1 + flame: ^1.1.0-releasecandidate.2 + flame_bloc: ^1.2.0-releasecandidate.2 + flame_forge2d: ^0.9.0-releasecandidate.2 flutter: sdk: flutter flutter_bloc: ^8.0.1 @@ -33,3 +33,6 @@ dev_dependencies: flutter: uses-material-design: true generate: true + + assets: + - assets/images/components/ diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index 14b77b73..c665dd52 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -99,7 +99,7 @@ void main() { final fixture = ball.body.fixtures[0]; expect(fixture.shape.shapeType, equals(ShapeType.circle)); - expect(fixture.shape.radius, equals(2)); + expect(fixture.shape.radius, equals(1)); }, ); });