diff --git a/lib/game/components/backbox/backbox.dart b/lib/game/components/backbox/backbox.dart index b3743df3..2563bf21 100644 --- a/lib/game/components/backbox/backbox.dart +++ b/lib/game/components/backbox/backbox.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; import 'package:pinball/game/components/backbox/displays/displays.dart'; +import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; @@ -12,7 +13,7 @@ import 'package:pinball_theme/pinball_theme.dart' hide Assets; /// {@template backbox} /// The [Backbox] of the pinball machine. /// {@endtemplate} -class Backbox extends PositionComponent with ZIndex { +class Backbox extends PositionComponent with ZIndex, HasGameRef { /// {@macro backbox} Backbox({ required LeaderboardRepository leaderboardRepository, @@ -58,6 +59,8 @@ class Backbox extends PositionComponent with ZIndex { } else if (state is LeaderboardSuccessState) { _display.add(LeaderboardDisplay(entries: state.entries)); } else if (state is InitialsFormState) { + // TODO check + gameRef.overlays.add(PinballGame.mobileControlsOverlay); _display.add( InitialsInputDisplay( score: state.score, diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 503d6261..8da327e8 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -7,6 +7,8 @@ import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; @@ -38,6 +40,9 @@ class PinballGame extends PinballForge2DGame /// Identifier of the play button overlay static const playButtonOverlay = 'play_button'; + /// Identifier of the mobile controls overlay + static const mobileControlsOverlay = 'mobile_controls'; + @override Color backgroundColor() => Colors.transparent; @@ -112,6 +117,16 @@ class PinballGame extends PinballForge2DGame final focusedBoardSide = {}; + void triggerVirtualKeyUp(LogicalKeyboardKey key) { + final keyControllers = descendants().whereType(); + + for (final controller in keyControllers) { + if (!controller.onVirtualKeyUp(key)) { + break; + } + } + } + @override void onTapDown(int pointerId, TapDownInfo info) { if (info.raw.kind == PointerDeviceKind.touch) { diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 076ed336..898c5d38 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -3,6 +3,7 @@ import 'package:flame/game.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/assets_manager/assets_manager.dart'; @@ -137,6 +138,36 @@ class PinballGameLoadedView extends StatelessWidget { child: PlayButtonOverlay(), ); }, + PinballGame.mobileControlsOverlay: (context, game) { + return Positioned( + bottom: 0, + left: 0, + right: 0, + child: MobileControls( + onTapUp: () { + game.triggerVirtualKeyUp(LogicalKeyboardKey.arrowUp); + }, + onTapDown: () { + game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowDown, + ); + }, + onTapLeft: () { + game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowLeft, + ); + }, + onTapRight: () { + game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowRight, + ); + }, + onTapEnter: () { + game.triggerVirtualKeyUp(LogicalKeyboardKey.enter); + }, + ), + ); + }, }, ), ), diff --git a/lib/game/view/widgets/mobile_controls.dart b/lib/game/view/widgets/mobile_controls.dart new file mode 100644 index 00000000..b69f4c85 --- /dev/null +++ b/lib/game/view/widgets/mobile_controls.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +/// {@template mobile_controls} +/// Widget with the controls used to enable the user initials input on mobile. +/// {@endtemplate} +class MobileControls extends StatelessWidget { + /// {@macro mobile_controls} + const MobileControls({Key? key, + required this.onTapUp, + required this.onTapDown, + required this.onTapLeft, + required this.onTapRight, + required this.onTapEnter, + }) : super(key: key); + + /// Called when dpad up is pressed + final VoidCallback onTapUp; + + /// Called when dpad down is pressed + final VoidCallback onTapDown; + + /// Called when dpad left is pressed + final VoidCallback onTapLeft; + + /// Called when dpad right is pressed + final VoidCallback onTapRight; + + /// Called when enter is pressed + final VoidCallback onTapEnter; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + MobileDpad( + onTapUp: onTapUp, + onTapDown: onTapUp, + onTapLeft: onTapLeft, + onTapRight: onTapRight, + ), + PinballButton( + text: 'Enter', // TODO l10n + onTap: onTapEnter, + ), + ], + ); + } +} diff --git a/lib/game/view/widgets/mobile_dpad.dart b/lib/game/view/widgets/mobile_dpad.dart new file mode 100644 index 00000000..6e5dafc8 --- /dev/null +++ b/lib/game/view/widgets/mobile_dpad.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +/// {@template mobile_dpad} +/// Widget that renders a 4 direction dpad. +/// {@endtemplate} +class MobileDpad extends StatelessWidget { + /// {@template mobile_dpad} + const MobileDpad({ + Key? key, + required this.onTapUp, + required this.onTapDown, + required this.onTapLeft, + required this.onTapRight, + }) : super(key: key); + + static const _size = 180.0; + + /// Called when dpad up is pressed + final VoidCallback onTapUp; + + /// Called when dpad down is pressed + final VoidCallback onTapDown; + + /// Called when dpad left is pressed + final VoidCallback onTapLeft; + + /// Called when dpad right is pressed + final VoidCallback onTapRight; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: _size, + height: _size, + child: Column( + children: [ + Row( + children: [ + const Expanded(child: SizedBox()), + PinballDpadButton( + direction: PinballDpadDirection.up, + onTap: onTapUp, + ), + const Expanded(child: SizedBox()), + ], + ), + Row( + children: [ + PinballDpadButton( + direction: PinballDpadDirection.left, + onTap: onTapLeft, + ), + const Expanded(child: SizedBox()), + PinballDpadButton( + direction: PinballDpadDirection.right, + onTap: onTapRight, + ), + ], + ), + Row( + children: [ + const Expanded(child: SizedBox()), + PinballDpadButton( + direction: PinballDpadDirection.down, + onTap: onTapDown, + ), + const Expanded(child: SizedBox()), + ], + ), + ], + ), + ); + } +} diff --git a/lib/game/view/widgets/widgets.dart b/lib/game/view/widgets/widgets.dart index 5d1fccf8..2a04670f 100644 --- a/lib/game/view/widgets/widgets.dart +++ b/lib/game/view/widgets/widgets.dart @@ -1,5 +1,7 @@ export 'bonus_animation.dart'; export 'game_hud.dart'; +export 'mobile_controls.dart'; +export 'mobile_dpad.dart'; export 'play_button_overlay.dart'; export 'round_count_display.dart'; export 'score_view.dart'; diff --git a/packages/pinball_flame/lib/src/keyboard_input_controller.dart b/packages/pinball_flame/lib/src/keyboard_input_controller.dart index 8249e599..4ce80804 100644 --- a/packages/pinball_flame/lib/src/keyboard_input_controller.dart +++ b/packages/pinball_flame/lib/src/keyboard_input_controller.dart @@ -18,6 +18,18 @@ class KeyboardInputController extends Component with KeyboardHandler { final Map _keyUp; final Map _keyDown; + /// Trigger a virtual key up, can be used to simulate a key up event, without + /// having a real physical event happening. + bool onVirtualKeyUp(LogicalKeyboardKey key) { + final handler = _keyUp[key]; + + if (handler != null) { + return handler(); + } + + return true; + } + @override bool onKeyEvent(RawKeyEvent event, Set keysPressed) { final isUp = event is RawKeyUpEvent; diff --git a/packages/pinball_ui/assets/images/button/dpad_down.png b/packages/pinball_ui/assets/images/button/dpad_down.png new file mode 100644 index 00000000..11bbb26f Binary files /dev/null and b/packages/pinball_ui/assets/images/button/dpad_down.png differ diff --git a/packages/pinball_ui/assets/images/button/dpad_left.png b/packages/pinball_ui/assets/images/button/dpad_left.png new file mode 100644 index 00000000..943cacc4 Binary files /dev/null and b/packages/pinball_ui/assets/images/button/dpad_left.png differ diff --git a/packages/pinball_ui/assets/images/button/dpad_right.png b/packages/pinball_ui/assets/images/button/dpad_right.png new file mode 100644 index 00000000..724b9f3e Binary files /dev/null and b/packages/pinball_ui/assets/images/button/dpad_right.png differ diff --git a/packages/pinball_ui/assets/images/button/dpad_up.png b/packages/pinball_ui/assets/images/button/dpad_up.png new file mode 100644 index 00000000..d1175d57 Binary files /dev/null and b/packages/pinball_ui/assets/images/button/dpad_up.png differ diff --git a/packages/pinball_ui/lib/gen/assets.gen.dart b/packages/pinball_ui/lib/gen/assets.gen.dart index 8972e8e0..9b09b254 100644 --- a/packages/pinball_ui/lib/gen/assets.gen.dart +++ b/packages/pinball_ui/lib/gen/assets.gen.dart @@ -3,8 +3,6 @@ /// FlutterGen /// ***************************************************** -// ignore_for_file: directives_ordering,unnecessary_import - import 'package:flutter/widgets.dart'; class $AssetsImagesGen { @@ -17,7 +15,14 @@ class $AssetsImagesGen { class $AssetsImagesButtonGen { const $AssetsImagesButtonGen(); - /// File path: assets/images/button/pinball_button.png + AssetGenImage get dpadDown => + const AssetGenImage('assets/images/button/dpad_down.png'); + AssetGenImage get dpadLeft => + const AssetGenImage('assets/images/button/dpad_left.png'); + AssetGenImage get dpadRight => + const AssetGenImage('assets/images/button/dpad_right.png'); + AssetGenImage get dpadUp => + const AssetGenImage('assets/images/button/dpad_up.png'); AssetGenImage get pinballButton => const AssetGenImage('assets/images/button/pinball_button.png'); } @@ -25,7 +30,6 @@ class $AssetsImagesButtonGen { class $AssetsImagesDialogGen { const $AssetsImagesDialogGen(); - /// File path: assets/images/dialog/background.png AssetGenImage get background => const AssetGenImage('assets/images/dialog/background.png'); } diff --git a/packages/pinball_ui/lib/gen/fonts.gen.dart b/packages/pinball_ui/lib/gen/fonts.gen.dart index 5f77da16..b15f2dd0 100644 --- a/packages/pinball_ui/lib/gen/fonts.gen.dart +++ b/packages/pinball_ui/lib/gen/fonts.gen.dart @@ -3,14 +3,9 @@ /// FlutterGen /// ***************************************************** -// ignore_for_file: directives_ordering,unnecessary_import - class FontFamily { FontFamily._(); - /// Font family: PixeloidMono static const String pixeloidMono = 'PixeloidMono'; - - /// Font family: PixeloidSans static const String pixeloidSans = 'PixeloidSans'; } diff --git a/packages/pinball_ui/lib/src/widgets/pinball_dpad.dart b/packages/pinball_ui/lib/src/widgets/pinball_dpad.dart new file mode 100644 index 00000000..6d929f53 --- /dev/null +++ b/packages/pinball_ui/lib/src/widgets/pinball_dpad.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_ui/gen/gen.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +/// Enum with all possibile directions of a [PinballDpadButton]. +enum PinballDpadDirection { + /// Up + up, + + /// Down + down, + + /// Left + left, + + /// Right + right, +} + +extension _PinballDpadDirectionX on PinballDpadDirection { + String toAsset() { + switch (this) { + case PinballDpadDirection.up: + return Assets.images.button.dpadUp.keyName; + case PinballDpadDirection.down: + return Assets.images.button.dpadDown.keyName; + case PinballDpadDirection.left: + return Assets.images.button.dpadLeft.keyName; + case PinballDpadDirection.right: + return Assets.images.button.dpadRight.keyName; + } + } +} + +/// {@template pinball_dpad_button} +/// Widget that renders a Dpad button with a given direction. +/// {@endtemplate} +class PinballDpadButton extends StatelessWidget { + /// {@macro pinball_dpad_button} + const PinballDpadButton({ + Key? key, + required this.direction, + required this.onTap, + }) : super(key: key); + + /// Which [PinballDpadDirection] this button is. + final PinballDpadDirection direction; + + /// The function executed when the button is pressed. + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Material( + color: PinballColors.transparent, + child: InkWell( + onTap: onTap, + child: Image.asset( + direction.toAsset(), + width: 60, + height: 60, + ), + ), + ); + } +} diff --git a/packages/pinball_ui/lib/src/widgets/widgets.dart b/packages/pinball_ui/lib/src/widgets/widgets.dart index 3aa96c3e..68fd2ee9 100644 --- a/packages/pinball_ui/lib/src/widgets/widgets.dart +++ b/packages/pinball_ui/lib/src/widgets/widgets.dart @@ -1,4 +1,5 @@ export 'animated_ellipsis_text.dart'; export 'crt_background.dart'; export 'pinball_button.dart'; +export 'pinball_dpad.dart'; export 'pinball_loading_indicator.dart'; diff --git a/packages/platform_helper/lib/src/platform_helper.dart b/packages/platform_helper/lib/src/platform_helper.dart index 638d1ab6..53cf4491 100644 --- a/packages/platform_helper/lib/src/platform_helper.dart +++ b/packages/platform_helper/lib/src/platform_helper.dart @@ -6,7 +6,8 @@ import 'package:flutter/foundation.dart'; class PlatformHelper { /// {@macro platform_helper} bool get isMobile { - return defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android; + return true; + //return defaultTargetPlatform == TargetPlatform.iOS || + // defaultTargetPlatform == TargetPlatform.android; } }