refactor: bumpers to use group components

pull/204/head
Allison Ryan 3 years ago
parent 3735b06d55
commit f5c3c5fb56

@ -11,7 +11,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// can bounce off [DashNestBumper]s. /// can bounce off [DashNestBumper]s.
/// ///
/// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] /// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest]
/// is awarded, and the [BigDashNestBumper] releases a new [Ball]. /// is awarded, and the [DashNestBumper.main] releases a new [Ball].
/// {@endtemplate} /// {@endtemplate}
class FlutterForest extends Component class FlutterForest extends Component
with Controls<_FlutterForestController>, HasGameRef<PinballGame> { with Controls<_FlutterForestController>, HasGameRef<PinballGame> {
@ -27,11 +27,11 @@ class FlutterForest extends Component
final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3); final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3);
final bigNest = _BigDashNestBumper() final bigNest = _DashNestBumper.main()
..initialPosition = Vector2(18.55, -59.35); ..initialPosition = Vector2(18.55, -59.35);
final smallLeftNest = _SmallDashNestBumper.a() final smallLeftNest = _DashNestBumper.a()
..initialPosition = Vector2(8.95, -51.95); ..initialPosition = Vector2(8.95, -51.95);
final smallRightNest = _SmallDashNestBumper.b() final smallRightNest = _DashNestBumper.b()
..initialPosition = Vector2(23.3, -46.75); ..initialPosition = Vector2(23.3, -46.75);
final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66); final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66);
@ -81,15 +81,12 @@ class _FlutterForestController extends ComponentController<FlutterForest>
// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D // TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
// ContactCallback process is enhanced. // ContactCallback process is enhanced.
class _BigDashNestBumper extends BigDashNestBumper with ScorePoints { class _DashNestBumper extends DashNestBumper with ScorePoints {
@override _DashNestBumper.main() : super.main();
int get points => 20;
}
class _SmallDashNestBumper extends SmallDashNestBumper with ScorePoints { _DashNestBumper.a() : super.a();
_SmallDashNestBumper.a() : super.a();
_SmallDashNestBumper.b() : super.b(); _DashNestBumper.b() : super.b();
@override @override
int get points => 20; int get points => 20;

@ -1,36 +1,40 @@
import 'dart:async';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template alien_bumper} /// {@template alien_bumper}
/// Bumper for Alien area. /// Bumper for area under the [Spaceship].
/// {@endtemplate} /// {@endtemplate}
// TODO(ruimiguel): refactor later to unify with DashBumpers.
class AlienBumper extends BodyComponent with InitialPosition { class AlienBumper extends BodyComponent with InitialPosition {
/// {@macro alien_bumper} /// {@macro alien_bumper}
AlienBumper._({ AlienBumper._({
required double majorRadius, required double majorRadius,
required double minorRadius, required double minorRadius,
required String activeAssetPath, required String onAssetPath,
required String inactiveAssetPath, required String offAssetPath,
required SpriteComponent spriteComponent,
}) : _majorRadius = majorRadius, }) : _majorRadius = majorRadius,
_minorRadius = minorRadius, _minorRadius = minorRadius,
_activeAssetPath = activeAssetPath, super(
_inactiveAssetPath = inactiveAssetPath, children: [
_spriteComponent = spriteComponent; _AlienBumperSpriteGroupComponent(
onAssetPath: onAssetPath,
offAssetPath: offAssetPath,
),
],
) {
renderBody = false;
}
/// {@macro alien_bumper} /// {@macro alien_bumper}
AlienBumper.a() AlienBumper.a()
: this._( : this._(
majorRadius: 3.52, majorRadius: 3.52,
minorRadius: 2.97, minorRadius: 2.97,
activeAssetPath: Assets.images.alienBumper.a.active.keyName, onAssetPath: Assets.images.alienBumper.a.on.keyName,
inactiveAssetPath: Assets.images.alienBumper.a.inactive.keyName, offAssetPath: Assets.images.alienBumper.a.off.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.1),
),
); );
/// {@macro alien_bumper} /// {@macro alien_bumper}
@ -38,32 +42,12 @@ class AlienBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 3.19, majorRadius: 3.19,
minorRadius: 2.79, minorRadius: 2.79,
activeAssetPath: Assets.images.alienBumper.b.active.keyName, onAssetPath: Assets.images.alienBumper.b.on.keyName,
inactiveAssetPath: Assets.images.alienBumper.b.inactive.keyName, offAssetPath: Assets.images.alienBumper.a.off.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.1),
),
); );
final double _majorRadius; final double _majorRadius;
final double _minorRadius; final double _minorRadius;
final String _activeAssetPath;
late final Sprite _activeSprite;
final String _inactiveAssetPath;
late final Sprite _inactiveSprite;
final SpriteComponent _spriteComponent;
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await _loadSprites();
deactivate();
await add(_spriteComponent);
}
@override @override
Body createBody() { Body createBody() {
@ -84,25 +68,54 @@ class AlienBumper extends BodyComponent with InitialPosition {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
Future<void> _loadSprites() async { /// Animates the [AlienBumper].
// TODO(alestiago): I think ideally we would like to do: Future<void> animate() async {
// Sprite(path).load so we don't require to store the activeAssetPath and final spriteGroupComponent = firstChild<_AlienBumperSpriteGroupComponent>()
// the inactive assetPath. ?..current = AlienBumperSpriteState.off;
_inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath); await Future<void>.delayed(const Duration(milliseconds: 50));
_activeSprite = await gameRef.loadSprite(_activeAssetPath); spriteGroupComponent?.current = AlienBumperSpriteState.on;
} }
}
@visibleForTesting
/// Activates the [AlienBumper]. /// Indicates the [AlienBumper]'s current sprite state.
void activate() { enum AlienBumperSpriteState {
_spriteComponent /// The on sprite is being displayed.
..sprite = _activeSprite on,
..size = _activeSprite.originalSize / 10;
/// The off sprite is being displayed.
off,
} }
/// Deactivates the [AlienBumper]. class _AlienBumperSpriteGroupComponent
void deactivate() { extends SpriteGroupComponent<AlienBumperSpriteState> with HasGameRef {
_spriteComponent _AlienBumperSpriteGroupComponent({
..sprite = _inactiveSprite required String onAssetPath,
..size = _inactiveSprite.originalSize / 10; required String offAssetPath,
}) : _onAssetPath = onAssetPath,
_offAssetPath = offAssetPath,
super(
anchor: Anchor.center,
position: Vector2(0, -0.1),
);
final String _onAssetPath;
final String _offAssetPath;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprites = {
AlienBumperSpriteState.on: Sprite(gameRef.images.fromCache(_onAssetPath)),
AlienBumperSpriteState.off:
Sprite(gameRef.images.fromCache(_offAssetPath)),
};
this.sprites = sprites;
size = sprites[AlienBumperSpriteState.on]!.originalSize / 10;
current = AlienBumperSpriteState.on;
} }
} }

@ -2,138 +2,75 @@ import 'dart:math' as math;
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template dash_nest_bumper} /// {@template dash_nest_bumper}
/// Bumper with a nest appearance. /// Bumper with a nest appearance.
/// {@endtemplate} /// {@endtemplate}
abstract class DashNestBumper extends BodyComponent with InitialPosition { class DashNestBumper extends BodyComponent with InitialPosition {
/// {@macro dash_nest_bumper} /// {@macro dash_nest_bumper}
DashNestBumper._({ DashNestBumper._({
required double majorRadius,
required double minorRadius,
required String activeAssetPath, required String activeAssetPath,
required String inactiveAssetPath, required String inactiveAssetPath,
required SpriteComponent spriteComponent, required Vector2 spritePosition,
}) : _activeAssetPath = activeAssetPath, }) : _majorRadius = majorRadius,
_inactiveAssetPath = inactiveAssetPath, _minorRadius = minorRadius,
_spriteComponent = spriteComponent; super(
children: [
final String _activeAssetPath; _DashNestBumperSpriteGroupComponent(
late final Sprite _activeSprite; activeAssetPath: activeAssetPath,
final String _inactiveAssetPath; inactiveAssetPath: inactiveAssetPath,
late final Sprite _inactiveSprite; position: spritePosition,
final SpriteComponent _spriteComponent; ),
],
Future<void> _loadSprites() async { ) {
// TODO(alestiago): I think ideally we would like to do: renderBody = false;
// Sprite(path).load so we don't require to store the activeAssetPath and
// the inactive assetPath.
_inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath);
_activeSprite = await gameRef.loadSprite(_activeAssetPath);
}
/// Activates the [DashNestBumper].
void activate() {
_spriteComponent
..sprite = _activeSprite
..size = _activeSprite.originalSize / 10;
}
/// Deactivates the [DashNestBumper].
void deactivate() {
_spriteComponent
..sprite = _inactiveSprite
..size = _inactiveSprite.originalSize / 10;
}
@override
Future<void> onLoad() async {
await super.onLoad();
await _loadSprites();
// TODO(erickzanardo): Look into using onNewState instead.
// Currently doing: onNewState(gameRef.read<GameState>()) will throw an
// `Exception: build context is not available yet`
deactivate();
await add(_spriteComponent);
}
} }
/// {@macro dash_nest_bumper} /// {@macro dash_nest_bumper}
class BigDashNestBumper extends DashNestBumper { DashNestBumper.main()
/// {@macro dash_nest_bumper} : this._(
BigDashNestBumper()
: super._(
activeAssetPath: Assets.images.dash.bumper.main.active.keyName,
inactiveAssetPath: Assets.images.dash.bumper.main.inactive.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.3),
),
);
@override
Body createBody() {
final shape = EllipseShape(
center: Vector2.zero(),
majorRadius: 5.1, majorRadius: 5.1,
minorRadius: 3.75, minorRadius: 3.75,
)..rotate(math.pi / 1.9); activeAssetPath: Assets.images.dash.bumper.main.active.keyName,
final fixtureDef = FixtureDef(shape); inactiveAssetPath: Assets.images.dash.bumper.main.inactive.keyName,
final bodyDef = BodyDef( spritePosition: Vector2(0, -0.3),
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@macro dash_nest_bumper}
class SmallDashNestBumper extends DashNestBumper {
/// {@macro dash_nest_bumper}
SmallDashNestBumper._({
required String activeAssetPath,
required String inactiveAssetPath,
required SpriteComponent spriteComponent,
}) : super._(
activeAssetPath: activeAssetPath,
inactiveAssetPath: inactiveAssetPath,
spriteComponent: spriteComponent,
); );
/// {@macro dash_nest_bumper} /// {@macro dash_nest_bumper}
SmallDashNestBumper.a() DashNestBumper.a()
: this._( : this._(
majorRadius: 3,
minorRadius: 2.5,
activeAssetPath: Assets.images.dash.bumper.a.active.keyName, activeAssetPath: Assets.images.dash.bumper.a.active.keyName,
inactiveAssetPath: Assets.images.dash.bumper.a.inactive.keyName, inactiveAssetPath: Assets.images.dash.bumper.a.inactive.keyName,
spriteComponent: SpriteComponent( spritePosition: Vector2(0.35, -1.2),
anchor: Anchor.center,
position: Vector2(0.35, -1.2),
),
); );
/// {@macro dash_nest_bumper} /// {@macro dash_nest_bumper}
SmallDashNestBumper.b() DashNestBumper.b()
: this._( : this._(
majorRadius: 3,
minorRadius: 2.5,
activeAssetPath: Assets.images.dash.bumper.b.active.keyName, activeAssetPath: Assets.images.dash.bumper.b.active.keyName,
inactiveAssetPath: Assets.images.dash.bumper.b.inactive.keyName, inactiveAssetPath: Assets.images.dash.bumper.b.inactive.keyName,
spriteComponent: SpriteComponent( spritePosition: Vector2(0.35, -1.2),
anchor: Anchor.center,
position: Vector2(0.35, -1.2),
),
); );
final double _majorRadius;
final double _minorRadius;
@override @override
Body createBody() { Body createBody() {
final shape = EllipseShape( final shape = EllipseShape(
center: Vector2.zero(), center: Vector2.zero(),
majorRadius: 3, majorRadius: _majorRadius,
minorRadius: 2.25, minorRadius: _minorRadius,
)..rotate(math.pi / 2); )..rotate(math.pi / 1.9);
final fixtureDef = FixtureDef( final fixtureDef = FixtureDef(shape, restitution: 4);
shape,
restitution: 4,
);
final bodyDef = BodyDef( final bodyDef = BodyDef(
position: initialPosition, position: initialPosition,
userData: this, userData: this,
@ -141,4 +78,59 @@ class SmallDashNestBumper extends DashNestBumper {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
/// Activates the [DashNestBumper].
void activate() {
firstChild<_DashNestBumperSpriteGroupComponent>()?.current =
DashNestBumperSpriteState.active;
}
/// Deactivates the [DashNestBumper].
void deactivate() {
firstChild<_DashNestBumperSpriteGroupComponent>()?.current =
DashNestBumperSpriteState.inactive;
}
}
@visibleForTesting
/// Indicates the [DashNestBumper]'s current sprite state.
enum DashNestBumperSpriteState {
/// The active sprite is being displayed.
active,
/// The inactive sprite is being displayed.
inactive,
}
class _DashNestBumperSpriteGroupComponent
extends SpriteGroupComponent<DashNestBumperSpriteState> with HasGameRef {
_DashNestBumperSpriteGroupComponent({
required String activeAssetPath,
required String inactiveAssetPath,
required Vector2 position,
}) : _activeAssetPath = activeAssetPath,
_inactiveAssetPath = inactiveAssetPath,
super(
anchor: Anchor.center,
position: position,
);
final String _activeAssetPath;
final String _inactiveAssetPath;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprites = {
DashNestBumperSpriteState.active:
Sprite(gameRef.images.fromCache(_activeAssetPath)),
DashNestBumperSpriteState.inactive:
Sprite(gameRef.images.fromCache(_inactiveAssetPath)),
};
this.sprites = sprites;
size = sprites[DashNestBumperSpriteState.inactive]!.originalSize / 10;
current = DashNestBumperSpriteState.inactive;
}
} }

@ -2,37 +2,42 @@ import 'dart:math' as math;
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_bumper} /// {@template sparky_bumper}
/// Bumper for Sparky area. /// Bumper for Sparky area.
/// {@endtemplate} /// {@endtemplate}
// TODO(ruimiguel): refactor later to unify with DashBumpers.
class SparkyBumper extends BodyComponent with InitialPosition { class SparkyBumper extends BodyComponent with InitialPosition {
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
SparkyBumper._({ SparkyBumper._({
required double majorRadius, required double majorRadius,
required double minorRadius, required double minorRadius,
required String activeAssetPath, required String onAssetPath,
required String inactiveAssetPath, required String offAssetPath,
required SpriteComponent spriteComponent, required Vector2 spritePosition,
}) : _majorRadius = majorRadius, }) : _majorRadius = majorRadius,
_minorRadius = minorRadius, _minorRadius = minorRadius,
_activeAssetPath = activeAssetPath, super(
_inactiveAssetPath = inactiveAssetPath, children: [
_spriteComponent = spriteComponent; _SparkyBumperSpriteGroupComponent(
onAssetPath: onAssetPath,
offAssetPath: offAssetPath,
position: spritePosition,
),
],
) {
renderBody = false;
}
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
SparkyBumper.a() SparkyBumper.a()
: this._( : this._(
majorRadius: 2.9, majorRadius: 2.9,
minorRadius: 2.1, minorRadius: 2.1,
activeAssetPath: Assets.images.sparky.bumper.a.active.keyName, onAssetPath: Assets.images.sparky.bumper.a.on.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.a.inactive.keyName, offAssetPath: Assets.images.sparky.bumper.a.off.keyName,
spriteComponent: SpriteComponent( spritePosition: Vector2(0, -0.25),
anchor: Anchor.center,
position: Vector2(0, -0.25),
),
); );
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
@ -40,12 +45,9 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 2.85, majorRadius: 2.85,
minorRadius: 2, minorRadius: 2,
activeAssetPath: Assets.images.sparky.bumper.b.active.keyName, onAssetPath: Assets.images.sparky.bumper.b.on.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.b.inactive.keyName, offAssetPath: Assets.images.sparky.bumper.b.off.keyName,
spriteComponent: SpriteComponent( spritePosition: Vector2(0, -0.35),
anchor: Anchor.center,
position: Vector2(0, -0.35),
),
); );
/// {@macro sparky_bumper} /// {@macro sparky_bumper}
@ -53,33 +55,13 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 3, majorRadius: 3,
minorRadius: 2.2, minorRadius: 2.2,
activeAssetPath: Assets.images.sparky.bumper.c.active.keyName, onAssetPath: Assets.images.sparky.bumper.c.on.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.c.inactive.keyName, offAssetPath: Assets.images.sparky.bumper.c.off.keyName,
spriteComponent: SpriteComponent( spritePosition: Vector2(0, -0.4),
anchor: Anchor.center,
position: Vector2(0, -0.4),
),
); );
final double _majorRadius; final double _majorRadius;
final double _minorRadius; final double _minorRadius;
final String _activeAssetPath;
late final Sprite _activeSprite;
final String _inactiveAssetPath;
late final Sprite _inactiveSprite;
final SpriteComponent _spriteComponent;
@override
Future<void> onLoad() async {
await super.onLoad();
await _loadSprites();
// TODO(erickzanardo): Look into using onNewState instead.
// Currently doing: onNewState(gameRef.read<GameState>()) will throw an
// `Exception: build context is not available yet`
deactivate();
await add(_spriteComponent);
}
@override @override
Body createBody() { Body createBody() {
@ -101,25 +83,54 @@ class SparkyBumper extends BodyComponent with InitialPosition {
return world.createBody(bodyDef)..createFixture(fixtureDef); return world.createBody(bodyDef)..createFixture(fixtureDef);
} }
Future<void> _loadSprites() async { /// Animates the [DashNestBumper].
// TODO(alestiago): I think ideally we would like to do: Future<void> animate() async {
// Sprite(path).load so we don't require to store the activeAssetPath and final spriteGroupComponent = firstChild<_SparkyBumperSpriteGroupComponent>()
// the inactive assetPath. ?..current = SparkyBumperSpriteState.off;
_inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath); await Future<void>.delayed(const Duration(milliseconds: 50));
_activeSprite = await gameRef.loadSprite(_activeAssetPath); spriteGroupComponent?.current = SparkyBumperSpriteState.on;
} }
}
@visibleForTesting
/// Activates the [DashNestBumper]. /// Indicates the [SparkyBumper]'s current sprite state.
void activate() { enum SparkyBumperSpriteState {
_spriteComponent /// The on sprite is being displayed.
..sprite = _activeSprite on,
..size = _activeSprite.originalSize / 10;
/// The off sprite is being displayed.
off,
} }
/// Deactivates the [DashNestBumper]. class _SparkyBumperSpriteGroupComponent
void deactivate() { extends SpriteGroupComponent<SparkyBumperSpriteState> with HasGameRef {
_spriteComponent _SparkyBumperSpriteGroupComponent({
..sprite = _inactiveSprite required String onAssetPath,
..size = _inactiveSprite.originalSize / 10; required String offAssetPath,
required Vector2 position,
}) : _onAssetPath = onAssetPath,
_offAssetPath = offAssetPath,
super(
anchor: Anchor.center,
position: position,
);
final String _onAssetPath;
final String _offAssetPath;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprites = {
SparkyBumperSpriteState.on:
Sprite(gameRef.images.fromCache(_onAssetPath)),
SparkyBumperSpriteState.off:
Sprite(gameRef.images.fromCache(_offAssetPath)),
};
this.sprites = sprites;
size = sprites[SparkyBumperSpriteState.on]!.originalSize / 10;
current = SparkyBumperSpriteState.on;
} }
} }

Loading…
Cancel
Save