Merge branch 'main' into ci/included-concurrency-tags

pull/241/head
Alejandro Santiago 3 years ago committed by GitHub
commit 117e1e8f27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,18 @@
name: share_repository
on:
push:
paths:
- "packages/share_repository/**"
- ".github/workflows/share_repository.yaml"
pull_request:
paths:
- "packages/share_repository/**"
- ".github/workflows/share_repository.yaml"
jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
working_directory: packages/share_repository

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -13,7 +13,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart'; import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_audio/pinball_audio.dart';
class App extends StatelessWidget { class App extends StatelessWidget {
@ -36,7 +36,7 @@ class App extends StatelessWidget {
RepositoryProvider.value(value: _pinballAudio), RepositoryProvider.value(value: _pinballAudio),
], ],
child: BlocProvider( child: BlocProvider(
create: (context) => ThemeCubit(), create: (context) => CharacterThemeCubit(),
child: const MaterialApp( child: const MaterialApp(
title: 'I/O Pinball', title: 'I/O Pinball',
localizationsDelegates: [ localizationsDelegates: [

@ -12,6 +12,12 @@ enum GameBonus {
/// Bonus achieved when a ball enters Sparky's computer. /// Bonus achieved when a ball enters Sparky's computer.
sparkyTurboCharge, sparkyTurboCharge,
/// Bonus achieved when the ball goes in the dino mouth.
dinoChomp,
/// Bonus achieved when a ball enters the android spaceship.
androidSpaceship,
} }
/// {@template game_state} /// {@template game_state}

@ -4,7 +4,6 @@ export 'camera_controller.dart';
export 'controlled_ball.dart'; export 'controlled_ball.dart';
export 'controlled_flipper.dart'; export 'controlled_flipper.dart';
export 'controlled_plunger.dart'; export 'controlled_plunger.dart';
export 'controlled_sparky_computer.dart';
export 'flutter_forest.dart'; export 'flutter_forest.dart';
export 'game_flow_controller.dart'; export 'game_flow_controller.dart';
export 'google_word.dart'; export 'google_word.dart';

@ -15,8 +15,8 @@ class ControlledBall extends Ball with Controls<BallController> {
/// When a launched [Ball] is lost, it will decrease the [GameState.balls] /// When a launched [Ball] is lost, it will decrease the [GameState.balls]
/// count, and a new [Ball] is spawned. /// count, and a new [Ball] is spawned.
ControlledBall.launch({ ControlledBall.launch({
required PinballTheme theme, required CharacterTheme characterTheme,
}) : super(baseColor: theme.characterTheme.ballColor) { }) : super(baseColor: characterTheme.ballColor) {
controller = BallController(this); controller = BallController(this);
priority = RenderPriority.ballOnLaunchRamp; priority = RenderPriority.ballOnLaunchRamp;
layer = Layer.launcher; layer = Layer.launcher;
@ -28,8 +28,8 @@ class ControlledBall extends Ball with Controls<BallController> {
/// When a bonus [Ball] is lost, the [GameState.balls] doesn't change. /// When a bonus [Ball] is lost, the [GameState.balls] doesn't change.
/// {@endtemplate} /// {@endtemplate}
ControlledBall.bonus({ ControlledBall.bonus({
required PinballTheme theme, required CharacterTheme characterTheme,
}) : super(baseColor: theme.characterTheme.ballColor) { }) : super(baseColor: characterTheme.ballColor) {
controller = BallController(this); controller = BallController(this);
priority = RenderPriority.ballOnBoard; priority = RenderPriority.ballOnBoard;
} }
@ -62,10 +62,13 @@ class BallController extends ComponentController<Ball>
Future<void> turboCharge() async { Future<void> turboCharge() async {
gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated()); gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated());
// TODO(allisonryan0002): adjust delay to match animation duration once
// given animations.
component.stop(); component.stop();
await Future<void>.delayed(const Duration(seconds: 1)); // TODO(alestiago): Refactor this hard coded duration once the following is
// merged:
// https://github.com/flame-engine/flame/pull/1564
await Future<void>.delayed(
const Duration(milliseconds: 2583),
);
component.resume(); component.resume();
await component.boost(Vector2(40, 110)); await component.boost(Vector2(40, 110));
} }

@ -1,52 +0,0 @@
// ignore_for_file: avoid_renaming_method_parameters
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_flame/pinball_flame.dart';
/// {@template controlled_sparky_computer}
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<Forge2DGame> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() : super() {
controller = SparkyComputerController(this);
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
}
}
/// {@template sparky_computer_controller}
/// Controller attached to a [SparkyComputer] that handles its game related
/// logic.
/// {@endtemplate}
// TODO(allisonryan0002): listen for turbo charge game bonus and animate Sparky.
class SparkyComputerController
extends ComponentController<ControlledSparkyComputer> {
/// {@macro sparky_computer_controller}
SparkyComputerController(ControlledSparkyComputer controlledComputer)
: super(controlledComputer);
}
/// {@template sparky_computer_sensor_ball_contact_callback}
/// Turbo charges the [Ball] when it enters the [SparkyComputer]
/// {@endtemplate}
@visibleForTesting
class SparkyComputerSensorBallContactCallback
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
/// {@macro sparky_computer_sensor_ball_contact_callback}
SparkyComputerSensorBallContactCallback();
@override
void begin(_, ControlledBall ball, __) {
ball.controller.turboCharge();
}
}

@ -71,7 +71,7 @@ class _FlutterForestController extends ComponentController<FlutterForest>
Future<void> _addBonusBall() async { Future<void> _addBonusBall() async {
await gameRef.add( await gameRef.add(
ControlledBall.bonus(theme: gameRef.theme) ControlledBall.bonus(characterTheme: gameRef.characterTheme)
..initialPosition = Vector2(17.2, -52.7), ..initialPosition = Vector2(17.2, -52.7),
); );
} }

@ -32,8 +32,7 @@ class GameFlowController extends ComponentController<PinballGame>
// next page // next page
component.firstChild<Backboard>()?.gameOverMode( component.firstChild<Backboard>()?.gameOverMode(
score: state?.score ?? 0, score: state?.score ?? 0,
characterIconPath: characterIconPath: component.characterTheme.leaderboardIcon.keyName,
component.theme.characterTheme.leaderboardIcon.keyName,
); );
component.firstChild<CameraController>()?.focusOnBackboard(); component.firstChild<CameraController>()?.focusOnBackboard();
} }

@ -1,10 +1,10 @@
// ignore_for_file: avoid_renaming_method_parameters // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.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 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_fire_zone} /// {@template sparky_fire_zone}
/// Area positioned at the top left of the [Board] where the [Ball] /// Area positioned at the top left of the [Board] where the [Ball]
@ -12,29 +12,21 @@ import 'package:pinball_components/pinball_components.dart';
/// ///
/// When a [Ball] hits [SparkyBumper]s, the bumper animates. /// When a [Ball] hits [SparkyBumper]s, the bumper animates.
/// {@endtemplate} /// {@endtemplate}
class SparkyFireZone extends Component with HasGameRef<PinballGame> { class SparkyFireZone extends Blueprint {
/// {@macro sparky_fire_zone} /// {@macro sparky_fire_zone}
SparkyFireZone(); SparkyFireZone()
: super(
@override components: [
Future<void> onLoad() async { _SparkyBumper.a()..initialPosition = Vector2(-22.9, -41.65),
await super.onLoad(); _SparkyBumper.b()..initialPosition = Vector2(-21.25, -57.9),
_SparkyBumper.c()..initialPosition = Vector2(-3.3, -52.55),
gameRef.addContactCallback(SparkyBumperBallContactCallback()); SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8),
SparkyAnimatronic()..position = Vector2(-13.8, -58.2),
final lowerLeftBumper = _SparkyBumper.a() ],
..initialPosition = Vector2(-22.9, -41.65); blueprints: [
final upperLeftBumper = _SparkyBumper.b() SparkyComputer(),
..initialPosition = Vector2(-21.25, -57.9); ],
final rightBumper = _SparkyBumper.c() );
..initialPosition = Vector2(-3.3, -52.55);
await addAll([
lowerLeftBumper,
upperLeftBumper,
rightBumper,
]);
}
} }
// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D // TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
@ -48,6 +40,14 @@ class _SparkyBumper extends SparkyBumper with ScorePoints {
@override @override
int get points => 20; int get points => 20;
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
gameRef.addContactCallback(SparkyBumperBallContactCallback());
}
} }
/// Listens when a [Ball] bounces bounces against a [SparkyBumper]. /// Listens when a [Ball] bounces bounces against a [SparkyBumper].
@ -63,3 +63,48 @@ class SparkyBumperBallContactCallback
sparkyBumper.animate(); sparkyBumper.animate();
} }
} }
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].
/// {@endtemplate}
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
class SparkyComputerSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_computer_sensor}
SparkyComputerSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape, isSensor: true);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
}
}
@visibleForTesting
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
// ignore: public_member_api_docs
class SparkyComputerSensorBallContactCallback
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
@override
void begin(_, ControlledBall controlledBall, __) {
controlledBall.controller.turboCharge();
controlledBall.gameRef.firstChild<SparkyAnimatronic>()?.playing = true;
}
}

@ -43,8 +43,11 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.dash.bumper.b.inactive.keyName), images.load(components.Assets.images.dash.bumper.b.inactive.keyName),
images.load(components.Assets.images.dash.bumper.main.active.keyName), images.load(components.Assets.images.dash.bumper.main.active.keyName),
images.load(components.Assets.images.dash.bumper.main.inactive.keyName), images.load(components.Assets.images.dash.bumper.main.inactive.keyName),
images.load(components.Assets.images.plunger.plunger.keyName),
images.load(components.Assets.images.plunger.rocket.keyName),
images.load(components.Assets.images.boundary.bottom.keyName), images.load(components.Assets.images.boundary.bottom.keyName),
images.load(components.Assets.images.boundary.outer.keyName), images.load(components.Assets.images.boundary.outer.keyName),
images.load(components.Assets.images.boundary.outerBottom.keyName),
images.load(components.Assets.images.spaceship.saucer.keyName), images.load(components.Assets.images.spaceship.saucer.keyName),
images.load(components.Assets.images.spaceship.bridge.keyName), images.load(components.Assets.images.spaceship.bridge.keyName),
images.load(components.Assets.images.spaceship.ramp.boardOpening.keyName), images.load(components.Assets.images.spaceship.ramp.boardOpening.keyName),
@ -80,12 +83,11 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.alienBumper.b.inactive.keyName), images.load(components.Assets.images.alienBumper.b.inactive.keyName),
images.load(components.Assets.images.chromeDino.mouth.keyName), images.load(components.Assets.images.chromeDino.mouth.keyName),
images.load(components.Assets.images.chromeDino.head.keyName), images.load(components.Assets.images.chromeDino.head.keyName),
images.load(components.Assets.images.plunger.plunger.keyName),
images.load(components.Assets.images.plunger.rocket.keyName),
images.load(components.Assets.images.sparky.computer.base.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.bumper.a.active.keyName), images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.animatronic.keyName),
images.load(components.Assets.images.sparky.bumper.a.inactive.keyName), images.load(components.Assets.images.sparky.bumper.a.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.a.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.active.keyName), images.load(components.Assets.images.sparky.bumper.b.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.inactive.keyName), images.load(components.Assets.images.sparky.bumper.b.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.c.active.keyName), images.load(components.Assets.images.sparky.bumper.c.active.keyName),

@ -2,6 +2,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
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';
@ -19,7 +20,7 @@ class PinballGame extends Forge2DGame
HasKeyboardHandlerComponents, HasKeyboardHandlerComponents,
Controls<_GameBallsController> { Controls<_GameBallsController> {
PinballGame({ PinballGame({
required this.theme, required this.characterTheme,
required this.audio, required this.audio,
}) { }) {
images.prefix = ''; images.prefix = '';
@ -32,7 +33,7 @@ class PinballGame extends Forge2DGame
@override @override
Color backgroundColor() => Colors.transparent; Color backgroundColor() => Colors.transparent;
final PinballTheme theme; final CharacterTheme characterTheme;
final PinballAudio audio; final PinballAudio audio;
@ -49,13 +50,13 @@ class PinballGame extends Forge2DGame
// TODO(allisonryan0002): banish Wall and Board classes in later PR. // TODO(allisonryan0002): banish Wall and Board classes in later PR.
await add(BottomWall()); await add(BottomWall());
unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(ControlledSparkyComputer())); unawaited(addFromBlueprint(LaunchRamp()));
final launcher = Launcher(); final launcher = Launcher();
unawaited(addFromBlueprint(launcher)); unawaited(addFromBlueprint(launcher));
unawaited(add(Board())); unawaited(add(Board()));
unawaited(add(AlienZone())); unawaited(add(AlienZone()));
unawaited(add(SparkyFireZone())); await addFromBlueprint(SparkyFireZone());
unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls())); unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord()); unawaited(_addBonusWord());
@ -118,7 +119,7 @@ class _GameBallsController extends ComponentController<PinballGame>
void _spawnBall() { void _spawnBall() {
final ball = ControlledBall.launch( final ball = ControlledBall.launch(
theme: gameRef.theme, characterTheme: gameRef.characterTheme,
)..initialPosition = Vector2( )..initialPosition = Vector2(
_plunger.body.position.x, _plunger.body.position.x,
_plunger.body.position.y - Ball.size.y, _plunger.body.position.y - Ball.size.y,
@ -134,12 +135,12 @@ class _GameBallsController extends ComponentController<PinballGame>
} }
} }
class DebugPinballGame extends PinballGame with TapDetector { class DebugPinballGame extends PinballGame with FPSCounter, TapDetector {
DebugPinballGame({ DebugPinballGame({
required PinballTheme theme, required CharacterTheme characterTheme,
required PinballAudio audio, required PinballAudio audio,
}) : super( }) : super(
theme: theme, characterTheme: characterTheme,
audio: audio, audio: audio,
) { ) {
controller = _DebugGameBallsController(this); controller = _DebugGameBallsController(this);
@ -149,6 +150,7 @@ class DebugPinballGame extends PinballGame with TapDetector {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await _loadBackground(); await _loadBackground();
await add(_DebugInformation());
} }
// TODO(alestiago): Move to PinballGame once we have the real background // TODO(alestiago): Move to PinballGame once we have the real background
@ -191,3 +193,35 @@ class _DebugGameBallsController extends _GameBallsController {
return noBallsLeft && canBallRespawn; return noBallsLeft && canBallRespawn;
} }
} }
class _DebugInformation extends Component with HasGameRef<DebugPinballGame> {
_DebugInformation() : super(priority: RenderPriority.debugInfo);
@override
PositionType get positionType => PositionType.widget;
final _debugTextPaint = TextPaint(
style: const TextStyle(
color: Colors.green,
fontSize: 10,
),
);
final _debugBackgroundPaint = Paint()..color = Colors.white;
@override
void render(Canvas canvas) {
final debugText = [
'FPS: ${gameRef.fps().toStringAsFixed(1)}',
'BALLS: ${gameRef.descendants().whereType<ControlledBall>().length}',
].join(' | ');
final height = _debugTextPaint.measureTextHeight(debugText);
final position = Vector2(0, gameRef.camera.canvasSize.y - height);
canvas.drawRect(
position & Vector2(gameRef.camera.canvasSize.x, height),
_debugBackgroundPaint,
);
_debugTextPaint.render(canvas, debugText, position);
}
}

@ -5,8 +5,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball/start_game/start_game.dart'; import 'package:pinball/start_game/start_game.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_audio/pinball_audio.dart';
class PinballGamePage extends StatelessWidget { class PinballGamePage extends StatelessWidget {
@ -31,17 +31,19 @@ class PinballGamePage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.read<ThemeCubit>().state.theme; final characterTheme =
context.read<CharacterThemeCubit>().state.characterTheme;
final audio = context.read<PinballAudio>(); final audio = context.read<PinballAudio>();
final pinballAudio = context.read<PinballAudio>(); final pinballAudio = context.read<PinballAudio>();
final game = isDebugMode final game = isDebugMode
? DebugPinballGame(theme: theme, audio: audio) ? DebugPinballGame(characterTheme: characterTheme, audio: audio)
: PinballGame(theme: theme, audio: audio); : PinballGame(characterTheme: characterTheme, audio: audio);
final loadables = [ final loadables = [
...game.preLoadAssets(), ...game.preLoadAssets(),
pinballAudio.load(), pinballAudio.load(),
...BonusAnimation.loadAssets(),
]; ];
return MultiBlocProvider( return MultiBlocProvider(
@ -112,6 +114,13 @@ class PinballGameLoadedView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isPlaying = context.select(
(StartGameBloc bloc) => bloc.state.status == StartGameStatus.play,
);
final gameWidgetWidth = MediaQuery.of(context).size.height * 9 / 16;
final screenWidth = MediaQuery.of(context).size.width;
final leftMargin = (screenWidth / 2) - (gameWidgetWidth / 1.8);
return Stack( return Stack(
children: [ children: [
Positioned.fill( Positioned.fill(
@ -130,10 +139,13 @@ class PinballGameLoadedView extends StatelessWidget {
}, },
), ),
), ),
const Positioned( Positioned(
top: 8, top: 16,
left: 8, left: leftMargin,
child: GameHud(), child: Visibility(
visible: isPlaying,
child: const GameHud(),
),
), ),
], ],
); );

@ -1,19 +1,23 @@
// ignore_for_file: public_member_api_docs
import 'package:flame/flame.dart'; import 'package:flame/flame.dart';
import 'package:flame/sprite.dart'; import 'package:flame/sprite.dart';
import 'package:flame/widgets.dart';
import 'package:flutter/material.dart' hide Image; import 'package:flutter/material.dart' hide Image;
import 'package:pinball/gen/assets.gen.dart'; import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_flame/pinball_flame.dart';
class BonusAnimation extends StatelessWidget { /// {@template bonus_animation}
/// [Widget] that displays bonus animations.
/// {@endtemplate}
class BonusAnimation extends StatefulWidget {
/// {@macro bonus_animation}
const BonusAnimation._( const BonusAnimation._(
this.imagePath, { String imagePath, {
VoidCallback? onCompleted, VoidCallback? onCompleted,
Key? key, Key? key,
}) : _onCompleted = onCompleted, }) : _imagePath = imagePath,
_onCompleted = onCompleted,
super(key: key); super(key: key);
/// [Widget] that displays the dash nest animation.
BonusAnimation.dashNest({ BonusAnimation.dashNest({
Key? key, Key? key,
VoidCallback? onCompleted, VoidCallback? onCompleted,
@ -23,6 +27,7 @@ class BonusAnimation extends StatelessWidget {
key: key, key: key,
); );
/// [Widget] that displays the sparky turbo charge animation.
BonusAnimation.sparkyTurboCharge({ BonusAnimation.sparkyTurboCharge({
Key? key, Key? key,
VoidCallback? onCompleted, VoidCallback? onCompleted,
@ -32,56 +37,94 @@ class BonusAnimation extends StatelessWidget {
key: key, key: key,
); );
BonusAnimation.dino({ /// [Widget] that displays the dino chomp animation.
BonusAnimation.dinoChomp({
Key? key, Key? key,
VoidCallback? onCompleted, VoidCallback? onCompleted,
}) : this._( }) : this._(
Assets.images.bonusAnimation.dino.keyName, Assets.images.bonusAnimation.dinoChomp.keyName,
onCompleted: onCompleted, onCompleted: onCompleted,
key: key, key: key,
); );
BonusAnimation.android({ /// [Widget] that displays the android spaceship animation.
BonusAnimation.androidSpaceship({
Key? key, Key? key,
VoidCallback? onCompleted, VoidCallback? onCompleted,
}) : this._( }) : this._(
Assets.images.bonusAnimation.android.keyName, Assets.images.bonusAnimation.androidSpaceship.keyName,
onCompleted: onCompleted, onCompleted: onCompleted,
key: key, key: key,
); );
BonusAnimation.google({ /// [Widget] that displays the google word animation.
BonusAnimation.googleWord({
Key? key, Key? key,
VoidCallback? onCompleted, VoidCallback? onCompleted,
}) : this._( }) : this._(
Assets.images.bonusAnimation.google.keyName, Assets.images.bonusAnimation.googleWord.keyName,
onCompleted: onCompleted, onCompleted: onCompleted,
key: key, key: key,
); );
final String imagePath; final String _imagePath;
final VoidCallback? _onCompleted; final VoidCallback? _onCompleted;
static Future<void> loadAssets() { /// Returns a list of assets to be loaded for animations.
static List<Future> loadAssets() {
Flame.images.prefix = ''; Flame.images.prefix = '';
return Flame.images.loadAll([ return [
Assets.images.bonusAnimation.dashNest.keyName, Flame.images.load(Assets.images.bonusAnimation.dashNest.keyName),
Assets.images.bonusAnimation.sparkyTurboCharge.keyName, Flame.images.load(Assets.images.bonusAnimation.sparkyTurboCharge.keyName),
Assets.images.bonusAnimation.dino.keyName, Flame.images.load(Assets.images.bonusAnimation.dinoChomp.keyName),
Assets.images.bonusAnimation.android.keyName, Flame.images.load(Assets.images.bonusAnimation.androidSpaceship.keyName),
Assets.images.bonusAnimation.google.keyName, Flame.images.load(Assets.images.bonusAnimation.googleWord.keyName),
]); ];
}
@override
State<BonusAnimation> createState() => _BonusAnimationState();
}
class _BonusAnimationState extends State<BonusAnimation>
with TickerProviderStateMixin {
late SpriteAnimationController controller;
late SpriteAnimation animation;
bool shouldRunBuildCallback = true;
@override
void dispose() {
controller.dispose();
super.dispose();
}
// When the animation is overwritten by another animation, we need to stop
// the callback in the build method as it will break the new animation.
// Otherwise we need to set up a new callback when a new animation starts to
// show the score view at the end of the animation.
@override
void didUpdateWidget(BonusAnimation oldWidget) {
shouldRunBuildCallback = oldWidget._imagePath == widget._imagePath;
Future<void>.delayed(
Duration(seconds: animation.totalDuration().ceil()),
() {
widget._onCompleted?.call();
},
);
super.didUpdateWidget(oldWidget);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final spriteSheet = SpriteSheet.fromColumnsAndRows( final spriteSheet = SpriteSheet.fromColumnsAndRows(
image: Flame.images.fromCache(imagePath), image: Flame.images.fromCache(widget._imagePath),
columns: 8, columns: 8,
rows: 9, rows: 9,
); );
final animation = spriteSheet.createAnimation( animation = spriteSheet.createAnimation(
row: 0, row: 0,
stepTime: 1 / 24, stepTime: 1 / 24,
to: spriteSheet.rows * spriteSheet.columns, to: spriteSheet.rows * spriteSheet.columns,
@ -91,15 +134,22 @@ class BonusAnimation extends StatelessWidget {
Future<void>.delayed( Future<void>.delayed(
Duration(seconds: animation.totalDuration().ceil()), Duration(seconds: animation.totalDuration().ceil()),
() { () {
_onCompleted?.call(); if (shouldRunBuildCallback) {
widget._onCompleted?.call();
}
}, },
); );
controller = SpriteAnimationController(
animation: animation,
vsync: this,
)..forward();
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: SpriteAnimationWidget( child: SpriteAnimationWidget(
animation: animation, controller: controller,
), ),
); );
} }

@ -1,46 +1,122 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball/gen/gen.dart';
import 'package:pinball/theme/app_colors.dart';
/// {@template game_hud} /// {@template game_hud}
/// Overlay of a [PinballGame] that displays the current [GameState.score] and /// Overlay on the [PinballGame].
/// [GameState.balls]. ///
/// Displays the current [GameState.score], [GameState.balls] and animates when
/// the player gets a [GameBonus].
/// {@endtemplate} /// {@endtemplate}
class GameHud extends StatelessWidget { class GameHud extends StatefulWidget {
/// {@macro game_hud} /// {@macro game_hud}
const GameHud({Key? key}) : super(key: key); const GameHud({Key? key}) : super(key: key);
@override
State<GameHud> createState() => _GameHudState();
}
class _GameHudState extends State<GameHud> {
bool showAnimation = false;
/// Ratio from sprite frame (width 500, height 144) w / h = ratio
static const _ratio = 3.47;
static const _width = 265.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<GameBloc>().state; final isGameOver = context.select((GameBloc bloc) => bloc.state.isGameOver);
return Container( return _ScoreViewDecoration(
color: Colors.redAccent, child: SizedBox(
width: 200, height: _width / _ratio,
height: 100, width: _width,
padding: const EdgeInsets.all(16), child: BlocListener<GameBloc, GameState>(
child: Row( listenWhen: (previous, current) =>
mainAxisAlignment: MainAxisAlignment.spaceBetween, previous.bonusHistory.length != current.bonusHistory.length,
children: [ listener: (_, __) => setState(() => showAnimation = true),
Text( child: AnimatedSwitcher(
'${state.score}', duration: kThemeAnimationDuration,
style: Theme.of(context).textTheme.headline3, child: showAnimation && !isGameOver
? _AnimationView(
onComplete: () {
if (mounted) {
setState(() => showAnimation = false);
}
},
)
: const ScoreView(),
), ),
Wrap( ),
direction: Axis.vertical, ),
children: [ );
for (var i = 0; i < state.balls; i++) }
const Padding( }
padding: EdgeInsets.only(top: 6, right: 6),
child: CircleAvatar( class _ScoreViewDecoration extends StatelessWidget {
radius: 8, const _ScoreViewDecoration({
backgroundColor: Colors.black, Key? key,
), required this.child,
), }) : super(key: key);
],
final Widget child;
@override
Widget build(BuildContext context) {
const radius = BorderRadius.all(Radius.circular(12));
const boardWidth = 5.0;
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: radius,
border: Border.all(
color: AppColors.white,
width: boardWidth,
),
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage(
Assets.images.score.miniScoreBackground.path,
), ),
], ),
), ),
child: Padding(
padding: const EdgeInsets.all(boardWidth - 1),
child: ClipRRect(
borderRadius: radius,
child: child,
),
),
);
}
}
class _AnimationView extends StatelessWidget {
const _AnimationView({
Key? key,
required this.onComplete,
}) : super(key: key);
final VoidCallback onComplete;
@override
Widget build(BuildContext context) {
final lastBonus = context.select(
(GameBloc bloc) => bloc.state.bonusHistory.last,
); );
switch (lastBonus) {
case GameBonus.dashNest:
return BonusAnimation.dashNest(onCompleted: onComplete);
case GameBonus.sparkyTurboCharge:
return BonusAnimation.sparkyTurboCharge(onCompleted: onComplete);
case GameBonus.dinoChomp:
return BonusAnimation.dinoChomp(onCompleted: onComplete);
case GameBonus.googleWord:
return BonusAnimation.googleWord(onCompleted: onComplete);
case GameBonus.androidSpaceship:
return BonusAnimation.androidSpaceship(onCompleted: onComplete);
}
} }
} }

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball/game/pinball_game.dart'; import 'package:pinball/game/pinball_game.dart';
import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart'; import 'package:pinball/select_character/select_character.dart';
/// {@template play_button_overlay} /// {@template play_button_overlay}
/// [Widget] that renders the button responsible to starting the game /// [Widget] that renders the button responsible to starting the game

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart';
/// {@template round_count_display}
/// Colored square indicating if a round is available.
/// {@endtemplate}
class RoundCountDisplay extends StatelessWidget {
/// {@macro round_count_display}
const RoundCountDisplay({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
// TODO(arturplaczek): refactor when GameState handle balls and rounds and
// select state.rounds property instead of state.ball
final balls = context.select((GameBloc bloc) => bloc.state.balls);
return Row(
children: [
Text(
l10n.rounds,
style: AppTextStyle.subtitle1.copyWith(
color: AppColors.orange,
),
),
const SizedBox(width: 8),
Row(
children: [
RoundIndicator(isActive: balls >= 1),
RoundIndicator(isActive: balls >= 2),
RoundIndicator(isActive: balls >= 3),
],
),
],
);
}
}
/// {@template round_indicator}
/// [Widget] that displays the round indicator.
/// {@endtemplate}
@visibleForTesting
class RoundIndicator extends StatelessWidget {
/// {@macro round_indicator}
const RoundIndicator({
Key? key,
required this.isActive,
}) : super(key: key);
/// A value that describes whether the indicator is active.
final bool isActive;
@override
Widget build(BuildContext context) {
final color = isActive ? AppColors.orange : AppColors.orange.withAlpha(128);
const size = 8.0;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(
color: color,
height: size,
width: size,
),
);
}
}

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template score_view}
/// [Widget] that displays the score.
/// {@endtemplate}
class ScoreView extends StatelessWidget {
/// {@macro score_view}
const ScoreView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final isGameOver = context.select((GameBloc bloc) => bloc.state.isGameOver);
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: AnimatedSwitcher(
duration: kThemeAnimationDuration,
child: isGameOver ? const _GameOver() : const _ScoreDisplay(),
),
);
}
}
class _GameOver extends StatelessWidget {
const _GameOver({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Text(
l10n.gameOver,
style: AppTextStyle.headline1.copyWith(
color: AppColors.white,
),
);
}
}
class _ScoreDisplay extends StatelessWidget {
const _ScoreDisplay({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
l10n.score.toLowerCase(),
style: AppTextStyle.subtitle1.copyWith(
color: AppColors.orange,
),
),
const _ScoreText(),
const RoundCountDisplay(),
],
);
}
}
class _ScoreText extends StatelessWidget {
const _ScoreText({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final score = context.select((GameBloc bloc) => bloc.state.score);
return Text(
score.formatScore(),
style: AppTextStyle.headline1.copyWith(
color: AppColors.white,
),
);
}
}

@ -1,3 +1,5 @@
export 'bonus_animation.dart'; export 'bonus_animation.dart';
export 'game_hud.dart'; export 'game_hud.dart';
export 'play_button_overlay.dart'; export 'play_button_overlay.dart';
export 'round_count_display.dart';
export 'score_view.dart';

@ -14,26 +14,27 @@ class $AssetsImagesGen {
const $AssetsImagesBonusAnimationGen(); const $AssetsImagesBonusAnimationGen();
$AssetsImagesComponentsGen get components => $AssetsImagesComponentsGen get components =>
const $AssetsImagesComponentsGen(); const $AssetsImagesComponentsGen();
$AssetsImagesScoreGen get score => const $AssetsImagesScoreGen();
} }
class $AssetsImagesBonusAnimationGen { class $AssetsImagesBonusAnimationGen {
const $AssetsImagesBonusAnimationGen(); const $AssetsImagesBonusAnimationGen();
/// File path: assets/images/bonus_animation/android.png /// File path: assets/images/bonus_animation/android_spaceship.png
AssetGenImage get android => AssetGenImage get androidSpaceship => const AssetGenImage(
const AssetGenImage('assets/images/bonus_animation/android.png'); 'assets/images/bonus_animation/android_spaceship.png');
/// File path: assets/images/bonus_animation/dash_nest.png /// File path: assets/images/bonus_animation/dash_nest.png
AssetGenImage get dashNest => AssetGenImage get dashNest =>
const AssetGenImage('assets/images/bonus_animation/dash_nest.png'); const AssetGenImage('assets/images/bonus_animation/dash_nest.png');
/// File path: assets/images/bonus_animation/dino.png /// File path: assets/images/bonus_animation/dino_chomp.png
AssetGenImage get dino => AssetGenImage get dinoChomp =>
const AssetGenImage('assets/images/bonus_animation/dino.png'); const AssetGenImage('assets/images/bonus_animation/dino_chomp.png');
/// File path: assets/images/bonus_animation/google.png /// File path: assets/images/bonus_animation/google_word.png
AssetGenImage get google => AssetGenImage get googleWord =>
const AssetGenImage('assets/images/bonus_animation/google.png'); const AssetGenImage('assets/images/bonus_animation/google_word.png');
/// File path: assets/images/bonus_animation/sparky_turbo_charge.png /// File path: assets/images/bonus_animation/sparky_turbo_charge.png
AssetGenImage get sparkyTurboCharge => const AssetGenImage( AssetGenImage get sparkyTurboCharge => const AssetGenImage(
@ -48,6 +49,14 @@ class $AssetsImagesComponentsGen {
const AssetGenImage('assets/images/components/background.png'); const AssetGenImage('assets/images/components/background.png');
} }
class $AssetsImagesScoreGen {
const $AssetsImagesScoreGen();
/// File path: assets/images/score/mini_score_background.png
AssetGenImage get miniScoreBackground =>
const AssetGenImage('assets/images/score/mini_score_background.png');
}
class Assets { class Assets {
Assets._(); Assets._();

@ -75,5 +75,9 @@
"enterInitials": "Enter your initials", "enterInitials": "Enter your initials",
"@enterInitials": { "@enterInitials": {
"description": "Text displayed on the ending dialog when game finishes to ask the user for his initials" "description": "Text displayed on the ending dialog when game finishes to ask the user for his initials"
},
"rounds": "Ball Ct:",
"@rounds": {
"description": "Text displayed on the scoreboard widget to indicate rounds left"
} }
} }

@ -36,7 +36,7 @@ extension LeaderboardEntryDataX on LeaderboardEntryData {
rank: position.toString(), rank: position.toString(),
playerInitials: playerInitials, playerInitials: playerInitials,
score: score, score: score,
character: character.toTheme.character, character: character.toTheme.leaderboardIcon,
); );
} }
} }

@ -5,7 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/leaderboard/leaderboard.dart'; import 'package:pinball/leaderboard/leaderboard.dart';
import 'package:pinball/theme/theme.dart'; import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_theme/pinball_theme.dart'; import 'package:pinball_theme/pinball_theme.dart';
class LeaderboardPage extends StatelessWidget { class LeaderboardPage extends StatelessWidget {

@ -5,12 +5,12 @@ import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:pinball_theme/pinball_theme.dart'; import 'package:pinball_theme/pinball_theme.dart';
part 'theme_state.dart'; part 'character_theme_state.dart';
class ThemeCubit extends Cubit<ThemeState> { class CharacterThemeCubit extends Cubit<CharacterThemeState> {
ThemeCubit() : super(const ThemeState.initial()); CharacterThemeCubit() : super(const CharacterThemeState.initial());
void characterSelected(CharacterTheme characterTheme) { void characterSelected(CharacterTheme characterTheme) {
emit(ThemeState(PinballTheme(characterTheme: characterTheme))); emit(CharacterThemeState(characterTheme));
} }
} }

@ -0,0 +1,15 @@
// ignore_for_file: public_member_api_docs
// TODO(allisonryan0002): Document this section when the API is stable.
part of 'character_theme_cubit.dart';
class CharacterThemeState extends Equatable {
const CharacterThemeState(this.characterTheme);
const CharacterThemeState.initial() : characterTheme = const DashTheme();
final CharacterTheme characterTheme;
@override
List<Object> get props => [characterTheme];
}

@ -0,0 +1,2 @@
export 'cubit/character_theme_cubit.dart';
export 'view/view.dart';

@ -3,8 +3,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball/start_game/start_game.dart'; import 'package:pinball/start_game/start_game.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_theme/pinball_theme.dart'; import 'package:pinball_theme/pinball_theme.dart';
class CharacterSelectionDialog extends StatelessWidget { class CharacterSelectionDialog extends StatelessWidget {
@ -19,7 +19,7 @@ class CharacterSelectionDialog extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => ThemeCubit(), create: (_) => CharacterThemeCubit(),
child: const CharacterSelectionView(), child: const CharacterSelectionView(),
); );
} }
@ -109,12 +109,14 @@ class CharacterImageButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final currentCharacterTheme = context.select<ThemeCubit, CharacterTheme>( final currentCharacterTheme =
(cubit) => cubit.state.theme.characterTheme, context.select<CharacterThemeCubit, CharacterTheme>(
(cubit) => cubit.state.characterTheme,
); );
return GestureDetector( return GestureDetector(
onTap: () => context.read<ThemeCubit>().characterSelected(characterTheme), onTap: () =>
context.read<CharacterThemeCubit>().characterSelected(characterTheme),
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: (currentCharacterTheme == characterTheme) color: (currentCharacterTheme == characterTheme)
@ -124,7 +126,7 @@ class CharacterImageButton extends StatelessWidget {
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: characterTheme.character.image(), child: characterTheme.icon.image(),
), ),
), ),
); );

@ -5,7 +5,7 @@ import 'package:pinball/theme/theme.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
const _fontPackage = 'pinball_components'; const _fontPackage = 'pinball_components';
const _primaryFontFamily = PinballFonts.pixeloidSans; const _primaryFontFamily = FontFamily.pixeloidSans;
abstract class AppTextStyle { abstract class AppTextStyle {
static const headline1 = TextStyle( static const headline1 = TextStyle(

@ -1,16 +0,0 @@
// ignore_for_file: public_member_api_docs
// TODO(allisonryan0002): Document this section when the API is stable.
part of 'theme_cubit.dart';
class ThemeState extends Equatable {
const ThemeState(this.theme);
const ThemeState.initial()
: theme = const PinballTheme(characterTheme: DashTheme());
final PinballTheme theme;
@override
List<Object> get props => [theme];
}

@ -1,4 +1,2 @@
export 'app_colors.dart'; export 'app_colors.dart';
export 'app_text_style.dart'; export 'app_text_style.dart';
export 'cubit/theme_cubit.dart';
export 'view/view.dart';

@ -72,6 +72,20 @@ class FetchPlayerRankingException extends LeaderboardException {
); );
} }
/// {@template fetch_prohibited_initials_exception}
/// Exception thrown when failure occurs while fetching prohibited initials.
/// {@endtemplate}
class FetchProhibitedInitialsException extends LeaderboardException {
/// {@macro fetch_prohibited_initials_exception}
const FetchProhibitedInitialsException(
Object error,
StackTrace stackTrace,
) : super(
error,
stackTrace,
);
}
/// {@template leaderboard_repository} /// {@template leaderboard_repository}
/// Repository to access leaderboard data in Firebase Cloud Firestore. /// Repository to access leaderboard data in Firebase Cloud Firestore.
/// {@endtemplate} /// {@endtemplate}
@ -152,4 +166,25 @@ class LeaderboardRepository {
throw FetchPlayerRankingException(error, stackTrace); throw FetchPlayerRankingException(error, stackTrace);
} }
} }
/// Determines if the given [initials] are allowed.
Future<bool> areInitialsAllowed({required String initials}) async {
// Initials can only be three uppercase A-Z letters
final initialsRegex = RegExp(r'^[A-Z]{3}$');
if (!initialsRegex.hasMatch(initials)) {
return false;
}
try {
final document = await _firebaseFirestore
.collection('prohibitedInitials')
.doc('list')
.get();
final prohibitedInitials =
document.get('prohibitedInitials') as List<String>;
return !prohibitedInitials.contains(initials);
} on Exception catch (error, stackTrace) {
throw FetchProhibitedInitialsException(error, stackTrace);
}
}
} }

@ -21,6 +21,9 @@ class MockQueryDocumentSnapshot extends Mock
class MockDocumentReference extends Mock class MockDocumentReference extends Mock
implements DocumentReference<Map<String, dynamic>> {} implements DocumentReference<Map<String, dynamic>> {}
class MockDocumentSnapshot extends Mock
implements DocumentSnapshot<Map<String, dynamic>> {}
void main() { void main() {
group('LeaderboardRepository', () { group('LeaderboardRepository', () {
late FirebaseFirestore firestore; late FirebaseFirestore firestore;
@ -223,5 +226,94 @@ void main() {
); );
}); });
}); });
group('areInitialsAllowed', () {
late LeaderboardRepository leaderboardRepository;
late CollectionReference<Map<String, dynamic>> collectionReference;
late DocumentReference<Map<String, dynamic>> documentReference;
late DocumentSnapshot<Map<String, dynamic>> documentSnapshot;
setUp(() async {
collectionReference = MockCollectionReference();
documentReference = MockDocumentReference();
documentSnapshot = MockDocumentSnapshot();
leaderboardRepository = LeaderboardRepository(firestore);
when(() => firestore.collection('prohibitedInitials'))
.thenReturn(collectionReference);
when(() => collectionReference.doc('list'))
.thenReturn(documentReference);
when(() => documentReference.get())
.thenAnswer((_) async => documentSnapshot);
when<dynamic>(() => documentSnapshot.get('prohibitedInitials'))
.thenReturn(['BAD']);
});
test('returns true if initials are three letters and allowed', () async {
final isUsernameAllowedResponse =
await leaderboardRepository.areInitialsAllowed(
initials: 'ABC',
);
expect(
isUsernameAllowedResponse,
isTrue,
);
});
test(
'returns false if initials are shorter than 3 characters',
() async {
final areInitialsAllowedResponse =
await leaderboardRepository.areInitialsAllowed(initials: 'AB');
expect(areInitialsAllowedResponse, isFalse);
},
);
test(
'returns false if initials are longer than 3 characters',
() async {
final areInitialsAllowedResponse =
await leaderboardRepository.areInitialsAllowed(initials: 'ABCD');
expect(areInitialsAllowedResponse, isFalse);
},
);
test(
'returns false if initials contain a lowercase letter',
() async {
final areInitialsAllowedResponse =
await leaderboardRepository.areInitialsAllowed(initials: 'AbC');
expect(areInitialsAllowedResponse, isFalse);
},
);
test(
'returns false if initials contain a special character',
() async {
final areInitialsAllowedResponse =
await leaderboardRepository.areInitialsAllowed(initials: 'A@C');
expect(areInitialsAllowedResponse, isFalse);
},
);
test('returns false if initials are forbidden', () async {
final areInitialsAllowedResponse =
await leaderboardRepository.areInitialsAllowed(initials: 'BAD');
expect(areInitialsAllowedResponse, isFalse);
});
test(
'throws FetchProhibitedInitialsException when Exception occurs '
'when trying to retrieve information from firestore',
() async {
when(() => firestore.collection('prohibitedInitials'))
.thenThrow(Exception('oops'));
expect(
() => leaderboardRepository.areInitialsAllowed(initials: 'ABC'),
throwsA(isA<FetchProhibitedInitialsException>()),
);
},
);
});
}); });
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

@ -86,6 +86,10 @@ class $AssetsImagesBoundaryGen {
AssetGenImage get bottom => AssetGenImage get bottom =>
const AssetGenImage('assets/images/boundary/bottom.png'); const AssetGenImage('assets/images/boundary/bottom.png');
/// File path: assets/images/boundary/outer-bottom.png
AssetGenImage get outerBottom =>
const AssetGenImage('assets/images/boundary/outer-bottom.png');
/// File path: assets/images/boundary/outer.png /// File path: assets/images/boundary/outer.png
AssetGenImage get outer => AssetGenImage get outer =>
const AssetGenImage('assets/images/boundary/outer.png'); const AssetGenImage('assets/images/boundary/outer.png');
@ -257,6 +261,8 @@ class $AssetsImagesSpaceshipGen {
class $AssetsImagesSparkyGen { class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen(); const $AssetsImagesSparkyGen();
AssetGenImage get animatronic =>
const AssetGenImage('assets/images/sparky/animatronic.png');
$AssetsImagesSparkyBumperGen get bumper => $AssetsImagesSparkyBumperGen get bumper =>
const $AssetsImagesSparkyBumperGen(); const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyComputerGen get computer => $AssetsImagesSparkyComputerGen get computer =>

@ -1,2 +1,3 @@
export 'assets.gen.dart'; export 'assets.gen.dart';
export 'fonts.gen.dart';
export 'pinball_fonts.dart'; export 'pinball_fonts.dart';

@ -13,6 +13,7 @@ class Boundaries extends Blueprint {
components: [ components: [
_BottomBoundary(), _BottomBoundary(),
_OuterBoundary(), _OuterBoundary(),
_OuterBottomBoundarySpriteComponent(),
], ],
); );
} }
@ -91,8 +92,10 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
/// {@macro outer_boundary} /// {@macro outer_boundary}
_OuterBoundary() _OuterBoundary()
: super( : super(
priority: RenderPriority.outerBoudary, priority: RenderPriority.outerBoundary,
children: [_OuterBoundarySpriteComponent()], children: [
_OuterBoundarySpriteComponent(),
],
) { ) {
renderBody = false; renderBody = false;
} }
@ -157,3 +160,25 @@ class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
} }
} }
class _OuterBottomBoundarySpriteComponent extends SpriteComponent
with HasGameRef {
_OuterBottomBoundarySpriteComponent()
: super(
priority: RenderPriority.outerBottomBoundary,
anchor: Anchor.center,
position: Vector2(0, 71),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.boundary.outerBottom.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -29,5 +29,6 @@ export 'slingshot.dart';
export 'spaceship.dart'; export 'spaceship.dart';
export 'spaceship_rail.dart'; export 'spaceship_rail.dart';
export 'spaceship_ramp.dart'; export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart';
export 'sparky_bumper.dart'; export 'sparky_bumper.dart';
export 'sparky_computer.dart'; export 'sparky_computer.dart';

@ -38,17 +38,9 @@ class DashAnimatronic extends SpriteAnimationComponent with HasGameRef {
textureSize: textureSize, textureSize: textureSize,
loop: false, loop: false,
), ),
); )..onComplete = () {
} animation?.reset();
@override
void update(double dt) {
super.update(dt);
if (animation != null) {
if (animation!.isLastFrame) {
animation!.reset();
playing = false; playing = false;
} };
}
} }
} }

@ -34,24 +34,21 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
} }
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()
..set( ..set(
Vector2(28.65, -35.1), Vector2(28.65, -35.1),
Vector2(29.5, -35.1), Vector2(29.5, -35.1),
); );
final topStraightFixtureDef = FixtureDef(topStraightShape); final topStraightFixtureDef = FixtureDef(topStraightShape);
fixturesDef.add(topStraightFixtureDef);
final topCurveShape = BezierCurveShape( final topCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
topStraightShape.vertex1, topStraightShape.vertex1,
Vector2(17.4, -26.38), Vector2(18.8, -27),
Vector2(25.5, -20.7), Vector2(26.6, -21),
], ],
); );
fixturesDef.add(FixtureDef(topCurveShape)); final topCurveFixtureDef = FixtureDef(topCurveShape);
final middleCurveShape = BezierCurveShape( final middleCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
@ -60,16 +57,16 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
Vector2(26.8, -19.5), Vector2(26.8, -19.5),
], ],
); );
fixturesDef.add(FixtureDef(middleCurveShape)); final middleCurveFixtureDef = FixtureDef(middleCurveShape);
final bottomCurveShape = BezierCurveShape( final bottomCurveShape = BezierCurveShape(
controlPoints: [ controlPoints: [
middleCurveShape.vertices.last, middleCurveShape.vertices.last,
Vector2(21.5, -15.8), Vector2(23, -15),
Vector2(25.8, -14.8), Vector2(27, -15),
], ],
); );
fixturesDef.add(FixtureDef(bottomCurveShape)); final bottomCurveFixtureDef = FixtureDef(bottomCurveShape);
final bottomStraightShape = EdgeShape() final bottomStraightShape = EdgeShape()
..set( ..set(
@ -77,9 +74,14 @@ class _DinoTopWall extends BodyComponent with InitialPosition {
Vector2(31, -14.5), Vector2(31, -14.5),
); );
final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); final bottomStraightFixtureDef = FixtureDef(bottomStraightShape);
fixturesDef.add(bottomStraightFixtureDef);
return fixturesDef; return [
topStraightFixtureDef,
topCurveFixtureDef,
middleCurveFixtureDef,
bottomCurveFixtureDef,
bottomStraightFixtureDef,
];
} }
@override @override
@ -106,12 +108,14 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef {
@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.dino.dinoLandTop.keyName, gameRef.images.fromCache(
Assets.images.dino.dinoLandTop.keyName,
),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
position = Vector2(22, -41.8); position = Vector2(22.8, -38.9);
} }
} }
@ -129,69 +133,56 @@ class _DinoBottomWall extends BodyComponent with InitialPosition {
} }
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
const restitution = 1.0; const restitution = 1.0;
final topStraightControlPoints = [
Vector2(32.4, -8.8),
Vector2(25, -7.7),
];
final topStraightShape = EdgeShape() final topStraightShape = EdgeShape()
..set( ..set(
topStraightControlPoints.first, Vector2(32.4, -8.8),
topStraightControlPoints.last, Vector2(25, -7.7),
); );
final topStraightFixtureDef = FixtureDef( final topStraightFixtureDef = FixtureDef(
topStraightShape, topStraightShape,
restitution: restitution, restitution: restitution,
); );
fixturesDef.add(topStraightFixtureDef);
final topLeftCurveControlPoints = [
topStraightControlPoints.last,
Vector2(21.8, -7),
Vector2(29.5, 13.8),
];
final topLeftCurveShape = BezierCurveShape( final topLeftCurveShape = BezierCurveShape(
controlPoints: topLeftCurveControlPoints, controlPoints: [
topStraightShape.vertex2,
Vector2(21.8, -7),
Vector2(29.8, 13.8),
],
); );
final topLeftCurveFixtureDef = FixtureDef( final topLeftCurveFixtureDef = FixtureDef(
topLeftCurveShape, topLeftCurveShape,
restitution: restitution, restitution: restitution,
); );
fixturesDef.add(topLeftCurveFixtureDef);
final bottomLeftStraightControlPoints = [
topLeftCurveControlPoints.last,
Vector2(31.8, 44.1),
];
final bottomLeftStraightShape = EdgeShape() final bottomLeftStraightShape = EdgeShape()
..set( ..set(
bottomLeftStraightControlPoints.first, topLeftCurveShape.vertices.last,
bottomLeftStraightControlPoints.last, Vector2(31.9, 44.1),
); );
final bottomLeftStraightFixtureDef = FixtureDef( final bottomLeftStraightFixtureDef = FixtureDef(
bottomLeftStraightShape, bottomLeftStraightShape,
restitution: restitution, restitution: restitution,
); );
fixturesDef.add(bottomLeftStraightFixtureDef);
final bottomStraightControlPoints = [
bottomLeftStraightControlPoints.last,
Vector2(37.8, 44.1),
];
final bottomStraightShape = EdgeShape() final bottomStraightShape = EdgeShape()
..set( ..set(
bottomStraightControlPoints.first, bottomLeftStraightShape.vertex2,
bottomStraightControlPoints.last, Vector2(37.8, 44.1),
); );
final bottomStraightFixtureDef = FixtureDef( final bottomStraightFixtureDef = FixtureDef(
bottomStraightShape, bottomStraightShape,
restitution: restitution, restitution: restitution,
); );
fixturesDef.add(bottomStraightFixtureDef);
return fixturesDef; return [
topStraightFixtureDef,
topLeftCurveFixtureDef,
bottomLeftStraightFixtureDef,
bottomStraightFixtureDef,
];
} }
@override @override
@ -212,8 +203,10 @@ class _DinoBottomWallSpriteComponent extends SpriteComponent with HasGameRef {
@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.dino.dinoLandBottom.keyName, gameRef.images.fromCache(
Assets.images.dino.dinoLandBottom.keyName,
),
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;

@ -78,6 +78,7 @@ class _GoogleLetterSprite extends SpriteComponent with HasGameRef {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
// TODO(alestiago): Used cached assets.
final sprite = await gameRef.loadSprite(_path); final sprite = await gameRef.loadSprite(_path);
this.sprite = sprite; this.sprite = sprite;
// TODO(alestiago): Size correctly once the assets are provided. // TODO(alestiago): Size correctly once the assets are provided.

@ -136,6 +136,7 @@ class _KickerSpriteComponent extends SpriteComponent with HasGameRef {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
// TODO(alestiago): Used cached asset.
final sprite = await gameRef.loadSprite( final sprite = await gameRef.loadSprite(
(_side.isLeft) (_side.isLeft)
? Assets.images.kicker.left.keyName ? Assets.images.kicker.left.keyName

@ -148,6 +148,7 @@ class _PlungerSpriteAnimationGroupComponent
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
// TODO(alestiago): Used cached images.
final spriteSheet = await gameRef.images.load( final spriteSheet = await gameRef.images.load(
Assets.images.plunger.plunger.keyName, Assets.images.plunger.plunger.keyName,
); );

@ -39,7 +39,9 @@ abstract class RenderPriority {
static const int bottomBoundary = _above + dinoBottomWall; static const int bottomBoundary = _above + dinoBottomWall;
static const int outerBoudary = _above + background; static const int outerBoundary = _above + background;
static const int outerBottomBoundary = _above + rocket;
// Bottom Group // Bottom Group
@ -47,7 +49,7 @@ abstract class RenderPriority {
// Launcher // Launcher
static const int launchRamp = _above + outerBoudary; static const int launchRamp = _above + outerBoundary;
static const int launchRampForegroundRailing = _below + ballOnBoard; static const int launchRampForegroundRailing = _below + ballOnBoard;
@ -113,4 +115,7 @@ abstract class RenderPriority {
// Score Text // Score Text
static const int scoreText = _above + spaceshipRampForegroundRailing; static const int scoreText = _above + spaceshipRampForegroundRailing;
// Debug information
static const int debugInfo = _above + scoreText;
} }

@ -0,0 +1,46 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_animatronic}
/// Animated Sparky that sits on top of the [SparkyComputer].
/// {@endtemplate}
class SparkyAnimatronic extends SpriteAnimationComponent with HasGameRef {
/// {@macro sparky_animatronic}
SparkyAnimatronic()
: super(
anchor: Anchor.center,
playing: false,
priority: RenderPriority.sparkyAnimatronic,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = gameRef.images.fromCache(
Assets.images.sparky.animatronic.keyName,
);
const amountPerRow = 9;
const amountPerColumn = 7;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: (amountPerRow * amountPerColumn) - 1,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
loop: false,
),
)..onComplete = () {
animation?.reset();
playing = false;
};
}
}

@ -7,9 +7,6 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer} /// {@template sparky_computer}
/// A computer owned by Sparky. /// A computer owned by Sparky.
///
/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when
/// something enters the [SparkyComputer].
/// {@endtemplate} /// {@endtemplate}
class SparkyComputer extends Blueprint { class SparkyComputer extends Blueprint {
/// {@macro sparky_computer} /// {@macro sparky_computer}
@ -18,7 +15,6 @@ class SparkyComputer extends Blueprint {
components: [ components: [
_ComputerBase(), _ComputerBase(),
_ComputerTopSpriteComponent(), _ComputerTopSpriteComponent(),
SparkyComputerSensor(),
], ],
); );
} }
@ -104,24 +100,3 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
} }
} }
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].
/// {@endtemplate}
class SparkyComputerSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_computer_sensor}
SparkyComputerSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape, isSensor: true);
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}

@ -61,6 +61,7 @@ flutter:
- assets/images/slingshot/ - assets/images/slingshot/
- assets/images/alien_bumper/a/ - assets/images/alien_bumper/a/
- assets/images/alien_bumper/b/ - assets/images/alien_bumper/b/
- assets/images/sparky/
- assets/images/sparky/computer/ - assets/images/sparky/computer/
- assets/images/sparky/bumper/a/ - assets/images/sparky/bumper/a/
- assets/images/sparky/bumper/b/ - assets/images/sparky/bumper/b/

@ -0,0 +1,36 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
const _path =
'https://github.com/VGVentures/pinball/tree/main/packages/pinball_components/sandbox/lib/stories/';
extension StoryAddGame on Story {
void addGame({
required String title,
required String description,
required Game Function(DashbookContext) gameBuilder,
}) {
final _chapter = Chapter(
title,
(DashbookContext context) {
final game = gameBuilder(context);
if (game is Traceable) {
game.trace = context.boolProperty('Trace', true);
}
return GameWidget(game: game);
},
this,
codeLink: '$_path${name.toPath()}/${title.toPath()}',
info: description,
);
chapters.add(_chapter);
}
}
extension on String {
String toPath() {
return replaceAll(' ', '_')..toLowerCase();
}
}

@ -1,3 +1,3 @@
export 'add_game.dart';
export 'games.dart'; export 'games.dart';
export 'methods.dart';
export 'trace.dart'; export 'trace.dart';

@ -5,16 +5,25 @@ 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';
abstract class BasicGame extends Forge2DGame { abstract class AssetsGame extends Forge2DGame {
BasicGame() { AssetsGame({
List<String>? imagesFileNames,
}) : _imagesFileNames = imagesFileNames {
images.prefix = ''; images.prefix = '';
} }
}
abstract class BasicKeyboardGame extends BasicGame final List<String>? _imagesFileNames;
with HasKeyboardHandlerComponents {}
@override
Future<void> onLoad() async {
await super.onLoad();
if (_imagesFileNames != null) {
await images.loadAll(_imagesFileNames!);
}
}
}
abstract class LineGame extends BasicGame with PanDetector { abstract class LineGame extends AssetsGame with PanDetector {
Vector2? _lineEnd; Vector2? _lineEnd;
@override @override

@ -1,3 +0,0 @@
String buildSourceLink(String path) {
return 'https://github.com/VGVentures/pinball/tree/main/packages/pinball_components/sandbox/lib/stories/$path';
}

@ -35,7 +35,7 @@ mixin Traceable on Forge2DGame {
.forEach((bodyComponent) => bodyComponent.trace()); .forEach((bodyComponent) => bodyComponent.trace());
descendants() descendants()
.whereType<SpriteComponent>() .whereType<HasPaint>()
.forEach((sprite) => sprite.setOpacity(0.5)); .forEach((sprite) => sprite.setOpacity(0.5));
} }
} }

@ -16,9 +16,6 @@ void main() {
addLayerStories(dashbook); addLayerStories(dashbook);
addEffectsStories(dashbook); addEffectsStories(dashbook);
addFlipperStories(dashbook); addFlipperStories(dashbook);
addSpaceshipStories(dashbook);
addSpaceshipRampStories(dashbook);
addSpaceshipRailStories(dashbook);
addBaseboardStories(dashbook); addBaseboardStories(dashbook);
addChromeDinoStories(dashbook); addChromeDinoStories(dashbook);
addDashNestBumperStories(dashbook); addDashNestBumperStories(dashbook);
@ -27,12 +24,12 @@ void main() {
addSlingshotStories(dashbook); addSlingshotStories(dashbook);
addSparkyBumperStories(dashbook); addSparkyBumperStories(dashbook);
addAlienZoneStories(dashbook); addAlienZoneStories(dashbook);
addZoomStories(dashbook);
addBoundariesStories(dashbook); addBoundariesStories(dashbook);
addGoogleWordStories(dashbook); addGoogleWordStories(dashbook);
addLaunchRampStories(dashbook); addLaunchRampStories(dashbook);
addScoreTextStories(dashbook); addScoreTextStories(dashbook);
addBackboardStories(dashbook); addBackboardStories(dashbook);
addDinoWallStories(dashbook);
runApp(dashbook); runApp(dashbook);
} }

@ -4,10 +4,17 @@ import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class AlienBumperAGame extends BasicBallGame { class AlienBumperAGame extends BallGame {
AlienBumperAGame() : super(color: const Color(0xFF0000FF)); AlienBumperAGame()
: super(
static const info = ''' color: const Color(0xFF0000FF),
imagesFileNames: [
Assets.images.alienBumper.a.active.keyName,
Assets.images.alienBumper.a.inactive.keyName,
],
);
static const description = '''
Shows how a AlienBumperA is rendered. Shows how a AlienBumperA is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -17,16 +24,10 @@ class AlienBumperAGame extends BasicBallGame {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([ camera.followVector2(Vector2.zero());
Assets.images.alienBumper.a.active.keyName, await add(
Assets.images.alienBumper.a.inactive.keyName, AlienBumper.a()..priority = 1,
]); );
final center = screenToWorld(camera.viewport.canvasSize! / 2);
final alienBumperA = AlienBumper.a()
..initialPosition = Vector2(center.x - 20, center.y - 20)
..priority = 1;
await add(alienBumperA);
await traceAllBodies(); await traceAllBodies();
} }

@ -4,10 +4,17 @@ import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class AlienBumperBGame extends BasicBallGame { class AlienBumperBGame extends BallGame {
AlienBumperBGame() : super(color: const Color(0xFF0000FF)); AlienBumperBGame()
: super(
static const info = ''' color: const Color(0xFF0000FF),
imagesFileNames: [
Assets.images.alienBumper.b.active.keyName,
Assets.images.alienBumper.b.inactive.keyName,
],
);
static const description = '''
Shows how a AlienBumperB is rendered. Shows how a AlienBumperB is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -17,16 +24,10 @@ class AlienBumperBGame extends BasicBallGame {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([ camera.followVector2(Vector2.zero());
Assets.images.alienBumper.b.active.keyName, await add(
Assets.images.alienBumper.b.inactive.keyName, AlienBumper.b()..priority = 1,
]); );
final center = screenToWorld(camera.viewport.canvasSize! / 2);
final alienBumperB = AlienBumper.b()
..initialPosition = Vector2(center.x - 10, center.y + 10)
..priority = 1;
await add(alienBumperB);
await traceAllBodies(); await traceAllBodies();
} }

@ -6,8 +6,8 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BasicSpaceshipGame extends BasicGame with TapDetector { class SpaceshipGame extends AssetsGame with TapDetector {
static const info = ''' static const description = '''
Shows how a Spaceship works. Shows how a Spaceship works.
- Tap anywhere on the screen to spawn a Ball into the game. - Tap anywhere on the screen to spawn a Ball into the game.
@ -18,10 +18,10 @@ class BasicSpaceshipGame extends BasicGame with TapDetector {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await addFromBlueprint(
unawaited( Spaceship(position: Vector2.zero()),
addFromBlueprint(Spaceship(position: Vector2.zero())),
); );
await ready();
} }
@override @override

@ -6,7 +6,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRailGame extends BasicBallGame { class SpaceshipRailGame extends BallGame {
SpaceshipRailGame() SpaceshipRailGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
@ -14,7 +14,7 @@ class SpaceshipRailGame extends BasicBallGame {
ballLayer: Layer.spaceshipExitRail, ballLayer: Layer.spaceshipExitRail,
); );
static const info = ''' static const description = '''
Shows how SpaceshipRail are rendered. Shows how SpaceshipRail are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -26,10 +26,8 @@ class SpaceshipRailGame extends BasicBallGame {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2(-30, -10)); camera.followVector2(Vector2(-30, -10));
await addFromBlueprint(SpaceshipRail());
final spaceshipRail = SpaceshipRail(); await ready();
unawaited(addFromBlueprint(spaceshipRail));
await traceAllBodies(); await traceAllBodies();
} }
} }

@ -7,15 +7,27 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRampGame extends BasicBallGame with KeyboardEvents { class SpaceshipRampGame extends BallGame with KeyboardEvents {
SpaceshipRampGame() SpaceshipRampGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
ballPriority: RenderPriority.ballOnSpaceshipRamp, ballPriority: RenderPriority.ballOnSpaceshipRamp,
ballLayer: Layer.spaceshipEntranceRamp, ballLayer: Layer.spaceshipEntranceRamp,
imagesFileNames: [
Assets.images.spaceship.ramp.railingBackground.keyName,
Assets.images.spaceship.ramp.main.keyName,
Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.spaceship.ramp.arrow.inactive.keyName,
Assets.images.spaceship.ramp.arrow.active1.keyName,
Assets.images.spaceship.ramp.arrow.active2.keyName,
Assets.images.spaceship.ramp.arrow.active3.keyName,
Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName,
],
); );
static const info = ''' static const description = '''
Shows how SpaceshipRamp is rendered. Shows how SpaceshipRamp is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -29,22 +41,10 @@ class SpaceshipRampGame extends BasicBallGame with KeyboardEvents {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.spaceship.ramp.railingBackground.keyName,
Assets.images.spaceship.ramp.main.keyName,
Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.spaceship.ramp.arrow.inactive.keyName,
Assets.images.spaceship.ramp.arrow.active1.keyName,
Assets.images.spaceship.ramp.arrow.active2.keyName,
Assets.images.spaceship.ramp.arrow.active3.keyName,
Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName,
]);
_spaceshipRamp = SpaceshipRamp();
await addFromBlueprint(_spaceshipRamp);
camera.followVector2(Vector2(-12, -50)); camera.followVector2(Vector2(-12, -50));
await addFromBlueprint(
_spaceshipRamp = SpaceshipRamp(),
);
await traceAllBodies(); await traceAllBodies();
} }

@ -1,25 +1,36 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/alien_zone/alien_bumper_a_game.dart'; import 'package:sandbox/stories/alien_zone/alien_bumper_a_game.dart';
import 'package:sandbox/stories/alien_zone/alien_bumper_b_game.dart'; import 'package:sandbox/stories/alien_zone/alien_bumper_b_game.dart';
import 'package:sandbox/stories/alien_zone/spaceship_game.dart';
import 'package:sandbox/stories/alien_zone/spaceship_rail_game.dart';
import 'package:sandbox/stories/alien_zone/spaceship_ramp_game.dart';
void addAlienZoneStories(Dashbook dashbook) { void addAlienZoneStories(Dashbook dashbook) {
dashbook.storiesOf('Alien Zone') dashbook.storiesOf('Alien Zone')
..add( ..addGame(
'Alien Bumper A', title: 'Alien Bumper A',
(context) => GameWidget( description: AlienBumperAGame.description,
game: AlienBumperAGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => AlienBumperAGame(),
),
codeLink: buildSourceLink('alien_zone/alien_bumper_a.dart'),
info: AlienBumperAGame.info,
) )
..add( ..addGame(
'Alien Bumper B', title: 'Alien Bumper B',
(context) => GameWidget( description: AlienBumperBGame.description,
game: AlienBumperBGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => AlienBumperBGame(),
), )
codeLink: buildSourceLink('alien_zone/alien_bumper_b.dart'), ..addGame(
info: AlienBumperAGame.info, title: 'Spaceship',
description: SpaceshipGame.description,
gameBuilder: (_) => SpaceshipGame(),
)
..addGame(
title: 'Spaceship Rail',
description: SpaceshipRailGame.description,
gameBuilder: (_) => SpaceshipRailGame(),
)
..addGame(
title: 'Spaceship Ramp',
description: SpaceshipRampGame.description,
gameBuilder: (_) => SpaceshipRampGame(),
); );
} }

@ -1,21 +1,22 @@
import 'package:flame/components.dart'; import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets; import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart'; import 'package:pinball_theme/pinball_theme.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BackboardGameOverGame extends BasicKeyboardGame { class BackboardGameOverGame extends AssetsGame
BackboardGameOverGame(this.score, this.character); with HasKeyboardHandlerComponents {
BackboardGameOverGame(this.score, this.character)
: super(
imagesFileNames: characterIconPaths.values.toList(),
);
static const info = ''' static const description = '''
Simple example showing the game over mode of the backboard. Shows how the Backboard in game over mode is rendered.
- Select a character to update the character icon. - Select a character to update the character icon.
'''; ''';
final int score;
final String character;
static final characterIconPaths = <String, String>{ static final characterIconPaths = <String, String>{
'Dash': Assets.images.dash.leaderboardIcon.keyName, 'Dash': Assets.images.dash.leaderboardIcon.keyName,
'Sparky': Assets.images.sparky.leaderboardIcon.keyName, 'Sparky': Assets.images.sparky.leaderboardIcon.keyName,
@ -23,14 +24,16 @@ class BackboardGameOverGame extends BasicKeyboardGame {
'Dino': Assets.images.dino.leaderboardIcon.keyName, 'Dino': Assets.images.dino.leaderboardIcon.keyName,
}; };
final int score;
final String character;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
camera camera
..followVector2(Vector2.zero()) ..followVector2(Vector2.zero())
..zoom = 5; ..zoom = 5;
await images.loadAll(characterIconPaths.values.toList());
await add( await add(
Backboard.gameOver( Backboard.gameOver(
position: Vector2(0, 20), position: Vector2(0, 20),

@ -2,9 +2,14 @@ import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BackboardWaitingGame extends BasicGame { class BackboardWaitingGame extends AssetsGame {
static const info = ''' BackboardWaitingGame()
Simple example showing the waiting mode of the backboard. : super(
imagesFileNames: [],
);
static const description = '''
Shows how the Backboard in waiting mode is rendered.
'''; ''';
@override @override
@ -13,7 +18,8 @@ class BackboardWaitingGame extends BasicGame {
..followVector2(Vector2.zero()) ..followVector2(Vector2.zero())
..zoom = 5; ..zoom = 5;
final backboard = Backboard.waiting(position: Vector2(0, 20)); await add(
await add(backboard); Backboard.waiting(position: Vector2(0, 20)),
);
} }
} }

@ -1,32 +1,25 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/backboard/backboard_game_over_game.dart'; import 'package:sandbox/stories/backboard/backboard_game_over_game.dart';
import 'package:sandbox/stories/backboard/backboard_waiting_game.dart'; import 'package:sandbox/stories/backboard/backboard_waiting_game.dart';
void addBackboardStories(Dashbook dashbook) { void addBackboardStories(Dashbook dashbook) {
dashbook.storiesOf('Backboard') dashbook.storiesOf('Backboard')
..add( ..addGame(
'Waiting mode', title: 'Waiting',
(context) => GameWidget( description: BackboardWaitingGame.description,
game: BackboardWaitingGame(), gameBuilder: (_) => BackboardWaitingGame(),
),
codeLink: buildSourceLink('backboard/waiting.dart'),
info: BackboardWaitingGame.info,
) )
..add( ..addGame(
'Game over', title: 'Game over',
(context) => GameWidget( description: BackboardGameOverGame.description,
game: BackboardGameOverGame( gameBuilder: (context) => BackboardGameOverGame(
context.numberProperty('Score', 9000000000).toInt(), context.numberProperty('Score', 9000000000).toInt(),
context.listProperty( context.listProperty(
'Character', 'Character',
BackboardGameOverGame.characterIconPaths.keys.first, BackboardGameOverGame.characterIconPaths.keys.first,
BackboardGameOverGame.characterIconPaths.keys.toList(), BackboardGameOverGame.characterIconPaths.keys.toList(),
),
), ),
), ),
codeLink: buildSourceLink('backboard/game_over.dart'),
info: BackboardGameOverGame.info,
); );
} }

@ -4,7 +4,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BallBoosterGame extends LineGame { class BallBoosterGame extends LineGame {
static const info = ''' static const description = '''
Shows how a Ball with a boost works. Shows how a Ball with a boost works.
- Drag to launch a boosted Ball. - Drag to launch a boosted Ball.

@ -3,14 +3,20 @@ import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BasicBallGame extends BasicGame with TapDetector, Traceable { class BallGame extends AssetsGame with TapDetector, Traceable {
BasicBallGame({ BallGame({
this.color = Colors.blue, this.color = Colors.blue,
this.ballPriority = 0, this.ballPriority = 0,
this.ballLayer = Layer.all, this.ballLayer = Layer.all,
}); List<String>? imagesFileNames,
}) : super(
imagesFileNames: [
Assets.images.ball.ball.keyName,
if (imagesFileNames != null) ...imagesFileNames,
],
);
static const info = ''' static const description = '''
Shows how a Ball works. Shows how a Ball works.
- Tap anywhere on the screen to spawn a ball into the game. - Tap anywhere on the screen to spawn a ball into the game.

@ -1,5 +1,4 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/ball_booster_game.dart'; import 'package:sandbox/stories/ball/ball_booster_game.dart';
@ -7,22 +6,16 @@ import 'package:sandbox/stories/ball/basic_ball_game.dart';
void addBallStories(Dashbook dashbook) { void addBallStories(Dashbook dashbook) {
dashbook.storiesOf('Ball') dashbook.storiesOf('Ball')
..add( ..addGame(
'Basic', title: 'Colored',
(context) => GameWidget( description: BallGame.description,
game: BasicBallGame( gameBuilder: (context) => BallGame(
color: context.colorProperty('color', Colors.blue), color: context.colorProperty('color', Colors.blue),
)..trace = context.boolProperty('Trace', true),
), ),
codeLink: buildSourceLink('ball/basic.dart'),
info: BasicBallGame.info,
) )
..add( ..addGame(
'Booster', title: 'Booster',
(context) => GameWidget( description: BallBoosterGame.description,
game: BallBoosterGame(), gameBuilder: (context) => BallBoosterGame(),
),
codeLink: buildSourceLink('ball/ball_booster.dart'),
info: BallBoosterGame.info,
); );
} }

@ -1,10 +1,17 @@
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/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BaseboardGame extends BasicBallGame with Traceable { class BaseboardGame extends BallGame {
static const info = ''' BaseboardGame()
: super(
imagesFileNames: [
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
],
);
static const description = '''
Shows how the Baseboards are rendered. Shows how the Baseboards are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -15,22 +22,14 @@ class BaseboardGame extends BasicBallGame with Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.baseboard.left.keyName,
Assets.images.baseboard.right.keyName,
]);
final center = screenToWorld(camera.viewport.canvasSize! / 2); final center = screenToWorld(camera.viewport.canvasSize! / 2);
final leftBaseboard = Baseboard(side: BoardSide.left)
..initialPosition = center - Vector2(25, 0)
..priority = 1;
final rightBaseboard = Baseboard(side: BoardSide.right)
..initialPosition = center + Vector2(25, 0)
..priority = 1;
await addAll([ await addAll([
leftBaseboard, Baseboard(side: BoardSide.left)
rightBaseboard, ..initialPosition = center - Vector2(25, 0)
..priority = 1,
Baseboard(side: BoardSide.right)
..initialPosition = center + Vector2(25, 0)
..priority = 1,
]); ]);
await traceAllBodies(); await traceAllBodies();

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/baseboard/baseboard_game.dart'; import 'package:sandbox/stories/baseboard/baseboard_game.dart';
void addBaseboardStories(Dashbook dashbook) { void addBaseboardStories(Dashbook dashbook) {
dashbook.storiesOf('Baseboard').add( dashbook.storiesOf('Baseboard').addGame(
'Basic', title: 'Traced',
(context) => GameWidget( description: BaseboardGame.description,
game: BaseboardGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => BaseboardGame(),
),
codeLink: buildSourceLink('baseboard_game/basic.dart'),
info: BaseboardGame.info,
); );
} }

@ -1,11 +1,19 @@
import 'package:flame/extensions.dart'; import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BoundariesGame extends BasicBallGame with Traceable { class BoundariesGame extends BallGame {
static const info = ''' BoundariesGame()
: super(
imagesFileNames: [
Assets.images.boundary.outer.keyName,
Assets.images.boundary.outerBottom.keyName,
Assets.images.boundary.bottom.keyName,
],
);
static const description = '''
Shows how Boundaries are rendered. Shows how Boundaries are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -16,18 +24,11 @@ class BoundariesGame extends BasicBallGame with Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.boundary.outer.keyName,
Assets.images.boundary.bottom.keyName,
]);
await addFromBlueprint(Boundaries());
await ready();
camera camera
..followVector2(Vector2.zero()) ..followVector2(Vector2.zero())
..zoom = 6; ..zoom = 6;
await addFromBlueprint(Boundaries());
await ready();
await traceAllBodies(); await traceAllBodies();
} }
} }

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/boundaries/boundaries_game.dart'; import 'package:sandbox/stories/boundaries/boundaries_game.dart';
void addBoundariesStories(Dashbook dashbook) { void addBoundariesStories(Dashbook dashbook) {
dashbook.storiesOf('Boundaries').add( dashbook.storiesOf('Boundaries').addGame(
'Basic', title: 'Traced',
(context) => GameWidget( description: BoundariesGame.description,
game: BoundariesGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => BoundariesGame(),
),
codeLink: buildSourceLink('boundaries_game/basic.dart'),
info: BoundariesGame.info,
); );
} }

@ -2,7 +2,7 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
class ChromeDinoGame extends Forge2DGame { class ChromeDinoGame extends Forge2DGame {
static const info = 'Shows how a ChromeDino is rendered.'; static const description = 'Shows how a ChromeDino is rendered.';
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/chrome_dino/chrome_dino_game.dart'; import 'package:sandbox/stories/chrome_dino/chrome_dino_game.dart';
void addChromeDinoStories(Dashbook dashbook) { void addChromeDinoStories(Dashbook dashbook) {
dashbook.storiesOf('Chrome Dino').add( dashbook.storiesOf('Chrome Dino').addGame(
'Basic', title: 'Trace',
(context) => GameWidget( description: ChromeDinoGame.description,
game: ChromeDinoGame(), gameBuilder: (_) => ChromeDinoGame(),
),
codeLink: buildSourceLink('chrome_dino/basic.dart'),
info: ChromeDinoGame.info,
); );
} }

@ -0,0 +1,31 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class DinoWallGame extends BallGame {
DinoWallGame() : super();
static const description = '''
Shows how DinoWalls are rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.dino.dinoLandTop.keyName,
Assets.images.dino.dinoLandBottom.keyName,
]);
await addFromBlueprint(DinoWalls());
camera.followVector2(Vector2.zero());
await traceAllBodies();
}
}

@ -0,0 +1,11 @@
import 'package:dashbook/dashbook.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/dino_wall/dino_wall_game.dart';
void addDinoWallStories(Dashbook dashbook) {
dashbook.storiesOf('DinoWall').addGame(
title: 'Traced',
description: DinoWallGame.description,
gameBuilder: (_) => DinoWallGame(),
);
}

@ -3,8 +3,8 @@ import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BasicCameraZoomGame extends BasicGame with TapDetector { class CameraZoomGame extends AssetsGame with TapDetector {
static const info = ''' static const description = '''
Shows how CameraZoom can be used. Shows how CameraZoom can be used.
- Tap to zoom in/out. - Tap to zoom in/out.

@ -3,7 +3,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class FireEffectGame extends LineGame { class FireEffectGame extends LineGame {
static const info = ''' static const description = '''
Shows how the FireEffect renders. Shows how the FireEffect renders.
- Drag a line to define the trail direction. - Drag a line to define the trail direction.

@ -1,13 +1,18 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/effects/camera_zoom_game.dart';
import 'package:sandbox/stories/effects/fire_effect_game.dart'; import 'package:sandbox/stories/effects/fire_effect_game.dart';
void addEffectsStories(Dashbook dashbook) { void addEffectsStories(Dashbook dashbook) {
dashbook.storiesOf('Effects').add( dashbook.storiesOf('Effects')
'Fire Effect', ..addGame(
(context) => GameWidget(game: FireEffectGame()), title: 'Fire',
codeLink: buildSourceLink('effects/fire_effect.dart'), description: FireEffectGame.description,
info: FireEffectGame.info, gameBuilder: (_) => FireEffectGame(),
); )
..addGame(
title: 'CameraZoom',
description: CameraZoomGame.description,
gameBuilder: (_) => CameraZoomGame(),
);
} }

@ -2,12 +2,19 @@ import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable { class FlipperGame extends BallGame with KeyboardEvents {
static const info = ''' FlipperGame()
: super(
imagesFileNames: [
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
],
);
static const description = '''
Shows how Flippers are rendered. Shows how Flippers are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -33,21 +40,12 @@ class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
]);
final center = screenToWorld(camera.viewport.canvasSize! / 2); final center = screenToWorld(camera.viewport.canvasSize! / 2);
leftFlipper = Flipper(side: BoardSide.left)
..initialPosition = center - Vector2(Flipper.size.x, 0);
rightFlipper = Flipper(side: BoardSide.right)
..initialPosition = center + Vector2(Flipper.size.x, 0);
await addAll([ await addAll([
leftFlipper, leftFlipper = Flipper(side: BoardSide.left)
rightFlipper, ..initialPosition = center - Vector2(Flipper.size.x, 0),
rightFlipper = Flipper(side: BoardSide.right)
..initialPosition = center + Vector2(Flipper.size.x, 0),
]); ]);
await traceAllBodies(); await traceAllBodies();

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/flipper/flipper_game.dart'; import 'package:sandbox/stories/flipper/flipper_game.dart';
void addFlipperStories(Dashbook dashbook) { void addFlipperStories(Dashbook dashbook) {
dashbook.storiesOf('Flipper').add( dashbook.storiesOf('Flipper').addGame(
'Basic', title: 'Traced',
(context) => GameWidget( description: FlipperGame.description,
game: FlipperGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => FlipperGame(),
),
codeLink: buildSourceLink('flipper/basic.dart'),
info: FlipperGame.info,
); );
} }

@ -2,11 +2,18 @@ import 'dart:async';
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/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BigDashNestBumperGame extends BasicBallGame with Traceable { class BigDashNestBumperGame extends BallGame {
static const info = ''' BigDashNestBumperGame()
: super(
imagesFileNames: [
Assets.images.dash.bumper.main.active.keyName,
Assets.images.dash.bumper.main.inactive.keyName,
],
);
static const description = '''
Shows how a BigDashNestBumper is rendered. Shows how a BigDashNestBumper is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -16,13 +23,10 @@ class BigDashNestBumperGame extends BasicBallGame with Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.dash.bumper.main.active.keyName,
Assets.images.dash.bumper.main.inactive.keyName,
]);
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await add(DashNestBumper.main()..priority = 1); await add(
DashNestBumper.main()..priority = 1,
);
await traceAllBodies(); await traceAllBodies();
} }
} }

@ -2,11 +2,20 @@ import 'dart:async';
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SignpostGame extends BasicBallGame with Traceable, TapDetector { class SignpostGame extends BallGame {
static const info = ''' SignpostGame()
: super(
imagesFileNames: [
Assets.images.signpost.inactive.keyName,
Assets.images.signpost.active1.keyName,
Assets.images.signpost.active2.keyName,
Assets.images.signpost.active3.keyName,
],
);
static const description = '''
Shows how a Signpost is rendered. Shows how a Signpost is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -17,13 +26,6 @@ class SignpostGame extends BasicBallGame with Traceable, TapDetector {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.signpost.inactive.keyName,
Assets.images.signpost.active1.keyName,
Assets.images.signpost.active2.keyName,
Assets.images.signpost.active3.keyName,
]);
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await add(Signpost()); await add(Signpost());
await traceAllBodies(); await traceAllBodies();

@ -2,11 +2,18 @@ import 'dart:async';
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/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SmallDashNestBumperAGame extends BasicBallGame with Traceable { class SmallDashNestBumperAGame extends BallGame {
static const info = ''' SmallDashNestBumperAGame()
: super(
imagesFileNames: [
Assets.images.dash.bumper.a.active.keyName,
Assets.images.dash.bumper.a.inactive.keyName,
],
);
static const description = '''
Shows how a SmallDashNestBumper ("a") is rendered. Shows how a SmallDashNestBumper ("a") is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -16,11 +23,6 @@ class SmallDashNestBumperAGame extends BasicBallGame with Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.dash.bumper.a.active.keyName,
Assets.images.dash.bumper.a.inactive.keyName,
]);
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await add(DashNestBumper.a()..priority = 1); await add(DashNestBumper.a()..priority = 1);
await traceAllBodies(); await traceAllBodies();

@ -2,11 +2,18 @@ import 'dart:async';
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/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SmallDashNestBumperBGame extends BasicBallGame with Traceable { class SmallDashNestBumperBGame extends BallGame {
static const info = ''' SmallDashNestBumperBGame()
: super(
imagesFileNames: [
Assets.images.dash.bumper.b.active.keyName,
Assets.images.dash.bumper.b.inactive.keyName,
],
);
static const description = '''
Shows how a SmallDashNestBumper ("b") is rendered. Shows how a SmallDashNestBumper ("b") is rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -16,11 +23,6 @@ class SmallDashNestBumperBGame extends BasicBallGame with Traceable {
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await images.loadAll([
Assets.images.dash.bumper.b.active.keyName,
Assets.images.dash.bumper.b.inactive.keyName,
]);
camera.followVector2(Vector2.zero()); camera.followVector2(Vector2.zero());
await add(DashNestBumper.b()..priority = 1); await add(DashNestBumper.b()..priority = 1);
await traceAllBodies(); await traceAllBodies();

@ -1,5 +1,4 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/flutter_forest/big_dash_nest_bumper_game.dart'; import 'package:sandbox/stories/flutter_forest/big_dash_nest_bumper_game.dart';
import 'package:sandbox/stories/flutter_forest/signpost_game.dart'; import 'package:sandbox/stories/flutter_forest/signpost_game.dart';
@ -8,39 +7,24 @@ import 'package:sandbox/stories/flutter_forest/small_dash_nest_bumper_b_game.dar
void addDashNestBumperStories(Dashbook dashbook) { void addDashNestBumperStories(Dashbook dashbook) {
dashbook.storiesOf('Flutter Forest') dashbook.storiesOf('Flutter Forest')
..add( ..addGame(
'Signpost', title: 'Signpost',
(context) => GameWidget( description: SignpostGame.description,
game: SignpostGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => SignpostGame(),
),
codeLink: buildSourceLink('flutter_forest/signpost.dart'),
info: SignpostGame.info,
) )
..add( ..addGame(
'Big Dash Nest Bumper', title: 'Big Dash Nest Bumper',
(context) => GameWidget( description: BigDashNestBumperGame.description,
game: BigDashNestBumperGame() gameBuilder: (_) => BigDashNestBumperGame(),
..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('flutter_forest/big_dash_nest_bumper.dart'),
info: BigDashNestBumperGame.info,
) )
..add( ..addGame(
'Small Dash Nest Bumper A', title: 'Small Dash Nest Bumper A',
(context) => GameWidget( description: SmallDashNestBumperAGame.description,
game: SmallDashNestBumperAGame() gameBuilder: (_) => SmallDashNestBumperAGame(),
..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('flutter_forest/small_dash_nest_bumper_a.dart'),
info: SmallDashNestBumperAGame.info,
) )
..add( ..addGame(
'Small Dash Nest Bumper B', title: 'Small Dash Nest Bumper B',
(context) => GameWidget( description: SmallDashNestBumperBGame.description,
game: SmallDashNestBumperBGame() gameBuilder: (_) => SmallDashNestBumperBGame(),
..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('flutter_forest/small_dash_nest_bumper_b.dart'),
info: SmallDashNestBumperBGame.info,
); );
} }

@ -5,10 +5,10 @@ import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class GoogleLetterGame extends BasicBallGame { class GoogleLetterGame extends BallGame {
GoogleLetterGame() : super(color: const Color(0xFF009900)); GoogleLetterGame() : super(color: const Color(0xFF009900));
static const info = ''' static const description = '''
Shows how a GoogleLetter is rendered. Shows how a GoogleLetter is rendered.
- Tap anywhere on the screen to spawn a ball into the game. - Tap anywhere on the screen to spawn a ball into the game.

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/google_word/google_letter_game.dart'; import 'package:sandbox/stories/google_word/google_letter_game.dart';
void addGoogleWordStories(Dashbook dashbook) { void addGoogleWordStories(Dashbook dashbook) {
dashbook.storiesOf('Google Word').add( dashbook.storiesOf('Google Word').addGame(
'Letter', title: 'Letter 0',
(context) => GameWidget( description: GoogleLetterGame.description,
game: GoogleLetterGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => GoogleLetterGame(),
),
codeLink: buildSourceLink('google_word/letter.dart'),
info: GoogleLetterGame.info,
); );
} }

@ -1,10 +1,9 @@
import 'package:flame/extensions.dart'; import 'package:flame/extensions.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class KickerGame extends BasicBallGame with Traceable { class KickerGame extends BallGame {
static const info = ''' static const description = '''
Shows how Kickers are rendered. Shows how Kickers are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -16,14 +15,14 @@ class KickerGame extends BasicBallGame with Traceable {
await super.onLoad(); await super.onLoad();
final center = screenToWorld(camera.viewport.canvasSize! / 2); final center = screenToWorld(camera.viewport.canvasSize! / 2);
await addAll(
final leftKicker = Kicker(side: BoardSide.left) [
..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y); Kicker(side: BoardSide.left)
await add(leftKicker); ..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y),
Kicker(side: BoardSide.right)
final rightKicker = Kicker(side: BoardSide.right) ..initialPosition = Vector2(center.x + (Kicker.size.x * 2), center.y),
..initialPosition = Vector2(center.x + (Kicker.size.x * 2), center.y); ],
await add(rightKicker); );
await traceAllBodies(); await traceAllBodies();
} }

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/kicker/kicker_game.dart'; import 'package:sandbox/stories/kicker/kicker_game.dart';
void addKickerStories(Dashbook dashbook) { void addKickerStories(Dashbook dashbook) {
dashbook.storiesOf('Kickers').add( dashbook.storiesOf('Kickers').addGame(
'Basic', title: 'Traced',
(context) => GameWidget( description: KickerGame.description,
game: KickerGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => KickerGame(),
),
codeLink: buildSourceLink('kicker_game/basic.dart'),
info: KickerGame.info,
); );
} }

@ -6,7 +6,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class LaunchRampGame extends BasicBallGame { class LaunchRampGame extends BallGame {
LaunchRampGame() LaunchRampGame()
: super( : super(
color: Colors.blue, color: Colors.blue,
@ -14,7 +14,7 @@ class LaunchRampGame extends BasicBallGame {
ballLayer: Layer.launcher, ballLayer: Layer.launcher,
); );
static const info = ''' static const description = '''
Shows how LaunchRamp are rendered. Shows how LaunchRamp are rendered.
- Activate the "trace" parameter to overlay the body. - Activate the "trace" parameter to overlay the body.
@ -28,10 +28,8 @@ class LaunchRampGame extends BasicBallGame {
camera camera
..followVector2(Vector2(0, 0)) ..followVector2(Vector2(0, 0))
..zoom = 7.5; ..zoom = 7.5;
await addFromBlueprint(LaunchRamp());
final launchRamp = LaunchRamp(); await ready();
unawaited(addFromBlueprint(launchRamp));
await traceAllBodies(); await traceAllBodies();
} }
} }

@ -1,15 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/launch_ramp/launch_ramp_game.dart'; import 'package:sandbox/stories/launch_ramp/launch_ramp_game.dart';
void addLaunchRampStories(Dashbook dashbook) { void addLaunchRampStories(Dashbook dashbook) {
dashbook.storiesOf('LaunchRamp').add( dashbook.storiesOf('LaunchRamp').addGame(
'Basic', title: 'Traced',
(context) => GameWidget( description: LaunchRampGame.description,
game: LaunchRampGame()..trace = context.boolProperty('Trace', true), gameBuilder: (_) => LaunchRampGame(),
),
codeLink: buildSourceLink('launch_ramp/basic.dart'),
info: LaunchRampGame.info,
); );
} }

@ -2,36 +2,35 @@ 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 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
class BasicLayerGame extends BasicGame with TapDetector { class LayerGame extends BallGame with TapDetector {
BasicLayerGame({required this.color}); static const description = '''
static const info = '''
Shows how Layers work when a Ball hits other components. Shows how Layers work when a Ball hits other components.
- Tap anywhere on the screen to spawn a Ball into the game. - Tap anywhere on the screen to spawn a Ball into the game.
'''; ''';
final Color color;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await add(BigSquare()..initialPosition = Vector2(30, -40)); await addAll(
await add(SmallSquare()..initialPosition = Vector2(50, -40)); [
await add(UnlayeredSquare()..initialPosition = Vector2(60, -40)); _BigSquare()..initialPosition = Vector2(30, -40),
} _SmallSquare()..initialPosition = Vector2(50, -40),
_UnlayeredSquare()..initialPosition = Vector2(60, -40),
@override ],
void onTapUp(TapUpInfo info) {
add(
Ball(baseColor: color)..initialPosition = info.eventPosition.game,
); );
} }
} }
class BigSquare extends BodyComponent with InitialPosition, Layered { class _BigSquare extends BodyComponent with InitialPosition, Layered {
BigSquare() { _BigSquare()
: super(
children: [
_UnlayeredSquare()..initialPosition = Vector2.all(4),
_SmallSquare()..initialPosition = Vector2.all(-4),
],
) {
paint = Paint() paint = Paint()
..color = const Color.fromARGB(255, 8, 218, 241) ..color = const Color.fromARGB(255, 8, 218, 241)
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
@ -42,27 +41,13 @@ class BigSquare extends BodyComponent with InitialPosition, Layered {
Body createBody() { Body createBody() {
final shape = PolygonShape()..setAsBoxXY(16, 16); final shape = PolygonShape()..setAsBoxXY(16, 16);
final fixtureDef = FixtureDef(shape); final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()..position = initialPosition; final bodyDef = BodyDef()..position = initialPosition;
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
@override
Future<void> onLoad() async {
await super.onLoad();
await addAll(
[
UnlayeredSquare()..initialPosition = Vector2.all(4),
SmallSquare()..initialPosition = Vector2.all(-4),
],
);
}
} }
class SmallSquare extends BodyComponent with InitialPosition, Layered { class _SmallSquare extends BodyComponent with InitialPosition, Layered {
SmallSquare() { _SmallSquare() {
paint = Paint() paint = Paint()
..color = const Color.fromARGB(255, 27, 241, 8) ..color = const Color.fromARGB(255, 27, 241, 8)
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
@ -73,15 +58,13 @@ class SmallSquare extends BodyComponent with InitialPosition, Layered {
Body createBody() { Body createBody() {
final shape = PolygonShape()..setAsBoxXY(2, 2); final shape = PolygonShape()..setAsBoxXY(2, 2);
final fixtureDef = FixtureDef(shape); final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()..position = initialPosition; final bodyDef = BodyDef()..position = initialPosition;
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }
class UnlayeredSquare extends BodyComponent with InitialPosition { class _UnlayeredSquare extends BodyComponent with InitialPosition {
UnlayeredSquare() { _UnlayeredSquare() {
paint = Paint() paint = Paint()
..color = const Color.fromARGB(255, 241, 8, 8) ..color = const Color.fromARGB(255, 241, 8, 8)
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
@ -91,9 +74,7 @@ class UnlayeredSquare extends BodyComponent with InitialPosition {
Body createBody() { Body createBody() {
final shape = PolygonShape()..setAsBoxXY(3, 3); final shape = PolygonShape()..setAsBoxXY(3, 3);
final fixtureDef = FixtureDef(shape); final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef()..position = initialPosition; final bodyDef = BodyDef()..position = initialPosition;
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
} }

@ -1,18 +1,11 @@
import 'package:dashbook/dashbook.dart'; import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/layer/basic_layer_game.dart'; import 'package:sandbox/stories/layer/layer_game.dart';
void addLayerStories(Dashbook dashbook) { void addLayerStories(Dashbook dashbook) {
dashbook.storiesOf('Layer').add( dashbook.storiesOf('Layer').addGame(
'Layer', title: 'Example',
(context) => GameWidget( description: LayerGame.description,
game: BasicLayerGame( gameBuilder: (_) => LayerGame(),
color: context.colorProperty('color', Colors.blue),
),
),
codeLink: buildSourceLink('layer/basic.dart'),
info: BasicLayerGame.info,
); );
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save