feat: adjusted Blueprint implementation

pull/212/head
alestiago 3 years ago
parent 58324adad6
commit 5b37577564

@ -1,6 +1,5 @@
// 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';
@ -11,20 +10,16 @@ import 'package:pinball_flame/pinball_flame.dart';
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<PinballGame> {
with Controls<SparkyComputerController> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() {
ControlledSparkyComputer()
: super(
components: [
SparkyTurboChargeSensor()..initialPosition = Vector2(-13, -49.8)
],
) {
controller = SparkyComputerController(this);
}
@override
void build(Forge2DGame _) {
addContactCallback(SparkyTurboChargeSensorBallContactCallback());
final sparkyTurboChargeSensor = SparkyTurboChargeSensor()
..initialPosition = Vector2(-13, -49.8);
add(sparkyTurboChargeSensor);
super.build(_);
}
}
/// {@template sparky_computer_controller}
@ -62,6 +57,12 @@ class SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(SparkyTurboChargeSensorBallContactCallback());
}
}
/// {@template sparky_turbo_charge_sensor_ball_contact_callback}

@ -7,21 +7,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
/// [LaunchRamp].
/// {@endtemplate}
class Launcher extends Forge2DBlueprint {
class Launcher extends Blueprint {
/// {@macro launcher}
Launcher();
/// [Plunger] to launch the [Ball] onto the board.
late final Plunger plunger;
@override
void build(Forge2DGame gameRef) {
plunger = ControlledPlunger(compressionDistance: 14)
..initialPosition = Vector2(40.7, 38);
final _rocket = RocketSpriteComponent()..position = Vector2(43, 62);
addAll([_rocket, plunger]);
addBlueprint(LaunchRamp());
}
Launcher()
: super(
components: [
ControlledPlunger(compressionDistance: 14)
..initialPosition = Vector2(40.7, 38),
RocketSpriteComponent()..position = Vector2(43, 62),
],
blueprints: [LaunchRamp()],
);
}

@ -55,6 +55,7 @@ class PinballGame extends Forge2DGame
unawaited(addFromBlueprint(launcher));
unawaited(add(Board()));
unawaited(add(AlienZone()));
unawaited(add(SparkyFireZone()));
unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls()));
@ -69,7 +70,7 @@ class PinballGame extends Forge2DGame
);
unawaited(addFromBlueprint(SpaceshipRail()));
controller.attachTo(launcher.plunger);
controller.attachTo(launcher.components.whereType<Plunger>().first);
await super.onLoad();
}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
@ -8,14 +6,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template boundaries}
/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary].
///{@endtemplate boundaries}
class Boundaries extends Forge2DBlueprint {
@override
void build(_) {
final bottomBoundary = _BottomBoundary();
final outerBoundary = _OuterBoundary();
addAll([outerBoundary, bottomBoundary]);
}
class Boundaries extends Blueprint {
/// {@macro boundaries}
Boundaries()
: super(
components: [
_BottomBoundary(),
_OuterBoundary(),
],
);
}
/// {@template bottom_boundary}

@ -1,5 +1,3 @@
// ignore_for_file: comment_references, avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
@ -11,17 +9,15 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template dinowalls}
/// A [Blueprint] which creates walls for the [ChromeDino].
/// {@endtemplate}
class DinoWalls extends Forge2DBlueprint {
class DinoWalls extends Blueprint {
/// {@macro dinowalls}
DinoWalls();
@override
void build(_) {
addAll([
DinoWalls()
: super(
components: [
_DinoTopWall(),
_DinoBottomWall(),
]);
}
],
);
}
/// {@template dino_top_wall}

@ -11,30 +11,18 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [_LaunchRampBase] and
/// [_LaunchRampForegroundRailing].
/// {@endtemplate}
class LaunchRamp extends Forge2DBlueprint {
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_LaunchRampExit>(),
]);
final launchRampBase = _LaunchRampBase();
final launchRampForegroundRailing = _LaunchRampForegroundRailing();
final launchRampExit = _LaunchRampExit(rotation: math.pi / 2)
..initialPosition = Vector2(0.6, -34);
final launchRampCloseWall = _LaunchRampCloseWall()
..initialPosition = Vector2(4, -69.5);
addAll([
launchRampBase,
launchRampForegroundRailing,
launchRampExit,
launchRampCloseWall,
]);
}
class LaunchRamp extends Blueprint {
/// {@macro launch_ramp}
LaunchRamp()
: super(
components: [
// TODO(alestiago): Is it using initialPosition?
_LaunchRampBase(),
_LaunchRampForegroundRailing(),
_LaunchRampExit()..initialPosition = Vector2(0.6, -34),
_LaunchRampCloseWall()..initialPosition = Vector2(4, -69.5),
],
);
}
/// {@template launch_ramp_base}
@ -125,6 +113,13 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered {
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef
.addContactCallback(LayerSensorBallContactCallback<_LaunchRampExit>());
}
}
class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef {
@ -258,10 +253,8 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered {
/// {@endtemplate}
class _LaunchRampExit extends LayerSensor {
/// {@macro launch_ramp_exit}
_LaunchRampExit({
required double rotation,
}) : _rotation = rotation,
super(
_LaunchRampExit()
: super(
insideLayer: Layer.launcher,
outsideLayer: Layer.board,
orientation: LayerEntranceOrientation.down,
@ -272,8 +265,6 @@ class _LaunchRampExit extends LayerSensor {
renderBody = false;
}
final double _rotation;
static final Vector2 _size = Vector2(1.6, 0.1);
@override
@ -282,6 +273,6 @@ class _LaunchRampExit extends LayerSensor {
_size.x,
_size.y,
initialPosition,
_rotation,
math.pi / 2,
);
}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
@ -9,26 +7,23 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of
/// the board.
/// {@endtemplate}
class Slingshots extends Forge2DBlueprint {
@override
void build(_) {
final upperSlingshot = Slingshot(
class Slingshots extends Blueprint {
/// {@macro slingshots}
Slingshots()
: super(
components: [
Slingshot(
length: 5.64,
angle: -0.017,
spritePath: Assets.images.slingshot.upper.keyName,
)..initialPosition = Vector2(22.3, -1.58);
final lowerSlingshot = Slingshot(
)..initialPosition = Vector2(22.3, -1.58),
Slingshot(
length: 3.46,
angle: -0.468,
spritePath: Assets.images.slingshot.lower.keyName,
)..initialPosition = Vector2(24.7, 6.2);
addAll([
upperSlingshot,
lowerSlingshot,
]);
}
)..initialPosition = Vector2(24.7, 6.2),
],
);
}
/// {@template slingshot}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'dart:math';
@ -12,24 +10,11 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
/// {@endtemplate}
class Spaceship extends Forge2DBlueprint {
class Spaceship extends Blueprint {
/// {@macro spaceship}
Spaceship({required this.position});
/// Total size of the spaceship.
static final size = Vector2(25, 19);
/// The [position] where the elements will be created
final Vector2 position;
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipEntrance>(),
LayerSensorBallContactCallback<_SpaceshipHole>(),
]);
addAll([
Spaceship({required Vector2 position})
: super(
components: [
SpaceshipSaucer()..initialPosition = position,
_SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position,
@ -42,8 +27,11 @@ class Spaceship extends Forge2DBlueprint {
outsidePriority: RenderPriority.ballOnBoard,
)..initialPosition = position - Vector2(-7.2, -0.8),
SpaceshipWall()..initialPosition = position,
]);
}
],
);
/// Total size of the spaceship.
static final size = Vector2(25, 19);
}
/// {@template spaceship_saucer}
@ -51,26 +39,28 @@ class Spaceship extends Forge2DBlueprint {
/// {@endtemplate}
class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_saucer}
SpaceshipSaucer() : super(priority: RenderPriority.spaceshipSaucer) {
SpaceshipSaucer()
: super(
priority: RenderPriority.spaceshipSaucer,
children: [
_SpaceshipSaucerSpriteComponent(),
],
) {
layer = Layer.spaceship;
renderBody = false;
}
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.spaceship.saucer.keyName,
);
await add(
SpriteComponent(
sprite: sprite,
size: Spaceship.size,
anchor: Anchor.center,
),
gameRef
..addContactCallback(
LayerSensorBallContactCallback<_SpaceshipEntrance>(),
)
..addContactCallback(
LayerSensorBallContactCallback<_SpaceshipHole>(),
);
renderBody = false;
}
@override
@ -89,6 +79,25 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
}
}
class _SpaceshipSaucerSpriteComponent extends SpriteComponent with HasGameRef {
_SpaceshipSaucerSpriteComponent()
: super(
anchor: Anchor.center,
// TODO(alestiago): Refactor to use sprite orignial size instead.
size: Spaceship.size,
);
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Use cached sprite.
sprite = await gameRef.loadSprite(
Assets.images.spaceship.saucer.keyName,
);
}
}
/// {@template spaceship_bridge}
/// A [BodyComponent] that provides both the collision and the rotation
/// animation for the bridge.

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'package:flame/components.dart';
@ -11,38 +9,32 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail}
/// A [Blueprint] for the spaceship drop tube.
/// {@endtemplate}
class SpaceshipRail extends Forge2DBlueprint {
class SpaceshipRail extends Blueprint {
/// {@macro spaceship_rail}
SpaceshipRail();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRailExit>(),
]);
final railRamp = _SpaceshipRailRamp();
final railEnd = _SpaceshipRailExit();
final topBase = _SpaceshipRailBase(radius: 0.55)
..initialPosition = Vector2(-26.15, -18.65);
final bottomBase = _SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, 12.9);
final railForeground = _SpaceshipRailForeground();
addAll([
railRamp,
railEnd,
topBase,
bottomBase,
railForeground,
]);
}
SpaceshipRail()
: super(
components: [
// TODO(alestiago): Investigate if it's using initial position.
_SpaceshipRailRamp(),
_SpaceshipRailExit(),
_SpaceshipRailBase(radius: 0.55)
..initialPosition = Vector2(-26.15, -18.65),
_SpaceshipRailBase(radius: 0.8)
..initialPosition = Vector2(-25.5, 12.9),
_SpaceshipRailForeground()
],
);
}
/// Represents the spaceship drop rail from the [Spaceship].
class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
_SpaceshipRailRamp() : super(priority: RenderPriority.spaceshipRail) {
_SpaceshipRailRamp()
: super(
priority: RenderPriority.spaceshipRail,
children: [_SpaceshipRailRampSpriteComponent()],
) {
layer = Layer.spaceshipExitRail;
renderBody = false;
}
List<FixtureDef> _createFixtureDefs() {
@ -134,9 +126,9 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await add(_SpaceshipRailRampSpriteComponent());
gameRef.addContactCallback(
LayerSensorBallContactCallback<_SpaceshipRailExit>(),
);
}
}

@ -1,5 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:math' as math;
import 'package:flame/components.dart';
@ -11,56 +9,45 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [Spaceship].
/// {@endtemplate}
class SpaceshipRamp extends Forge2DBlueprint {
class SpaceshipRamp extends Blueprint {
/// {@macro spaceship_ramp}
SpaceshipRamp();
@override
void build(_) {
addAllContactCallback([
LayerSensorBallContactCallback<_SpaceshipRampOpening>(),
]);
final rightOpening = _SpaceshipRampOpening(
SpaceshipRamp()
: super(
components: [
_SpaceshipRampOpening(
outsidePriority: RenderPriority.ballOnBoard,
rotation: math.pi,
)
..initialPosition = Vector2(1.7, -19.8)
..layer = Layer.opening;
final leftOpening = _SpaceshipRampOpening(
..layer = Layer.opening,
_SpaceshipRampOpening(
outsideLayer: Layer.spaceship,
outsidePriority: RenderPriority.ballOnSpaceship,
rotation: math.pi,
)
..initialPosition = Vector2(-13.7, -18.6)
..layer = Layer.spaceshipEntranceRamp;
final spaceshipRamp = _SpaceshipRampBackground();
final spaceshipRampBoardOpeningSprite =
..layer = Layer.spaceshipEntranceRamp,
_SpaceshipRampBackground(),
_SpaceshipRampBoardOpeningSpriteComponent()
..position = Vector2(3.4, -39.5);
final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing();
final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20);
addAll([
spaceshipRampBoardOpeningSprite,
rightOpening,
leftOpening,
baseRight,
..position = Vector2(3.4, -39.5),
_SpaceshipRampForegroundRailing(),
_SpaceshipRampBase()..initialPosition = Vector2(1.7, -20),
_SpaceshipRampBackgroundRailingSpriteComponent(),
spaceshipRamp,
spaceshipRampForegroundRailing,
]);
}
],
);
}
class _SpaceshipRampBackground extends BodyComponent
with InitialPosition, Layered {
_SpaceshipRampBackground() : super(priority: RenderPriority.spaceshipRamp) {
_SpaceshipRampBackground()
: super(
priority: RenderPriority.spaceshipRamp,
children: [
_SpaceshipRampBackgroundRampSpriteComponent(),
],
) {
layer = Layer.spaceshipEntranceRamp;
renderBody = false;
}
/// Width between walls of the ramp.
@ -116,16 +103,19 @@ class _SpaceshipRampBackground extends BodyComponent
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await add(_SpaceshipRampBackgroundRampSpriteComponent());
gameRef.addContactCallback(
LayerSensorBallContactCallback<_SpaceshipRampOpening>(),
);
}
}
class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
with HasGameRef {
_SpaceshipRampBackgroundRailingSpriteComponent()
: super(priority: RenderPriority.spaceshipRampBackgroundRailing);
: super(
priority: RenderPriority.spaceshipRampBackgroundRailing,
);
@override
Future<void> onLoad() async {
await super.onLoad();

@ -9,17 +9,17 @@ import 'package:pinball_flame/pinball_flame.dart';
/// A [Blueprint] which creates the [_ComputerBase] and
/// [_ComputerTopSpriteComponent].
/// {@endtemplate}
class SparkyComputer extends Forge2DBlueprint {
@override
void build(_) {
final computerBase = _ComputerBase();
final computerTop = _ComputerTopSpriteComponent();
addAll([
computerBase,
computerTop,
]);
}
class SparkyComputer extends Blueprint {
/// {@macro sparky_computer}
SparkyComputer({
Iterable<Component>? components,
}) : super(
components: [
_ComputerBase(),
_ComputerTopSpriteComponent(),
if (components != null) ...components,
],
);
}
class _ComputerBase extends BodyComponent with InitialPosition {

@ -1,101 +1,47 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/foundation.dart';
const _attachedErrorMessage = "Can't add to attached Blueprints";
// TODO(erickzanardo): Keeping this inside our code base
// so we can experiment with the idea, but this is a
// potential upstream change on Flame.
/// A [Blueprint] is a virtual way of grouping [Component]s
/// that are related, but they need to be added directly on
/// the [FlameGame] level.
/// {@template blueprint}
/// A [Blueprint] is a virtual way of grouping [Component]s that are related,
/// but they need to be added directly on the [FlameGame] level.
/// {@endtemplate blueprint}
// TODO(alestiago): refactor with feat/make-blueprint-extend-component.
abstract class Blueprint<T extends FlameGame> extends Component {
final List<Component> _components = [];
final List<Blueprint> _blueprints = [];
bool _isAttached = false;
/// Called before the the [Component]s managed
/// by this blueprint is added to the [FlameGame]
void build(T gameRef);
/// Attach the [Component]s built on [build] to the [game]
/// instance
@mustCallSuper
Future<void> attach(T game) async {
build(game);
await Future.wait([
game.addAll(_components),
..._blueprints.map(game.addFromBlueprint).toList(),
]);
_isAttached = true;
class Blueprint extends Component {
/// {@macro blueprint}
Blueprint({
Iterable<Component>? components,
Iterable<Blueprint>? blueprints,
}) {
if (components != null) _components.addAll(components);
if (blueprints != null) {
for (final blueprint in blueprints) {
_components.addAll(blueprint.components);
}
/// Adds a single [Component] to this blueprint.
@override
Future<void> add(Component component) async {
assert(!_isAttached, _attachedErrorMessage);
_components.add(component);
}
/// Adds a list of [Blueprint]s to this blueprint.
void addAllBlueprints(List<Blueprint> blueprints) {
assert(!_isAttached, _attachedErrorMessage);
_blueprints.addAll(blueprints);
}
/// Adds a single [Blueprint] to this blueprint.
void addBlueprint(Blueprint blueprint) {
assert(!_isAttached, _attachedErrorMessage);
_blueprints.add(blueprint);
}
/// Returns a copy of the components built by this blueprint
List<Component> get components => List.unmodifiable(_components);
/// Returns a copy of the children blueprints
List<Blueprint> get blueprints => List.unmodifiable(_blueprints);
}
/// A [Blueprint] that provides additional
/// structures specific to flame_forge2d
abstract class Forge2DBlueprint extends Blueprint<Forge2DGame> {
final List<ContactCallback> _callbacks = [];
/// Adds a single [ContactCallback] to this blueprint
void addContactCallback(ContactCallback callback) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.add(callback);
}
/// Adds a collection of [ContactCallback]s to this blueprint
void addAllContactCallback(List<ContactCallback> callbacks) {
assert(!_isAttached, _attachedErrorMessage);
_callbacks.addAll(callbacks);
}
@override
Future<void> attach(Forge2DGame game) async {
await super.attach(game);
final List<Component> _components = [];
for (final callback in _callbacks) {
game.addContactCallback(callback);
}
/// Attaches children.
@mustCallSuper
Future<void> _attach(Component parent) async {
await parent.addAll(_components);
}
/// Returns a copy of the callbacks built by this blueprint
List<ContactCallback> get callbacks => List.unmodifiable(_callbacks);
/// Returns a copy of the components built by this blueprint.
List<Component> get components => List.unmodifiable(_components);
}
/// Adds helper methods regardin [Blueprint]s to [FlameGame]
extension FlameGameBlueprint on FlameGame {
/// Adds helper methods regarding [Blueprint]s to [FlameGame].
extension FlameGameBlueprint on Component {
/// Shortcut to attach a [Blueprint] instance to this game
/// equivalent to `MyBluepinrt().attach(game)`
Future<void> addFromBlueprint(Blueprint blueprint) async {
await blueprint.attach(this);
await blueprint._attach(this);
}
}

@ -1,138 +1,57 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame_forge2d/contact_callbacks.dart';
import 'package:flame/game.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../helpers/helpers.dart';
class TestContactCallback extends ContactCallback<dynamic, dynamic> {}
class MyBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
addAll([Component(), Component()]);
}
}
class MyOtherBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
}
}
class YetMyOtherBlueprint extends Blueprint {
@override
void build(_) {
add(Component());
}
}
class MyComposedBlueprint extends Blueprint {
@override
void build(_) {
addBlueprint(MyBlueprint());
addAllBlueprints([MyOtherBlueprint(), YetMyOtherBlueprint()]);
}
}
class MyForge2dBlueprint extends Forge2DBlueprint {
@override
void build(_) {
addContactCallback(MockContactCallback());
addAllContactCallback([MockContactCallback(), MockContactCallback()]);
}
}
void main() {
group('Blueprint', () {
setUpAll(() {
registerFallbackValue(MyBlueprint());
registerFallbackValue(Component());
});
test('components can be added to it', () {
final blueprint = MyBlueprint()..build(MockForge2DGame());
expect(blueprint.components.length, equals(3));
});
test('blueprints can be added to it', () {
final blueprint = MyComposedBlueprint()..build(MockForge2DGame());
expect(blueprint.blueprints.length, equals(3));
});
test('adds the components to a game on attach', () {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyBlueprint().attach(mockGame);
TestWidgetsFlutterBinding.ensureInitialized();
verify(() => mockGame.addAll(any())).called(1);
});
test('adds components from a child Blueprint the to a game on attach', () {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
MyComposedBlueprint().attach(mockGame);
verify(() => mockGame.addAll(any())).called(4);
});
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
final blueprint = MyBlueprint();
await blueprint.attach(mockGame);
expect(() => blueprint.add(Component()), throwsAssertionError);
expect(() => blueprint.addAll([Component()]), throwsAssertionError);
},
group('Blueprint', () {
final flameTester = FlameTester(FlameGame.new);
flameTester.test('adds the components to parent on attach', (game) async {
final blueprint = Blueprint(
components: [
Component(),
Component(),
],
);
await game.addFromBlueprint(blueprint);
expect(game.children, equals(blueprint.components));
});
flameTester
.test('adds components from a child Blueprint the to a game on attach',
(game) async {
final childBlueprint = Blueprint(
components: [
Component(),
Component(),
],
);
final parentBlueprint = Blueprint(
components: [
Component(),
Component(),
],
blueprints: [
childBlueprint,
],
);
});
group('Forge2DBlueprint', () {
setUpAll(() {
registerFallbackValue(TestContactCallback());
});
test('callbacks can be added to it', () {
final blueprint = MyForge2dBlueprint()..build(MockForge2DGame());
expect(blueprint.callbacks.length, equals(3));
});
test('adds the callbacks to a game on attach', () async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
await MyForge2dBlueprint().attach(mockGame);
verify(() => mockGame.addContactCallback(any())).called(3);
});
test(
'throws assertion error when adding to an already attached blueprint',
() async {
final mockGame = MockForge2DGame();
when(() => mockGame.addAll(any())).thenAnswer((_) async {});
when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {});
final blueprint = MyForge2dBlueprint();
await blueprint.attach(mockGame);
await game.addFromBlueprint(parentBlueprint);
await game.ready();
expect(
() => blueprint.addContactCallback(MockContactCallback()),
throwsAssertionError,
);
expect(
() => blueprint.addAllContactCallback([MockContactCallback()]),
throwsAssertionError,
);
},
game.children,
equals([
...parentBlueprint.components,
...childBlueprint.components,
]),
);
});
});
}

Loading…
Cancel
Save