feat: applying assets to the spaceship (#101)

* feat: applying assets to the spaceship

* fix: rebase issues

* feat: adding missing coverage

* fix: lint

* feat: improving test

* Apply suggestions from code review

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

* feat: pr suggestions

* feat: pr suggestions

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

Co-authored-by: Alejandro Santiago <dev@alestiago.com>

* feat: pr suggestions

* fix: rebase issues

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>
Co-authored-by: Alejandro Santiago <dev@alestiago.com>
pull/108/head
Erick 4 years ago committed by GitHub
parent 72b8213f74
commit 90131d9724
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

@ -1,5 +1,4 @@
import 'package:flame/components.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';

@ -9,7 +9,5 @@ export 'jetpack_ramp.dart';
export 'kicker.dart';
export 'launcher_ramp.dart';
export 'plunger.dart';
export 'ramp_opening.dart';
export 'score_points.dart';
export 'spaceship.dart';
export 'wall.dart';

@ -6,7 +6,6 @@ import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';

@ -5,7 +5,6 @@ import 'dart:math' as math;
import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';

@ -5,7 +5,6 @@ import 'dart:math' as math;
import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';

@ -11,10 +11,6 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.flipper.left.keyName),
images.load(components.Assets.images.flipper.right.keyName),
images.load(Assets.images.components.background.path),
images.load(Assets.images.components.spaceship.androidTop.path),
images.load(Assets.images.components.spaceship.androidBottom.path),
images.load(Assets.images.components.spaceship.lower.path),
images.load(Assets.images.components.spaceship.upper.path),
]);
}
}

@ -7,9 +7,9 @@ import 'package:flame/extensions.dart';
import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart' hide Assets;
class PinballGame extends Forge2DGame
@ -46,7 +46,13 @@ class PinballGame extends Forge2DGame
unawaited(_addPlunger());
unawaited(_addBonusWord());
unawaited(_addPaths());
unawaited(addFromBlueprint(Spaceship()));
unawaited(
addFromBlueprint(
Spaceship(
position: Vector2(-25, 32),
),
),
);
// Fix camera on the center of the board.
camera

@ -3,8 +3,6 @@
/// FlutterGen
/// *****************************************************
// ignore_for_file: directives_ordering,unnecessary_import
import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
@ -17,36 +15,8 @@ class $AssetsImagesGen {
class $AssetsImagesComponentsGen {
const $AssetsImagesComponentsGen();
/// File path: assets/images/components/background.png
AssetGenImage get background =>
const AssetGenImage('assets/images/components/background.png');
$AssetsImagesComponentsSpaceshipGen get spaceship =>
const $AssetsImagesComponentsSpaceshipGen();
}
class $AssetsImagesComponentsSpaceshipGen {
const $AssetsImagesComponentsSpaceshipGen();
/// File path: assets/images/components/spaceship/android-bottom.png
AssetGenImage get androidBottom => const AssetGenImage(
'assets/images/components/spaceship/android-bottom.png');
/// File path: assets/images/components/spaceship/android-top.png
AssetGenImage get androidTop =>
const AssetGenImage('assets/images/components/spaceship/android-top.png');
/// File path: assets/images/components/spaceship/lower.png
AssetGenImage get lower =>
const AssetGenImage('assets/images/components/spaceship/lower.png');
/// File path: assets/images/components/spaceship/saucer.png
AssetGenImage get saucer =>
const AssetGenImage('assets/images/components/spaceship/saucer.png');
/// File path: assets/images/components/spaceship/upper.png
AssetGenImage get upper =>
const AssetGenImage('assets/images/components/spaceship/upper.png');
}
class Assets {

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@ -3,27 +3,24 @@
/// FlutterGen
/// *****************************************************
// ignore_for_file: directives_ordering,unnecessary_import
import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
const $AssetsImagesGen();
/// File path: assets/images/ball.png
AssetGenImage get ball => const AssetGenImage('assets/images/ball.png');
$AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen();
AssetGenImage get spaceshipBridge =>
const AssetGenImage('assets/images/spaceship_bridge.png');
AssetGenImage get spaceshipSaucer =>
const AssetGenImage('assets/images/spaceship_saucer.png');
}
class $AssetsImagesFlipperGen {
const $AssetsImagesFlipperGen();
/// File path: assets/images/flipper/left.png
AssetGenImage get left =>
const AssetGenImage('assets/images/flipper/left.png');
/// File path: assets/images/flipper/right.png
AssetGenImage get right =>
const AssetGenImage('assets/images/flipper/right.png');
}

@ -5,4 +5,6 @@ export 'flipper.dart';
export 'initial_position.dart';
export 'joint_anchor.dart';
export 'layer.dart';
export 'ramp_opening.dart';
export 'shapes/shapes.dart';
export 'spaceship.dart';

@ -1,17 +1,16 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template ramp_orientation}
/// Determines if a ramp is facing [up] or [down] on the [Board].
/// Determines if a ramp is facing [up] or [down] on the Board.
/// {@endtemplate}
enum RampOrientation {
/// Facing up on the [Board].
/// Facing up on the Board.
up,
/// Facing down on the [Board].
/// Facing down on the Board.
down,
}

@ -5,23 +5,24 @@ import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
/// {@endtemplate}
class Spaceship extends Forge2DBlueprint {
/// Total size of the spaceship
static const radius = 10.0;
/// {@macro spaceship}
Spaceship({required this.position});
/// Total size of the spaceship.
static final size = Vector2(24, 18);
/// The [position] where the elements will be created
final Vector2 position;
@override
void build(_) {
final position = Vector2(
PinballGame.boardBounds.left + radius + 15,
PinballGame.boardBounds.center.dy + 30,
);
addAllContactCallback([
SpaceshipHoleBallContactCallback(),
SpaceshipEntranceBallContactCallback(),
@ -30,10 +31,9 @@ class Spaceship extends Forge2DBlueprint {
addAll([
SpaceshipSaucer()..initialPosition = position,
SpaceshipEntrance()..initialPosition = position,
SpaceshipBridge()..initialPosition = position,
SpaceshipBridgeTop()..initialPosition = position + Vector2(0, 5.5),
SpaceshipHole()..initialPosition = position - Vector2(5, 4),
SpaceshipHole()..initialPosition = position - Vector2(-5, 4),
AndroidHead()..initialPosition = position,
SpaceshipHole()..initialPosition = position - Vector2(4.8, 4.2),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.6),
SpaceshipWall()..initialPosition = position,
]);
}
@ -51,25 +51,15 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
@override
Future<void> onLoad() async {
await super.onLoad();
final sprites = await Future.wait([
gameRef.loadSprite(Assets.images.components.spaceship.saucer.path),
gameRef.loadSprite(Assets.images.components.spaceship.upper.path),
]);
await add(
SpriteComponent(
sprite: sprites.first,
size: Vector2.all(Spaceship.radius * 2),
anchor: Anchor.center,
),
final sprite = await gameRef.loadSprite(
Assets.images.spaceshipSaucer.keyName,
);
await add(
SpriteComponent(
sprite: sprites.last,
size: Vector2((Spaceship.radius * 2) + 0.5, Spaceship.radius),
sprite: sprite,
size: Spaceship.size,
anchor: Anchor.center,
position: Vector2(0, -((Spaceship.radius * 2) / 3.5)),
),
);
@ -78,7 +68,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final circleShape = CircleShape()..radius = Spaceship.radius;
final circleShape = CircleShape()..radius = 3;
final bodyDef = BodyDef()
..userData = this
@ -92,48 +82,13 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
}
}
/// {@spaceship_bridge_top}
/// The bridge of the spaceship (the android head) is divided in two
// [BodyComponent]s, this is the top part of it which contains a single sprite
/// {@endtemplate}
class SpaceshipBridgeTop extends BodyComponent with InitialPosition {
/// {@macro spaceship_bridge_top}
SpaceshipBridgeTop() : super(priority: 6);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.components.spaceship.androidTop.path,
);
await add(
SpriteComponent(
sprite: sprite,
anchor: Anchor.center,
size: Vector2((Spaceship.radius * 2) / 2.5 - 1, Spaceship.radius / 2.5),
),
);
}
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition
..type = BodyType.static;
return world.createBody(bodyDef);
}
}
/// {@template spaceship_bridge}
/// The main part of the [SpaceshipBridge], this [BodyComponent]
/// provides both the collision and the rotation animation for the bridge.
/// A [BodyComponent] that provides both the collision and the rotation
/// animation for the bridge.
/// {@endtemplate}
class SpaceshipBridge extends BodyComponent with InitialPosition, Layered {
class AndroidHead extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_bridge}
SpaceshipBridge() : super(priority: 3) {
AndroidHead() : super(priority: 3) {
layer = Layer.spaceship;
}
@ -144,17 +99,20 @@ class SpaceshipBridge extends BodyComponent with InitialPosition, Layered {
renderBody = false;
final sprite = await gameRef.images.load(
Assets.images.components.spaceship.androidBottom.path,
Assets.images.spaceshipBridge.keyName,
);
await add(
SpriteAnimationComponent.fromFrameData(
sprite,
SpriteAnimationData.sequenced(
amount: 14,
stepTime: 0.2,
textureSize: Vector2(160, 114),
amount: 72,
amountPerRow: 24,
stepTime: 0.05,
textureSize: Vector2(82, 100),
),
size: Vector2.all((Spaceship.radius * 2) / 2.5),
size: Vector2(8.2, 10),
position: Vector2(0, -2),
anchor: Anchor.center,
),
);
@ -162,7 +120,7 @@ class SpaceshipBridge extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
final circleShape = CircleShape()..radius = Spaceship.radius / 2.5;
final circleShape = CircleShape()..radius = 2;
final bodyDef = BodyDef()
..userData = this
@ -193,7 +151,8 @@ class SpaceshipEntrance extends RampOpening {
@override
Shape get shape {
const radius = Spaceship.radius * 2;
renderBody = false;
final radius = Spaceship.size.y / 2;
return PolygonShape()
..setAsEdge(
Vector2(
@ -221,7 +180,7 @@ class SpaceshipHole extends BodyComponent with InitialPosition, Layered {
@override
Body createBody() {
renderBody = false;
final circleShape = CircleShape()..radius = Spaceship.radius / 40;
final circleShape = CircleShape()..radius = 1.5;
final bodyDef = BodyDef()
..userData = this
@ -235,6 +194,28 @@ class SpaceshipHole extends BodyComponent with InitialPosition, Layered {
}
}
/// {@template spaceship_wall_shape}
/// The [ChainShape] that defines the shape of the [SpaceshipWall].
/// {@endtemplate}
class _SpaceshipWallShape extends ChainShape {
/// {@macro spaceship_wall_shape}
_SpaceshipWallShape() {
final minorRadius = (Spaceship.size.y - 2) / 2;
final majorRadius = (Spaceship.size.x - 2) / 2;
createChain(
[
// TODO(alestiago): Try converting this logic to radian.
for (var angle = 20; angle <= 340; angle++)
Vector2(
minorRadius * cos(angle * pi / 180),
majorRadius * sin(angle * pi / 180),
),
],
);
}
}
/// {@template spaceship_wall}
/// A [BodyComponent] that provides the collision for the wall
/// surrounding the spaceship, with a small opening to allow the
@ -247,39 +228,11 @@ class SpaceshipWall extends BodyComponent with InitialPosition, Layered {
layer = Layer.spaceship;
}
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.components.spaceship.lower.path,
);
await add(
SpriteComponent(
sprite: sprite,
size: Vector2(Spaceship.radius * 2, Spaceship.radius + 1),
anchor: Anchor.center,
position: Vector2(-Spaceship.radius / 2, 0),
angle: 90 * pi / 180,
),
);
}
@override
Body createBody() {
renderBody = false;
final wallShape = ChainShape()
..createChain(
[
for (var angle = 20; angle <= 340; angle++)
Vector2(
Spaceship.radius * cos(angle * pi / 180),
Spaceship.radius * sin(angle * pi / 180),
),
],
);
final wallShape = _SpaceshipWallShape();
final bodyDef = BodyDef()
..userData = this

@ -1 +1,2 @@
export 'components/components.dart';
export 'flame/flame.dart';

@ -5,7 +5,7 @@ import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
class BasicGame extends Forge2DGame {
abstract class BasicGame extends Forge2DGame {
BasicGame() {
images.prefix = '';
}

@ -7,6 +7,7 @@
import 'package:dashbook/dashbook.dart';
import 'package:flutter/material.dart';
import 'package:sandbox/stories/effects/effects.dart';
import 'package:sandbox/stories/spaceship/spaceship.dart';
import 'package:sandbox/stories/stories.dart';
void main() {
@ -16,5 +17,6 @@ void main() {
addLayerStories(dashbook);
addEffectsStories(dashbook);
addFlipperStories(dashbook);
addSpaceshipStories(dashbook);
runApp(dashbook);
}

@ -0,0 +1,31 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
class BasicSpaceship extends BasicGame with TapDetector {
static String info = 'Renders a spaceship and allows balls to be '
'spawned upon click to test their interactions';
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2.zero());
unawaited(
addFromBlueprint(Spaceship(position: Vector2.zero())),
);
}
@override
void onTapUp(TapUpInfo info) {
add(
Ball(baseColor: Colors.blue)
..initialPosition = info.eventPosition.game
..layer = Layer.jetpack,
);
}
}

@ -0,0 +1,13 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/spaceship/basic.dart';
void addSpaceshipStories(Dashbook dashbook) {
dashbook.storiesOf('Spaceship').add(
'Basic',
(context) => GameWidget(game: BasicSpaceship()),
codeLink: buildSourceLink('spaceship/basic.dart'),
info: BasicSpaceship.info,
);
}

@ -1,5 +1,26 @@
import 'dart:ui';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
class MockCanvas extends Mock implements Canvas {}
class MockFilter extends Mock implements Filter {}
class MockFixture extends Mock implements Fixture {}
class MockBody extends Mock implements Body {}
class MockBall extends Mock implements Ball {}
class MockGame extends Mock implements Forge2DGame {}
class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {}
class MockSpaceshipHole extends Mock implements SpaceshipHole {}
class MockContact extends Mock implements Contact {}
class MockContactCallback extends Mock
implements ContactCallback<Object, Object> {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@ -2,8 +2,7 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockingjay/mockingjay.dart';
import 'package:pinball/game/game.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart';
@ -34,11 +33,11 @@ class TestRampOpeningBallContactCallback
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(PinballGameTest.create);
final flameTester = FlameTester(TestGame.new);
group('RampOpening', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(PinballGameTest.create);
final flameTester = FlameTester(TestGame.new);
flameTester.test(
'loads correctly',

@ -1,7 +1,10 @@
// ignore_for_file: cascade_invocations
import 'package:flame/game.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart';
@ -11,10 +14,10 @@ void main() {
late Filter filterData;
late Fixture fixture;
late Body body;
late PinballGame game;
late Ball ball;
late SpaceshipEntrance entrance;
late SpaceshipHole hole;
late Forge2DGame game;
setUp(() {
filterData = MockFilter();
@ -25,7 +28,7 @@ void main() {
body = MockBody();
when(() => body.fixtures).thenReturn([fixture]);
game = MockPinballGame();
game = MockGame();
ball = MockBall();
when(() => ball.gameRef).thenReturn(game);
@ -35,6 +38,27 @@ void main() {
hole = MockSpaceshipHole();
});
group('Spaceship', () {
testWidgets('renders correctly', (tester) async {
final game = TestGame();
// TODO(erickzanardo): This should be handled by flame test.
// refctor it when https://github.com/flame-engine/flame/pull/1501 is merged
await tester.runAsync(() async {
await tester.pumpWidget(GameWidget(game: game));
await game.ready();
await game.addFromBlueprint(Spaceship(position: Vector2(30, -30)));
await game.ready();
await tester.pump();
});
await expectLater(
find.byGame<Forge2DGame>(),
matchesGoldenFile('golden/spaceship.png'),
);
});
});
group('SpaceshipEntranceBallContactCallback', () {
test('changes the ball priority on contact', () {
SpaceshipEntranceBallContactCallback().begin(

@ -1,10 +1,9 @@
import 'package:flame/components.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/flame/blueprint.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import '../helpers/helpers.dart';
import '../../helpers/helpers.dart';
class MyBlueprint extends Blueprint {
@override
@ -52,19 +51,19 @@ void main() {
});
test('components can be added to it', () {
final blueprint = MyBlueprint()..build(MockPinballGame());
final blueprint = MyBlueprint()..build(MockGame());
expect(blueprint.components.length, equals(3));
});
test('blueprints can be added to it', () {
final blueprint = MyComposedBlueprint()..build(MockPinballGame());
final blueprint = MyComposedBlueprint()..build(MockGame());
expect(blueprint.blueprints.length, equals(3));
});
test('adds the components to a game on attach', () {
final mockGame = MockPinballGame();
final mockGame = MockGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyBlueprint().attach(mockGame);
@ -72,7 +71,7 @@ void main() {
});
test('adds components from a child Blueprint the to a game on attach', () {
final mockGame = MockPinballGame();
final mockGame = MockGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyComposedBlueprint().attach(mockGame);
@ -82,7 +81,7 @@ void main() {
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockPinballGame();
final mockGame = MockGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
final blueprint = MyBlueprint();
await blueprint.attach(mockGame);
@ -99,13 +98,13 @@ void main() {
});
test('callbacks can be added to it', () {
final blueprint = MyForge2dBlueprint()..build(MockPinballGame());
final blueprint = MyForge2dBlueprint()..build(MockGame());
expect(blueprint.callbacks.length, equals(3));
});
test('adds the callbacks to a game on attach', () async {
final mockGame = MockPinballGame();
final mockGame = MockGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
await MyForge2dBlueprint().attach(mockGame);
@ -116,7 +115,7 @@ void main() {
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockPinballGame();
final mockGame = MockGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
final blueprint = MyForge2dBlueprint();

@ -43,7 +43,6 @@ flutter:
assets:
- assets/images/components/
- assets/images/components/spaceship/
flutter_gen:
line_length: 80

Loading…
Cancel
Save