@ -0,0 +1,63 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// A [Component] that controls its game camera focus
|
||||
class CameraController extends Component with HasGameRef, KeyboardHandler {
|
||||
/// The camera position for the board
|
||||
static final zeroPosition = Vector2(0, -7.8);
|
||||
|
||||
/// The camera position for the pinball panel
|
||||
static final panelPosition = Vector2(0, -100.8);
|
||||
|
||||
/// The zoom value for the game mode
|
||||
late final double gameZoom;
|
||||
|
||||
/// The zoom value for the panel mode
|
||||
late final double panelZoom;
|
||||
bool _isFocusingOnBoard = false;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
gameZoom = gameRef.size.y / 16;
|
||||
panelZoom = gameRef.size.y / 12;
|
||||
|
||||
// Game starts with the camera focused on the panel
|
||||
gameRef.camera
|
||||
..speed = 200
|
||||
..followVector2(panelPosition)
|
||||
..zoom = panelZoom;
|
||||
}
|
||||
|
||||
/// Move the camera focus to the game board
|
||||
Future<void> focusOnBoard() async {
|
||||
final zoom = CameraZoom(value: gameZoom);
|
||||
unawaited(gameRef.add(zoom));
|
||||
await zoom.completed;
|
||||
gameRef.camera.moveTo(zeroPosition);
|
||||
}
|
||||
|
||||
// TODO(erickzanardo): Just for testing while
|
||||
// we don't get the panel designs, which will be
|
||||
// where this event will be generated from
|
||||
@override
|
||||
bool onKeyEvent(
|
||||
RawKeyEvent event,
|
||||
Set<LogicalKeyboardKey> keysPressed,
|
||||
) {
|
||||
if (!_isFocusingOnBoard) {
|
||||
if (event is RawKeyUpEvent &&
|
||||
event.logicalKey == LogicalKeyboardKey.enter) {
|
||||
_isFocusingOnBoard = true;
|
||||
focusOnBoard();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onKeyEvent(event, keysPressed);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 890 KiB |
@ -0,0 +1,38 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template camera_zoom}
|
||||
/// Applies zoom to the camera of the game where this is added to
|
||||
/// {@endtemplate}
|
||||
class CameraZoom extends CurveComponent with HasGameRef {
|
||||
/// {@macro camera_zoom}
|
||||
CameraZoom({
|
||||
required this.value,
|
||||
}) : super(
|
||||
curve: Curves.easeOut,
|
||||
duration: 0.4,
|
||||
);
|
||||
|
||||
/// The total zoom value to be applied to the camera
|
||||
final double value;
|
||||
|
||||
late Tween<double> _tween;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
_tween = Tween(
|
||||
begin: gameRef.camera.zoom,
|
||||
end: value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void apply(double progress) {
|
||||
gameRef.camera.zoom = _tween.transform(progress);
|
||||
}
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
export 'ball.dart';
|
||||
export 'board_side.dart';
|
||||
export 'camera_zoom.dart';
|
||||
export 'curve.dart';
|
||||
export 'fire_effect.dart';
|
||||
export 'flipper.dart';
|
||||
export 'flutter_sign_post.dart';
|
||||
export 'initial_position.dart';
|
||||
export 'joint_anchor.dart';
|
||||
export 'layer.dart';
|
||||
export 'panel.dart';
|
||||
export 'ramp_opening.dart';
|
||||
export 'shapes/shapes.dart';
|
||||
export 'spaceship.dart';
|
||||
|
@ -0,0 +1,46 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// {@template curve_component}
|
||||
/// A simple component that runs for the given [duration]
|
||||
/// following an animation curve.
|
||||
/// {@endtemplate}
|
||||
class CurveComponent extends Component {
|
||||
/// {@macro curve_component}
|
||||
CurveComponent({required this.curve, required this.duration});
|
||||
|
||||
/// Curve of this component
|
||||
final Curve curve;
|
||||
|
||||
/// How many seconds this curve lasts
|
||||
final double duration;
|
||||
|
||||
double _value = 0;
|
||||
|
||||
final _completer = Completer<void>();
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
_value += dt;
|
||||
|
||||
final progress = curve.transform(min(_value, duration) / duration);
|
||||
apply(progress);
|
||||
|
||||
if (progress == 1) {
|
||||
removeFromParent();
|
||||
_completer.complete();
|
||||
}
|
||||
}
|
||||
|
||||
/// Method called with the proggress (between 0 and 1) of the curve
|
||||
/// Override this to apply side effects to the game/components
|
||||
void apply(double progress) {}
|
||||
|
||||
/// A future that completes once the curve has completed.
|
||||
Future<void> get completed => _completer.future;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/gen/assets.gen.dart';
|
||||
|
||||
/// {@template panel}
|
||||
/// The vertical panel of the pinball
|
||||
/// {@endtemplate}
|
||||
class Panel extends SpriteComponent with HasGameRef {
|
||||
///{@macro panel}
|
||||
Panel({
|
||||
required Vector2 position,
|
||||
}) : super(
|
||||
// TODO(erickzanardo): https://github.com/flame-engine/flame/issues/1132
|
||||
position: position
|
||||
..clone().multiply(
|
||||
Vector2(1, -1),
|
||||
),
|
||||
size: Vector2(80, 60),
|
||||
anchor: Anchor.bottomCenter,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
sprite = await gameRef.loadSprite(Assets.images.panel.keyName);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/camera/zoom.dart';
|
||||
|
||||
void addCameraStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Camera').add(
|
||||
'Zoom',
|
||||
(context) => GameWidget(
|
||||
game: ZoomCameraGame(),
|
||||
),
|
||||
codeLink: buildSourceLink('panel/zoom.dart'),
|
||||
info: ZoomCameraGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
|
||||
class ZoomCameraGame extends BasicGame with TapDetector {
|
||||
static const info = 'Shows how the zoom works, tap to zoom in/out';
|
||||
|
||||
bool zoomed = false;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
camera.followVector2(Vector2.zero());
|
||||
unawaited(add(Panel(position: Vector2(0, -5))));
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
if (firstChild<CameraZoom>() == null) {
|
||||
unawaited(add(CameraZoom(value: zoomed ? 10 : 20)));
|
||||
zoomed = !zoomed;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
|
||||
class BasicPanelGame extends BasicGame {
|
||||
static const info = 'Simple example which renders the Panel';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
camera.followVector2(Vector2.zero());
|
||||
unawaited(add(Panel(position: Vector2(0, -5))));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/panel/basic.dart';
|
||||
|
||||
void addPanelStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Panel').add(
|
||||
'Basic',
|
||||
(context) => GameWidget(
|
||||
game: BasicPanelGame(),
|
||||
),
|
||||
codeLink: buildSourceLink('panel/basic.dart'),
|
||||
info: BasicPanelGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// 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() {
|
||||
group('CameraZoom', () {
|
||||
final tester = FlameTester(TestGame.new);
|
||||
|
||||
tester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
game.camera.zoom = 1;
|
||||
await game.ensureAdd(Panel(position: Vector2(0, 10)));
|
||||
await game.ensureAdd(CameraZoom(value: 8));
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/camera_zoom/no_zoom.png'),
|
||||
);
|
||||
|
||||
game.update(0.2);
|
||||
await tester.pump();
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/camera_zoom/in_between.png'),
|
||||
);
|
||||
|
||||
game.update(0.4);
|
||||
await tester.pump();
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/camera_zoom/finished.png'),
|
||||
);
|
||||
game.update(0.1);
|
||||
await tester.pump();
|
||||
|
||||
expect(game.firstChild<CameraZoom>(), isNull);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
void main() {
|
||||
group('CurveComponent', () {
|
||||
testWithFlameGame('is removed once it finishes', (game) async {
|
||||
final curve = CurveComponent(curve: Curves.linear, duration: 1);
|
||||
await game.ensureAdd(curve);
|
||||
|
||||
expect(game.firstChild<CurveComponent>(), isNotNull);
|
||||
|
||||
game.update(2);
|
||||
game.update(0);
|
||||
expect(game.firstChild<CurveComponent>(), isNull);
|
||||
});
|
||||
|
||||
testWithFlameGame('completed completes once it finishes', (game) async {
|
||||
final curve = CurveComponent(curve: Curves.linear, duration: 1);
|
||||
await game.ensureAdd(curve);
|
||||
|
||||
final completed = curve.completed;
|
||||
|
||||
game.update(2);
|
||||
expect(completed, completes);
|
||||
});
|
||||
});
|
||||
}
|
After Width: | Height: | Size: 783 KiB |
After Width: | Height: | Size: 785 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 693 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
@ -0,0 +1,29 @@
|
||||
// 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() {
|
||||
group('Panel', () {
|
||||
final tester = FlameTester(TestGame.new);
|
||||
|
||||
tester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
game.camera.zoom = 5;
|
||||
await game.ensureAdd(Panel(position: Vector2(0, 10)));
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/panel.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/components/camera_controller.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('CameraController', () {
|
||||
late FlameGame game;
|
||||
late CameraController controller;
|
||||
|
||||
setUp(() async {
|
||||
game = FlameGame()..onGameResize(Vector2(100, 200));
|
||||
|
||||
controller = CameraController();
|
||||
await game.ensureAdd(controller);
|
||||
});
|
||||
|
||||
test('loads correctly', () async {
|
||||
expect(game.firstChild<CameraController>(), isNotNull);
|
||||
});
|
||||
|
||||
test('correctly calculates the zooms', () async {
|
||||
expect(controller.gameZoom.toInt(), equals(12));
|
||||
expect(controller.panelZoom.toInt(), equals(16));
|
||||
});
|
||||
|
||||
test('correctly sets the initial zoom and position', () async {
|
||||
expect(game.camera.zoom, equals(controller.panelZoom));
|
||||
expect(game.camera.follow, equals(CameraController.panelPosition));
|
||||
});
|
||||
|
||||
group('focusOnBoard', () {
|
||||
test('changes the zoom', () async {
|
||||
unawaited(controller.focusOnBoard());
|
||||
|
||||
await game.ready();
|
||||
final zoom = game.firstChild<CameraZoom>();
|
||||
expect(zoom, isNotNull);
|
||||
expect(zoom?.value, equals(controller.gameZoom));
|
||||
});
|
||||
|
||||
test('moves the camera after the zoom is completed', () async {
|
||||
final future = controller.focusOnBoard();
|
||||
await game.ready();
|
||||
|
||||
game.update(10);
|
||||
|
||||
await future;
|
||||
|
||||
expect(game.camera.position, Vector2(-3, -106.8));
|
||||
});
|
||||
|
||||
test('moves the camera when enter is pressed', () async {
|
||||
testRawKeyUpEvents([LogicalKeyboardKey.enter], (key) async {
|
||||
controller.onKeyEvent(key, {});
|
||||
await game.ready();
|
||||
|
||||
game.update(10);
|
||||
|
||||
expect(game.camera.position, Vector2(-3, -106.8));
|
||||
});
|
||||
});
|
||||
|
||||
test('does nothing when another key is pressed', () async {
|
||||
testRawKeyUpEvents([LogicalKeyboardKey.keyA], (key) async {
|
||||
final originalPosition = game.camera.position;
|
||||
controller.onKeyEvent(key, {});
|
||||
await game.ready();
|
||||
|
||||
game.update(10);
|
||||
|
||||
expect(game.camera.position, originalPosition);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|