feat: implemented Flippers behaviors

pull/343/head
alestiago 3 years ago
parent 0595e82649
commit 424f35a958

@ -1,6 +1,5 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
@ -41,7 +40,7 @@ class _BottomGroupSide extends Component {
final direction = _side.direction; final direction = _side.direction;
final centerXAdjustment = _side.isLeft ? 0 : -6.66; final centerXAdjustment = _side.isLeft ? 0 : -6.66;
final flipper = ControlledFlipper( final flipper = Flipper(
side: _side, side: _side,
)..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, 43.6); )..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, 43.6);
final baseboard = Baseboard(side: _side) final baseboard = Baseboard(side: _side)

@ -3,7 +3,6 @@ export 'backbox/backbox.dart';
export 'bottom_group.dart'; export 'bottom_group.dart';
export 'camera_controller.dart'; export 'camera_controller.dart';
export 'controlled_ball.dart'; export 'controlled_ball.dart';
export 'controlled_flipper.dart';
export 'controlled_plunger.dart'; export 'controlled_plunger.dart';
export 'dino_desert/dino_desert.dart'; export 'dino_desert/dino_desert.dart';
export 'drain.dart'; export 'drain.dart';

@ -14,7 +14,7 @@ export 'dash_nest_bumper/dash_nest_bumper.dart';
export 'dino_walls.dart'; export 'dino_walls.dart';
export 'fire_effect.dart'; export 'fire_effect.dart';
export 'flapper/flapper.dart'; export 'flapper/flapper.dart';
export 'flipper.dart'; export 'flipper/flipper.dart';
export 'google_letter/google_letter.dart'; export 'google_letter/google_letter.dart';
export 'initial_position.dart'; export 'initial_position.dart';
export 'joint_anchor.dart'; export 'joint_anchor.dart';

@ -0,0 +1,2 @@
export 'flipper_jointing_behavior.dart';
export 'flipper_key_listening_behavior.dart';

@ -0,0 +1,103 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Joints the [Flipper] to allow pivoting around one end.
class FlipperJointingBehavior extends Component
with ParentIsA<Flipper>, HasGameRef {
late final RevoluteJoint _joint;
@override
Future<void> onLoad() async {
await super.onLoad();
final anchor = _FlipperAnchor(flipper: parent);
await add(anchor);
final jointDef = _FlipperAnchorRevoluteJointDef(
flipper: parent,
anchor: anchor,
);
_joint = _FlipperJoint(jointDef);
parent.world.createJoint(_joint);
}
@override
void onMount() {
gameRef.ready().whenComplete(
() => parent.body.joints.whereType<_FlipperJoint>().first.unlock(),
);
}
}
/// {@template flipper_anchor}
/// [JointAnchor] positioned at the end of a [Flipper].
///
/// The end of a [Flipper] depends on its [Flipper.side].
/// {@endtemplate}
class _FlipperAnchor extends JointAnchor {
/// {@macro flipper_anchor}
_FlipperAnchor({
required Flipper flipper,
}) {
initialPosition = Vector2(
(Flipper.size.x * flipper.side.direction) / 2 -
(1.65 * flipper.side.direction),
-0.15,
);
}
}
/// {@template flipper_anchor_revolute_joint_def}
/// Hinges one end of [Flipper] to a [_FlipperAnchor] to achieve a potivoting
/// motion.
/// {@endtemplate}
class _FlipperAnchorRevoluteJointDef extends RevoluteJointDef {
/// {@macro flipper_anchor_revolute_joint_def}
_FlipperAnchorRevoluteJointDef({
required Flipper flipper,
required _FlipperAnchor anchor,
}) : side = flipper.side {
enableLimit = true;
initialize(
flipper.body,
anchor.body,
flipper.body.position + anchor.body.position,
);
}
final BoardSide side;
}
/// {@template flipper_joint}
/// [RevoluteJoint] that controls the pivoting motion of a [Flipper].
/// {@endtemplate}
class _FlipperJoint extends RevoluteJoint {
/// {@macro flipper_joint}
_FlipperJoint(_FlipperAnchorRevoluteJointDef def)
: side = def.side,
super(def) {
lock();
}
/// Half the angle of the arc motion.
static const _halfSweepingAngle = 0.611;
final BoardSide side;
/// Locks the [Flipper] to its resting position.
///
/// The joint is locked when initialized in order to force the [Flipper]
/// at its resting position.
void lock() {
final angle = _halfSweepingAngle * side.direction;
setLimits(angle, angle);
}
/// Unlocks the [Flipper] from its resting position.
void unlock() {
const angle = _halfSweepingAngle;
setLimits(-angle, angle);
}
}

@ -1,49 +1,33 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// {@template controlled_flipper} /// Allows controlling the [Flipper]'s movement with keyboard input.
/// A [Flipper] with a [FlipperController] attached. class FlipperKeyListeningBehavior extends Component
/// {@endtemplate} with KeyboardHandler, ParentIsA<Flipper> {
class ControlledFlipper extends Flipper with Controls<FlipperController> {
/// {@macro controlled_flipper}
ControlledFlipper({
required BoardSide side,
}) : super(side: side) {
controller = FlipperController(this);
}
}
/// {@template flipper_controller}
/// A [ComponentController] that controls a [Flipper]s movement.
/// {@endtemplate}
class FlipperController extends ComponentController<Flipper>
with KeyboardHandler, BlocComponent<GameBloc, GameState> {
/// {@macro flipper_controller}
FlipperController(Flipper flipper)
: _keys = flipper.side.flipperKeys,
super(flipper);
/// The [LogicalKeyboardKey]s that will control the [Flipper]. /// The [LogicalKeyboardKey]s that will control the [Flipper].
/// ///
/// [onKeyEvent] method listens to when one of these keys is pressed. /// [onKeyEvent] method listens to when one of these keys is pressed.
final List<LogicalKeyboardKey> _keys; late final List<LogicalKeyboardKey> _keys;
@override
Future<void> onLoad() async {
await super.onLoad();
_keys = parent.side.flipperKeys;
}
@override @override
bool onKeyEvent( bool onKeyEvent(
RawKeyEvent event, RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed, Set<LogicalKeyboardKey> keysPressed,
) { ) {
if (state?.isGameOver ?? false) return true;
if (!_keys.contains(event.logicalKey)) return true; if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) { if (event is RawKeyDownEvent) {
component.moveUp(); parent.moveUp();
} else if (event is RawKeyUpEvent) { } else if (event is RawKeyUpEvent) {
component.moveDown(); parent.moveDown();
} }
return false; return false;

@ -4,6 +4,8 @@ import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
export 'behaviors/behaviors.dart';
/// {@template flipper} /// {@template flipper}
/// A bat, typically found in pairs at the bottom of the board. /// A bat, typically found in pairs at the bottom of the board.
/// ///
@ -15,7 +17,11 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
required this.side, required this.side,
}) : super( }) : super(
renderBody: false, renderBody: false,
children: [_FlipperSpriteComponent(side: side)], children: [
_FlipperSpriteComponent(side: side),
FlipperJointingBehavior(),
FlipperKeyListeningBehavior(),
],
); );
/// The size of the [Flipper]. /// The size of the [Flipper].
@ -44,19 +50,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
body.linearVelocity = Vector2(0, -_speed); body.linearVelocity = Vector2(0, -_speed);
} }
/// Anchors the [Flipper] to the [RevoluteJoint] that controls its arc motion.
Future<void> _anchorToJoint() async {
final anchor = _FlipperAnchor(flipper: this);
await add(anchor);
final jointDef = _FlipperAnchorRevoluteJointDef(
flipper: this,
anchor: anchor,
);
final joint = _FlipperJoint(jointDef);
world.createJoint(joint);
}
List<FixtureDef> _createFixtureDefs() { List<FixtureDef> _createFixtureDefs() {
final direction = side.direction; final direction = side.direction;
@ -73,7 +66,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
assetShadow, assetShadow,
0, 0,
); );
final bigCircleFixtureDef = FixtureDef(bigCircleShape);
final smallCircleShape = CircleShape()..radius = size.y * 0.23; final smallCircleShape = CircleShape()..radius = size.y * 0.23;
smallCircleShape.position.setValues( smallCircleShape.position.setValues(
@ -82,7 +74,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
assetShadow, assetShadow,
0, 0,
); );
final smallCircleFixtureDef = FixtureDef(smallCircleShape);
final trapeziumVertices = side.isLeft final trapeziumVertices = side.isLeft
? [ ? [
@ -98,26 +89,18 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
Vector2(smallCircleShape.position.x, -smallCircleShape.radius), Vector2(smallCircleShape.position.x, -smallCircleShape.radius),
]; ];
final trapezium = PolygonShape()..set(trapeziumVertices); final trapezium = PolygonShape()..set(trapeziumVertices);
final trapeziumFixtureDef = FixtureDef(
trapezium,
density: 50, // TODO(alestiago): Use a proper density.
friction: .1, // TODO(alestiago): Use a proper friction.
);
return [ return [
bigCircleFixtureDef, FixtureDef(bigCircleShape),
smallCircleFixtureDef, FixtureDef(smallCircleShape),
trapeziumFixtureDef, FixtureDef(
trapezium,
density: 50, // TODO(alestiago): Use a proper density.
friction: .1, // TODO(alestiago): Use a proper friction.
),
]; ];
} }
@override
Future<void> onLoad() async {
await super.onLoad();
await _anchorToJoint();
}
@override @override
Body createBody() { Body createBody() {
final bodyDef = BodyDef( final bodyDef = BodyDef(
@ -131,15 +114,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition {
return body; return body;
} }
@override
void onMount() {
super.onMount();
gameRef.ready().whenComplete(
() => body.joints.whereType<_FlipperJoint>().first.unlock(),
);
}
} }
class _FlipperSpriteComponent extends SpriteComponent with HasGameRef { class _FlipperSpriteComponent extends SpriteComponent with HasGameRef {
@ -163,73 +137,3 @@ class _FlipperSpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
} }
} }
/// {@template flipper_anchor}
/// [JointAnchor] positioned at the end of a [Flipper].
///
/// The end of a [Flipper] depends on its [Flipper.side].
/// {@endtemplate}
class _FlipperAnchor extends JointAnchor {
/// {@macro flipper_anchor}
_FlipperAnchor({
required Flipper flipper,
}) {
initialPosition = Vector2(
(Flipper.size.x * flipper.side.direction) / 2 -
(1.65 * flipper.side.direction),
-0.15,
);
}
}
/// {@template flipper_anchor_revolute_joint_def}
/// Hinges one end of [Flipper] to a [_FlipperAnchor] to achieve an arc motion.
/// {@endtemplate}
class _FlipperAnchorRevoluteJointDef extends RevoluteJointDef {
/// {@macro flipper_anchor_revolute_joint_def}
_FlipperAnchorRevoluteJointDef({
required Flipper flipper,
required _FlipperAnchor anchor,
}) : side = flipper.side {
enableLimit = true;
initialize(
flipper.body,
anchor.body,
flipper.body.position + anchor.body.position,
);
}
final BoardSide side;
}
/// {@template flipper_joint}
/// [RevoluteJoint] that controls the arc motion of a [Flipper].
/// {@endtemplate}
class _FlipperJoint extends RevoluteJoint {
/// {@macro flipper_joint}
_FlipperJoint(_FlipperAnchorRevoluteJointDef def)
: side = def.side,
super(def) {
lock();
}
/// Half the angle of the arc motion.
static const _halfSweepingAngle = 0.611;
final BoardSide side;
/// Locks the [Flipper] to its resting position.
///
/// The joint is locked when initialized in order to force the [Flipper]
/// at its resting position.
void lock() {
final angle = _halfSweepingAngle * side.direction;
setLimits(angle, angle);
}
/// Unlocks the [Flipper] from its resting position.
void unlock() {
const angle = _halfSweepingAngle;
setLimits(-angle, angle);
}
}

@ -1,6 +1,4 @@
import 'package:flame/input.dart'; import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart';
@ -23,16 +21,6 @@ class FlipperGame extends BallGame with KeyboardEvents {
- Press right arrow key or "D" to move the right flipper. - Press right arrow key or "D" to move the right flipper.
'''; ''';
static const _leftFlipperKeys = [
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.keyA,
];
static const _rightFlipperKeys = [
LogicalKeyboardKey.arrowRight,
LogicalKeyboardKey.keyD,
];
late Flipper leftFlipper; late Flipper leftFlipper;
late Flipper rightFlipper; late Flipper rightFlipper;
@ -50,32 +38,4 @@ class FlipperGame extends BallGame with KeyboardEvents {
await traceAllBodies(); await traceAllBodies();
} }
@override
KeyEventResult onKeyEvent(
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
final movedLeftFlipper = _leftFlipperKeys.contains(event.logicalKey);
if (movedLeftFlipper) {
if (event is RawKeyDownEvent) {
leftFlipper.moveUp();
} else if (event is RawKeyUpEvent) {
leftFlipper.moveDown();
}
}
final movedRightFlipper = _rightFlipperKeys.contains(event.logicalKey);
if (movedRightFlipper) {
if (event is RawKeyDownEvent) {
rightFlipper.moveUp();
} else if (event is RawKeyUpEvent) {
rightFlipper.moveDown();
}
}
return movedLeftFlipper || movedRightFlipper
? KeyEventResult.handled
: KeyEventResult.ignored;
}
} }

@ -6,7 +6,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme; import 'package:pinball_theme/pinball_theme.dart' as theme;
import '../../helpers/helpers.dart'; import '../../../helpers/helpers.dart';
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
Loading…
Cancel
Save