feat: plunger animation (#200)

* feat: new plunger animation asset

* refactor: changed plunger position

* fix: fixed merge conflicts with main at launcher component

* fix: fixed merge conflicts on plunger

* feat: added plunger animation and fixed position

* refactor: plunger position and compressionDistance

* test: tests and golden test for plunger

* refactor: oncomplete at plunger animations

* test: fixed tests for animation

* refactor: plunger with SpriteAnimationGroupComponent

* test: fixed golden tests and removed unused ones

* test: golden tests images

* chore: doc and refactor methods

* refactor: improved plunger body to catch the ball

* refactor: improved plunger body to catch the ball

* test: updated plunger golden tests

* Update packages/pinball_components/lib/src/components/plunger.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* refactor: _PlungerSpriteAnimationGroupComponent load sprites onload

* chore: removed unused import

* Update packages/pinball_components/lib/src/components/plunger.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* Update packages/pinball_components/lib/src/components/plunger.dart

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

* fix: fix animation duration

* fix: fixed plunger animation

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>
pull/211/head
Rui Miguel Alonso 3 years ago committed by GitHub
parent 35c23140b5
commit a7a0082831
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,8 +16,8 @@ class Launcher extends Forge2DBlueprint {
@override
void build(Forge2DGame gameRef) {
plunger = ControlledPlunger(compressionDistance: 12.3)
..initialPosition = Vector2(40.1, 38);
plunger = ControlledPlunger(compressionDistance: 14)
..initialPosition = Vector2(40.7, 38);
final _rocket = RocketSpriteComponent()..position = Vector2(43, 62);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 290 KiB

@ -16,23 +16,47 @@ class Plunger extends BodyComponent with InitialPosition, Layered {
// are fixed.
}) : super(priority: RenderPriority.plunger) {
layer = Layer.launcher;
renderBody = false;
}
/// Distance the plunger can lower.
final double compressionDistance;
late final _PlungerSpriteAnimationGroupComponent _spriteComponent;
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final leftShapeVertices = [
Vector2(0, 0),
Vector2(-1.8, 0),
Vector2(-1.8, -2.2),
Vector2(0, -0.3),
]..map((vector) => vector.rotate(BoardDimensions.perspectiveAngle))
.toList();
final leftTriangleShape = PolygonShape()..set(leftShapeVertices);
final leftTriangleFixtureDef = FixtureDef(leftTriangleShape)..density = 80;
fixturesDef.add(leftTriangleFixtureDef);
final rightShapeVertices = [
Vector2(0, 0),
Vector2(1.8, 0),
Vector2(1.8, -2.2),
Vector2(0, -0.3),
]..map((vector) => vector.rotate(BoardDimensions.perspectiveAngle))
.toList();
final rightTriangleShape = PolygonShape()..set(rightShapeVertices);
final rightTriangleFixtureDef = FixtureDef(rightTriangleShape)
..density = 80;
fixturesDef.add(rightTriangleFixtureDef);
return fixturesDef;
}
@override
Body createBody() {
final shape = PolygonShape()
..setAsBox(
1.35,
0.5,
Vector2.zero(),
BoardDimensions.perspectiveAngle,
);
final fixtureDef = FixtureDef(shape)..density = 80;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
@ -40,12 +64,15 @@ class Plunger extends BodyComponent with InitialPosition, Layered {
gravityScale: Vector2.zero(),
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
/// Set a constant downward velocity on the [Plunger].
void pull() {
body.linearVelocity = Vector2(0, 7);
_spriteComponent.pull();
}
/// Set an upward velocity on the [Plunger].
@ -55,6 +82,7 @@ class Plunger extends BodyComponent with InitialPosition, Layered {
void release() {
final velocity = (initialPosition.y - body.position.y) * 5;
body.linearVelocity = Vector2(0, velocity);
_spriteComponent.release();
}
/// Anchors the [Plunger] to the [PrismaticJoint] that controls its vertical
@ -77,24 +105,84 @@ class Plunger extends BodyComponent with InitialPosition, Layered {
Future<void> onLoad() async {
await super.onLoad();
await _anchorToJoint();
renderBody = false;
await add(_PlungerSpriteComponent());
_spriteComponent = _PlungerSpriteAnimationGroupComponent();
await add(_spriteComponent);
}
}
class _PlungerSpriteComponent extends SpriteComponent with HasGameRef {
/// Animation states associated with a [Plunger].
enum _PlungerAnimationState {
/// Pull state.
pull,
/// Release state.
release,
}
/// Animations for pulling and releasing [Plunger].
class _PlungerSpriteAnimationGroupComponent
extends SpriteAnimationGroupComponent<_PlungerAnimationState>
with HasGameRef {
_PlungerSpriteAnimationGroupComponent()
: super(
anchor: Anchor.center,
position: Vector2(1.87, 14.9),
);
void pull() {
if (current != _PlungerAnimationState.pull) {
animation?.reset();
}
current = _PlungerAnimationState.pull;
}
void release() {
if (current != _PlungerAnimationState.release) {
animation?.reset();
}
current = _PlungerAnimationState.release;
}
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
final spriteSheet = await gameRef.images.load(
Assets.images.plunger.plunger.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(1.5, 13.4);
angle = -0.008;
const amountPerRow = 20;
const amountPerColumn = 1;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
// TODO(ruimiguel): we only need plunger pull animation, and release is just
// to reverse it, so we need to divide by 2 while we don't have only half of
// the animation (but amountPerRow and amountPerColumn needs to be correct
// in order of calculate textureSize correctly).
final pullAnimation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn ~/ 2,
amountPerRow: amountPerRow ~/ 2,
stepTime: 1 / 24,
textureSize: textureSize,
texturePosition: Vector2.zero(),
loop: false,
),
);
animations = {
_PlungerAnimationState.release: pullAnimation.reversed(),
_PlungerAnimationState.pull: pullAnimation,
};
current = _PlungerAnimationState.release;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

@ -23,9 +23,21 @@ void main() {
game.camera.zoom = 4.1;
},
verify: (game, tester) async {
final plunger = game.descendants().whereType<Plunger>().first;
plunger.pull();
game.update(1);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/plunger.png'),
matchesGoldenFile('golden/plunger/pull.png'),
);
plunger.release();
game.update(1);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/plunger/release.png'),
);
},
);
@ -110,12 +122,17 @@ void main() {
});
group('pull', () {
late Plunger plunger;
setUp(() {
plunger = Plunger(
compressionDistance: compressionDistance,
);
});
flameTester.test(
'moves downwards when pull is called',
(game) async {
final plunger = Plunger(
compressionDistance: compressionDistance,
);
await game.ensureAdd(plunger);
plunger.pull();
@ -123,6 +140,18 @@ void main() {
expect(plunger.body.linearVelocity.x, isZero);
},
);
flameTester.test(
'moves downwards when pull is called '
'and plunger is below its starting position', (game) async {
await game.ensureAdd(plunger);
plunger.pull();
plunger.release();
plunger.pull();
expect(plunger.body.linearVelocity.y, isPositive);
expect(plunger.body.linearVelocity.x, isZero);
});
});
group('release', () {

Loading…
Cancel
Save