mirror of https://github.com/flutter/pinball.git
feat: add sparky bumpers (#150)
* feat: added sparky bumpers * test: tests for sparky bumpers * feat: sandbox for sparky bumpers * chore: unused imports * refactor: removed Bumper and added TODO for future refactor * fix: fixed size and tracing * fix: fix tracing * fix: final ellipse sizes * refactor: different sized bumpers * Update packages/pinball_components/lib/src/components/sparky_bumper.dart Co-authored-by: Allison Ryan <allisonryan0002@gmail.com> Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>pull/162/head
parent
d798fdf9fe
commit
254c38d2a4
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,125 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template sparky_bumper}
|
||||
/// Bumper for Sparky area.
|
||||
/// {@endtemplate}
|
||||
// TODO(ruimiguel): refactor later to unify with DashBumpers.
|
||||
class SparkyBumper extends BodyComponent with InitialPosition {
|
||||
/// {@macro sparky_bumper}
|
||||
SparkyBumper._({
|
||||
required double majorRadius,
|
||||
required double minorRadius,
|
||||
required String activeAssetPath,
|
||||
required String inactiveAssetPath,
|
||||
required SpriteComponent spriteComponent,
|
||||
}) : _majorRadius = majorRadius,
|
||||
_minorRadius = minorRadius,
|
||||
_activeAssetPath = activeAssetPath,
|
||||
_inactiveAssetPath = inactiveAssetPath,
|
||||
_spriteComponent = spriteComponent;
|
||||
|
||||
/// {@macro sparky_bumper}
|
||||
SparkyBumper.a()
|
||||
: this._(
|
||||
majorRadius: 2.9,
|
||||
minorRadius: 2.1,
|
||||
activeAssetPath: Assets.images.sparkyBumper.a.active.keyName,
|
||||
inactiveAssetPath: Assets.images.sparkyBumper.a.inactive.keyName,
|
||||
spriteComponent: SpriteComponent(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(0, -0.25),
|
||||
),
|
||||
);
|
||||
|
||||
/// {@macro sparky_bumper}
|
||||
SparkyBumper.b()
|
||||
: this._(
|
||||
majorRadius: 2.85,
|
||||
minorRadius: 2,
|
||||
activeAssetPath: Assets.images.sparkyBumper.b.active.keyName,
|
||||
inactiveAssetPath: Assets.images.sparkyBumper.b.inactive.keyName,
|
||||
spriteComponent: SpriteComponent(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(0, -0.35),
|
||||
),
|
||||
);
|
||||
|
||||
/// {@macro sparky_bumper}
|
||||
SparkyBumper.c()
|
||||
: this._(
|
||||
majorRadius: 3,
|
||||
minorRadius: 2.2,
|
||||
activeAssetPath: Assets.images.sparkyBumper.c.active.keyName,
|
||||
inactiveAssetPath: Assets.images.sparkyBumper.c.inactive.keyName,
|
||||
spriteComponent: SpriteComponent(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(0, -0.4),
|
||||
),
|
||||
);
|
||||
|
||||
final double _majorRadius;
|
||||
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
|
||||
Body createBody() {
|
||||
renderBody = false;
|
||||
|
||||
final shape = EllipseShape(
|
||||
center: Vector2.zero(),
|
||||
majorRadius: _majorRadius,
|
||||
minorRadius: _minorRadius,
|
||||
)..rotate(math.pi / 1.9);
|
||||
final fixtureDef = FixtureDef(shape)
|
||||
..friction = 0
|
||||
..restitution = 4;
|
||||
|
||||
final bodyDef = BodyDef()
|
||||
..position = initialPosition
|
||||
..userData = this;
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
|
||||
Future<void> _loadSprites() async {
|
||||
// TODO(alestiago): I think ideally we would like to do:
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class SparkyBumperGame extends BasicBallGame {
|
||||
SparkyBumperGame({
|
||||
required this.trace,
|
||||
}) : super(color: const Color(0xFF0000FF));
|
||||
|
||||
static const info = '''
|
||||
Shows how a SparkyBumper is rendered.
|
||||
|
||||
Activate the "trace" parameter to overlay the body.
|
||||
''';
|
||||
|
||||
final bool trace;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final center = screenToWorld(camera.viewport.canvasSize! / 2);
|
||||
final sparkyBumperA = SparkyBumper.a()
|
||||
..initialPosition = Vector2(center.x - 20, center.y - 20)
|
||||
..priority = 1;
|
||||
final sparkyBumperB = SparkyBumper.b()
|
||||
..initialPosition = Vector2(center.x - 10, center.y + 10)
|
||||
..priority = 1;
|
||||
final sparkyBumperC = SparkyBumper.c()
|
||||
..initialPosition = Vector2(center.x + 20, center.y)
|
||||
..priority = 1;
|
||||
await addAll([
|
||||
sparkyBumperA,
|
||||
sparkyBumperB,
|
||||
sparkyBumperC,
|
||||
]);
|
||||
|
||||
if (trace) {
|
||||
sparkyBumperA.trace();
|
||||
sparkyBumperB.trace();
|
||||
sparkyBumperC.trace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/sparky_bumper/sparky_bumper_game.dart';
|
||||
|
||||
void addSparkyBumperStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Sparky Bumpers').add(
|
||||
'Basic',
|
||||
(context) => GameWidget(
|
||||
game: SparkyBumperGame(
|
||||
trace: context.boolProperty('Trace', true),
|
||||
),
|
||||
),
|
||||
codeLink: buildSourceLink('sparky_bumper/basic.dart'),
|
||||
info: SparkyBumperGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('SparkyBumper', () {
|
||||
flameTester.test('"a" loads correctly', (game) async {
|
||||
final bumper = SparkyBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(game.contains(bumper), isTrue);
|
||||
});
|
||||
|
||||
flameTester.test('"b" loads correctly', (game) async {
|
||||
final bumper = SparkyBumper.b();
|
||||
await game.ensureAdd(bumper);
|
||||
expect(game.contains(bumper), isTrue);
|
||||
});
|
||||
|
||||
flameTester.test('"c" loads correctly', (game) async {
|
||||
final bumper = SparkyBumper.c();
|
||||
await game.ensureAdd(bumper);
|
||||
expect(game.contains(bumper), isTrue);
|
||||
});
|
||||
|
||||
flameTester.test('activate returns normally', (game) async {
|
||||
final bumper = SparkyBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(bumper.activate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('deactivate returns normally', (game) async {
|
||||
final bumper = SparkyBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(bumper.deactivate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('changes sprite', (game) async {
|
||||
final bumper = SparkyBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
final spriteComponent = bumper.firstChild<SpriteComponent>()!;
|
||||
|
||||
final deactivatedSprite = spriteComponent.sprite;
|
||||
bumper.activate();
|
||||
expect(
|
||||
spriteComponent.sprite,
|
||||
isNot(equals(deactivatedSprite)),
|
||||
);
|
||||
|
||||
final activatedSprite = spriteComponent.sprite;
|
||||
bumper.deactivate();
|
||||
expect(
|
||||
spriteComponent.sprite,
|
||||
isNot(equals(activatedSprite)),
|
||||
);
|
||||
|
||||
expect(
|
||||
activatedSprite,
|
||||
isNot(equals(deactivatedSprite)),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in new issue