From 647f80976b6281c802ada25b885b75d86af6b315 Mon Sep 17 00:00:00 2001 From: Felithium <41200990+Felithium@users.noreply.github.com> Date: Tue, 5 Nov 2019 17:20:04 +0100 Subject: [PATCH 01/15] added missing imports & fixed inheritance --- doc/util.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/doc/util.md b/doc/util.md index 0be121789..e99c50266 100644 --- a/doc/util.md +++ b/doc/util.md @@ -55,11 +55,14 @@ Flame provides a simple utility class to help you handle countdowns and event li __Countdown example:__ ```dart -import 'package:flame/game.dart'; -import 'package:flame/time.dart'; -import 'package:flame/text_config.dart'; +import 'dart:ui'; -class MyGame extends MyGame { +import 'package:flame/game.dart'; +import 'package:flame/position.dart'; +import 'package:flame/text_config.dart'; +import 'package:flame/time.dart'; + +class MyGame extends Game { final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); final countdown = Timer(2); @@ -77,7 +80,8 @@ class MyGame extends MyGame { @override void render(Canvas canvas) { - textConfig.render(canvas, "Countdown: ${countdown.current.toString()}", Position(10, 100)); + textConfig.render(canvas, "Countdown: ${countdown.current.toString()}", + Position(10, 100)); } } @@ -86,11 +90,14 @@ class MyGame extends MyGame { __Interval example:__ ```dart -import 'package:flame/game.dart'; -import 'package:flame/time.dart'; -import 'package:flame/text_config.dart'; +import 'dart:ui'; -class MyGame extends MyGame { +import 'package:flame/game.dart'; +import 'package:flame/position.dart'; +import 'package:flame/text_config.dart'; +import 'package:flame/time.dart'; + +class MyGame extends Game { final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); Timer interval; @@ -104,8 +111,7 @@ class MyGame extends MyGame { } @override - void update(double dt) { - } + void update(double dt) {} @override void render(Canvas canvas) { From 8852e7a07a7d60adcf7253dd6845a35e7393fdc5 Mon Sep 17 00:00:00 2001 From: Felithium <41200990+Felithium@users.noreply.github.com> Date: Tue, 5 Nov 2019 19:30:05 +0100 Subject: [PATCH 02/15] timer needs to be updated to trigger callback --- doc/util.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/util.md b/doc/util.md index e99c50266..addb53dee 100644 --- a/doc/util.md +++ b/doc/util.md @@ -111,7 +111,9 @@ class MyGame extends Game { } @override - void update(double dt) {} + void update(double dt) { + interval.update(dt); + } @override void render(Canvas canvas) { From 4dcb32b1073ad742424d0a0c85dbec3a5bb467ff Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 23 Oct 2019 20:38:17 -0300 Subject: [PATCH 03/15] Adding new API for gesture detection on Game class --- lib/game.dart | 45 ++++++++++++++++++--------------------------- lib/util.dart | 2 ++ 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/lib/game.dart b/lib/game.dart index b27faf1b1..9867194e6 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -12,7 +12,6 @@ import 'package:ordered_set/ordered_set.dart'; import 'components/component.dart'; import 'components/mixins/has_game_ref.dart'; import 'components/mixins/tapable.dart'; -import 'flame.dart'; import 'position.dart'; /// Represents a generic game. @@ -20,23 +19,12 @@ import 'position.dart'; /// Subclass this to implement the [update] and [render] methods. /// Flame will deal with calling these methods properly when the game's widget is rendered. abstract class Game { - TapGestureRecognizer _createTapGestureRecognizer() => TapGestureRecognizer() - ..onTapUp = (TapUpDetails details) { - onTapUp(details); - } - ..onTapDown = (TapDownDetails details) { - onTapDown(details); - } - ..onTapCancel = () { - onTapCancel(); - }; + void onTap() {} void onTapCancel() {} void onTapDown(TapDownDetails details) {} void onTapUp(TapUpDetails details) {} - TapGestureRecognizer _gestureRecognizer; - // Widget Builder for this Game final builder = WidgetBuilder(); @@ -67,26 +55,29 @@ abstract class Game { Widget get widget => builder.build(this); // Called when the Game widget is attached - void onAttach() { - if (_gestureRecognizer != null) { - Flame.util.removeGestureRecognizer(_gestureRecognizer); - } - _gestureRecognizer = _createTapGestureRecognizer(); - Flame.util.addGestureRecognizer(_gestureRecognizer); - } + void onAttach() { } // Called when the Game widget is detached - void onDetach() { - if (_gestureRecognizer != null) { - Flame.util.removeGestureRecognizer(_gestureRecognizer); - } - } + void onDetach() { } } class WidgetBuilder { Offset offset = Offset.zero; - Widget build(Game game) => Directionality( - textDirection: TextDirection.ltr, child: EmbeddedGameWidget(game)); + + Widget build(Game game) { + return GestureDetector( + onTap: () => game.onTap(), + onTapCancel: () => game.onTapCancel(), + onTapDown: (TapDownDetails d) => game.onTapDown(d), + onTapUp: (TapUpDetails d) => game.onTapUp(d), + child: Container( + color: const Color(0xFF000000), + child: Directionality( + textDirection: TextDirection.ltr, child: EmbeddedGameWidget(game) + ) + ), + ); + } } /// This is a more complete and opinionated implementation of Game. diff --git a/lib/util.dart b/lib/util.dart index 12a7ae30a..d0dab7a11 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -113,6 +113,8 @@ class Util { /// This properly binds a gesture recognizer to your game. /// /// Use this in order to get it to work in case your app also contains other widgets. + /// + /// @Deprecated('This method can lead to confuse behaviour, use the gestures methods provided by the Game class') void addGestureRecognizer(GestureRecognizer recognizer) { if (GestureBinding.instance == null) { throw Exception( From cd025896ad139b0be156156b9eac4a35198f744c Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 23 Oct 2019 20:47:39 -0300 Subject: [PATCH 04/15] Fixing lint --- lib/game.dart | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/game.dart b/lib/game.dart index 9867194e6..e6c6adeb0 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -19,7 +19,6 @@ import 'position.dart'; /// Subclass this to implement the [update] and [render] methods. /// Flame will deal with calling these methods properly when the game's widget is rendered. abstract class Game { - void onTap() {} void onTapCancel() {} void onTapDown(TapDownDetails details) {} @@ -55,10 +54,10 @@ abstract class Game { Widget get widget => builder.build(this); // Called when the Game widget is attached - void onAttach() { } + void onAttach() {} // Called when the Game widget is detached - void onDetach() { } + void onDetach() {} } class WidgetBuilder { @@ -66,16 +65,15 @@ class WidgetBuilder { Widget build(Game game) { return GestureDetector( - onTap: () => game.onTap(), - onTapCancel: () => game.onTapCancel(), - onTapDown: (TapDownDetails d) => game.onTapDown(d), - onTapUp: (TapUpDetails d) => game.onTapUp(d), - child: Container( - color: const Color(0xFF000000), - child: Directionality( - textDirection: TextDirection.ltr, child: EmbeddedGameWidget(game) - ) - ), + onTap: () => game.onTap(), + onTapCancel: () => game.onTapCancel(), + onTapDown: (TapDownDetails d) => game.onTapDown(d), + onTapUp: (TapUpDetails d) => game.onTapUp(d), + child: Container( + color: const Color(0xFF000000), + child: Directionality( + textDirection: TextDirection.ltr, + child: EmbeddedGameWidget(game))), ); } } From c70ff42912c6cadecc6702716719f87be50892fb Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 30 Oct 2019 23:22:49 -0300 Subject: [PATCH 05/15] Adding missing methods --- lib/game.dart | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/lib/game.dart b/lib/game.dart index e6c6adeb0..a00f5ce43 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -23,6 +23,37 @@ abstract class Game { void onTapCancel() {} void onTapDown(TapDownDetails details) {} void onTapUp(TapUpDetails details) {} + void onSecondaryTapDown(TapDownDetails details) {} + void onSecondaryTapUp(TapUpDetails details) {} + void onSecondaryTapCancel() {} + void onDoubleTap() {} + void onLongPress() {} + void onLongPressStart(LongPressStartDetails details) {} + void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} + void onLongPressUp() {} + void onLongPressEnd(LongPressEndDetails details) {} + void onVerticalDragDown(DragDownDetails details) {} + void onVerticalDragStart(DragStartDetails details) {} + void onVerticalDragUpdate(DragUpdateDetails details) {} + void onVerticalDragEnd(DragEndDetails details) {} + void onVerticalDragCancel() {} + void onHorizontalDragDown(DragDownDetails details) {} + void onHorizontalDragStart(DragStartDetails details) {} + void onHorizontalDragUpdate(DragUpdateDetails details) {} + void onHorizontalDragEnd(DragEndDetails details) {} + void onHorizontalDragCancel() {} + void onForcePressStart(ForcePressDetails details) {} + void onForcePressPeak(ForcePressDetails details) {} + void onForcePressUpdate(ForcePressDetails details) {} + void onForcePressEnd(ForcePressDetails details) {} + void onPanDown(DragDownDetails details) {} + void onPanStart(DragStartDetails details) {} + void onPanUpdate(DragUpdateDetails details) {} + void onPanEnd(DragEndDetails details) {} + void onPanCancel() {} + void onScaleStart(ScaleStartDetails details) {} + void onScaleUpdate(ScaleUpdateDetails details) {} + void onScaleEnd(ScaleEndDetails details) {} // Widget Builder for this Game final builder = WidgetBuilder(); @@ -69,6 +100,37 @@ class WidgetBuilder { onTapCancel: () => game.onTapCancel(), onTapDown: (TapDownDetails d) => game.onTapDown(d), onTapUp: (TapUpDetails d) => game.onTapUp(d), + onSecondaryTapDown: (TapDownDetails d) => game.onSecondaryTapDown(d), + onSecondaryTapUp: (TapUpDetails d) => game.onSecondaryTapUp(d), + onSecondaryTapCancel: () => game.onSecondaryTapCancel(), + onDoubleTap: () => game.onDoubleTap(), + onLongPress: () => game.onLongPress(), + onLongPressStart: (LongPressStartDetails d) => game.onLongPressStart(d), + onLongPressMoveUpdate: (LongPressMoveUpdateDetails d) => game.onLongPressMoveUpdate(d), + onLongPressUp: () => game.onLongPressUp(), + onLongPressEnd: (LongPressEndDetails d) => game.onLongPressEnd(d), + onVerticalDragDown: (DragDownDetails d) => game.onVerticalDragDown(d), + onVerticalDragStart: (DragStartDetails d) => game.onVerticalDragStart(d), + onVerticalDragUpdate: (DragUpdateDetails d) => game.onVerticalDragUpdate(d), + onVerticalDragEnd: (DragEndDetails d) => game.onVerticalDragEnd(d), + onVerticalDragCancel: () => game.onVerticalDragCancel(), + onHorizontalDragDown: (DragDownDetails d) => game.onHorizontalDragDown(d), + onHorizontalDragStart: (DragStartDetails d) => game.onHorizontalDragStart(d), + onHorizontalDragUpdate: (DragUpdateDetails d) => game.onHorizontalDragUpdate(d), + onHorizontalDragEnd: (DragEndDetails d) => game.onHorizontalDragEnd(d), + onHorizontalDragCancel: () => game.onHorizontalDragCancel(), + onForcePressStart: (ForcePressDetails d) => game.onForcePressStart(d), + onForcePressPeak: (ForcePressDetails d) => game.onForcePressPeak(d), + onForcePressUpdate: (ForcePressDetails d) => game.onForcePressUpdate(d), + onForcePressEnd: (ForcePressDetails d) => game.onForcePressEnd(d), + onPanDown: (DragDownDetails d) => game.onPanDown(d), + onPanStart: (DragStartDetails d) => game.onPanStart(d), + onPanUpdate: (DragUpdateDetails d) => game.onPanUpdate(d), + onPanEnd: (DragEndDetails d) => game.onPanEnd(d), + onPanCancel: () => game.onPanCancel(), + onScaleStart: (ScaleStartDetails d) => game.onScaleStart(d), + onScaleUpdate: (ScaleUpdateDetails d) => game.onScaleUpdate(d), + onScaleEnd: (ScaleEndDetails d) => game.onScaleEnd(d), child: Container( color: const Color(0xFF000000), child: Directionality( From 18c1670a1e99bdc9d5ae7d2e65df82816154e99f Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 30 Oct 2019 23:29:28 -0300 Subject: [PATCH 06/15] Updating docs and CHANGELOG --- CHANGELOG.md | 1 + README.md | 15 +----------- doc/input.md | 69 +++++++++++++++++++++++++++------------------------- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 022571818..340627390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [next] - Fixing FlareAnimation API to match convention - Fixing FlareComponent renderization +- New GestureDetector api to Game ## 0.16.1 - Added `Bgm` class for easy looping background music management. diff --git a/README.md b/README.md index aba0efb6d..e2d46de7d 100644 --- a/README.md +++ b/README.md @@ -263,20 +263,7 @@ A very simple `BaseGame` implementation example can be seen below: ### Input -In order to handle user input, you can use the libraries provided by Flutter for regular apps: [Gesture Recognizers](https://flutter.io/gestures/). - -However, in order to bind them, use the `Flame.util.addGestureRecognizer` method; in doing so, you'll make sure they are properly unbound when the game widget is not being rendered, and so the rest of your screens will work appropriately. - -For example, to add a tap listener ("on click"): - -```dart - Flame.util.addGestureRecognizer(new TapGestureRecognizer() - ..onTapDown = (TapDownDetails evt) => game.handleInput(evt.globalPosition.dx, evt.globalPosition.dy)); -``` - -Where `game` is a reference to your game object and `handleInput` is a method you create to handle the input inside your game. - -If your game doesn't have other screens, just call this after your `runApp` call, in the `main` method. +`Game` class provides a whole set of methods which can be overridden to get access to touch events. [Complete Input Guide](doc/input.md) diff --git a/doc/input.md b/doc/input.md index 8bd47d56d..f4891eda5 100644 --- a/doc/input.md +++ b/doc/input.md @@ -1,42 +1,45 @@ # Input -In order to handle user input, you can use the libraries provided by Flutter for regular apps: [Gesture Recognizers](https://flutter.io/gestures/). - -However, in order to bind them, use the `Flame.util.addGestureRecognizer` method; in doing so, you'll make sure they are properly unbound when the game widget is not being rendered, and so the rest of your screens will work appropriately. - -For example, to add a tap listener ("on click"): +The `Game` class has a vast number of methods for handling touch controls, to use it, just override the method you want to use, and that method will start receiving events, below there is the whole list of available methods: ```dart - Flame.util.addGestureRecognizer(new TapGestureRecognizer() - ..onTapDown = (TapDownDetails evt) => game.handleInput(evt.globalPosition.dx, evt.globalPosition.dy)); + void onTap() {} + void onTapCancel() {} + void onTapDown(TapDownDetails details) {} + void onTapUp(TapUpDetails details) {} + void onSecondaryTapDown(TapDownDetails details) {} + void onSecondaryTapUp(TapUpDetails details) {} + void onSecondaryTapCancel() {} + void onDoubleTap() {} + void onLongPress() {} + void onLongPressStart(LongPressStartDetails details) {} + void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} + void onLongPressUp() {} + void onLongPressEnd(LongPressEndDetails details) {} + void onVerticalDragDown(DragDownDetails details) {} + void onVerticalDragStart(DragStartDetails details) {} + void onVerticalDragUpdate(DragUpdateDetails details) {} + void onVerticalDragEnd(DragEndDetails details) {} + void onVerticalDragCancel() {} + void onHorizontalDragDown(DragDownDetails details) {} + void onHorizontalDragStart(DragStartDetails details) {} + void onHorizontalDragUpdate(DragUpdateDetails details) {} + void onHorizontalDragEnd(DragEndDetails details) {} + void onHorizontalDragCancel() {} + void onForcePressStart(ForcePressDetails details) {} + void onForcePressPeak(ForcePressDetails details) {} + void onForcePressUpdate(ForcePressDetails details) {} + void onForcePressEnd(ForcePressDetails details) {} + void onPanDown(DragDownDetails details) {} + void onPanStart(DragStartDetails details) {} + void onPanUpdate(DragUpdateDetails details) {} + void onPanEnd(DragEndDetails details) {} + void onPanCancel() {} + void onScaleStart(ScaleStartDetails details) {} + void onScaleUpdate(ScaleUpdateDetails details) {} + void onScaleEnd(ScaleEndDetails details) {} ``` -Where `game` is a reference to your game object and `handleInput` is a method you create to handle the input inside your game. - -If your game doesn't have other screens, just call this after your `runApp` call, in the `main` method. - -Here are some example of more complex Gesture Recognizers: - -```dart - MyGame() { - // other init... - - Flame.util.addGestureRecognizer(createDragRecognizer()); - Flame.util.addGestureRecognizer(createTapRecognizer()); - } - - GestureRecognizer createDragRecognizer() { - return new ImmediateMultiDragGestureRecognizer() - ..onStart = (Offset position) => this.handleDrag(position); - } - - TapGestureRecognizer createTapRecognizer() { - return new TapGestureRecognizer() - ..onTapUp = (TapUpDetails details) => this.handleTap(details.globalPosition);; - } -``` -__ATTENTION:__ `Flame.util.addGestureRecognizer` must be called after the `runApp`, otherwise Flutter's `GestureBinding` will not be initialized yet and exceptions will occur. - ## Tapable components Flame also offers a simple helper to make it easier to handle tap events on `PositionComponent`, by using the mixin `Tapable` your components can override the following methods, enabling easy to use tap events on your Component. From 40a3598c597485c7467effd25fdb016f0f81a159 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 30 Oct 2019 23:35:05 -0300 Subject: [PATCH 07/15] Linting --- lib/game.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/game.dart b/lib/game.dart index a00f5ce43..129ee1315 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -106,17 +106,21 @@ class WidgetBuilder { onDoubleTap: () => game.onDoubleTap(), onLongPress: () => game.onLongPress(), onLongPressStart: (LongPressStartDetails d) => game.onLongPressStart(d), - onLongPressMoveUpdate: (LongPressMoveUpdateDetails d) => game.onLongPressMoveUpdate(d), + onLongPressMoveUpdate: (LongPressMoveUpdateDetails d) => + game.onLongPressMoveUpdate(d), onLongPressUp: () => game.onLongPressUp(), onLongPressEnd: (LongPressEndDetails d) => game.onLongPressEnd(d), onVerticalDragDown: (DragDownDetails d) => game.onVerticalDragDown(d), onVerticalDragStart: (DragStartDetails d) => game.onVerticalDragStart(d), - onVerticalDragUpdate: (DragUpdateDetails d) => game.onVerticalDragUpdate(d), + onVerticalDragUpdate: (DragUpdateDetails d) => + game.onVerticalDragUpdate(d), onVerticalDragEnd: (DragEndDetails d) => game.onVerticalDragEnd(d), onVerticalDragCancel: () => game.onVerticalDragCancel(), onHorizontalDragDown: (DragDownDetails d) => game.onHorizontalDragDown(d), - onHorizontalDragStart: (DragStartDetails d) => game.onHorizontalDragStart(d), - onHorizontalDragUpdate: (DragUpdateDetails d) => game.onHorizontalDragUpdate(d), + onHorizontalDragStart: (DragStartDetails d) => + game.onHorizontalDragStart(d), + onHorizontalDragUpdate: (DragUpdateDetails d) => + game.onHorizontalDragUpdate(d), onHorizontalDragEnd: (DragEndDetails d) => game.onHorizontalDragEnd(d), onHorizontalDragCancel: () => game.onHorizontalDragCancel(), onForcePressStart: (ForcePressDetails d) => game.onForcePressStart(d), From 655a70b0a616ef2bbc7d64583cf4cfe36152fba0 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 31 Oct 2019 19:24:06 -0300 Subject: [PATCH 08/15] Adding docs and faq entry for the new API --- FAQ.md | 6 ++++++ README.md | 18 ++++++++++++++++++ doc/input.md | 20 ++++++++++++++++++++ lib/util.dart | 2 ++ 4 files changed, 46 insertions(+) diff --git a/FAQ.md b/FAQ.md index 3f2f44ed0..9181d280a 100644 --- a/FAQ.md +++ b/FAQ.md @@ -52,6 +52,12 @@ If you are using `BaseGame`, you have a `camera` attribute that allows you to of For a more in-depth tutorial on how the camera works (in general & in Flame) and how to use it, check [erickzanardo](https://github.com/erickzanardo)'s [excellent tutorial](https://fireslime.xyz/articles/20190911_Basic_Camera_Usage_In_Flame.html), published via FireSlime. +## How to handle touch events on your game? + +You can always use all the Widgets and features that Flutter already provide for that, but Flame wraps gesture detector callbacks on its base Game class so it can ben a little easier to handle those events, you can find more about it on the input documentation page: + +https://flame-engine.org/docs/input.md + ## Other questions? Didn't find what you needed here? Please head to [FireSlime's Discord channel](https://discord.gg/pxrBmy4) where the community might help you with more questions. diff --git a/README.md b/README.md index e2d46de7d..b7298848a 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,24 @@ A very simple `BaseGame` implementation example can be seen below: `Game` class provides a whole set of methods which can be overridden to get access to touch events. +__Example__ + +```dart +class MyGame extends Game { + // Other methods ommited + + @override + void onTapDown(TapDownDetails details) { + print("Player tap down on ${details.globalPosition.dx} - ${details.globalPosition.dy}"); + } + + @override + void onTapUp(TapUpDetails details) { + print("Player tap up on ${details.globalPosition.dx} - ${details.globalPosition.dy}"); + } +} +``` + [Complete Input Guide](doc/input.md) ## Credits diff --git a/doc/input.md b/doc/input.md index f4891eda5..f438778b6 100644 --- a/doc/input.md +++ b/doc/input.md @@ -40,6 +40,26 @@ The `Game` class has a vast number of methods for handling touch controls, to us void onScaleEnd(ScaleEndDetails details) {} ``` +All those methods are basically a mirror from the callbacks available on the [GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), you can also read more about Flutter's gestures [here](https://api.flutter.dev/flutter/gestures/gestures-library.html). + +## Example + +```dart +class MyGame extends Game { + // Other methods ommited + + @override + void onTapDown(TapDownDetails details) { + print("Player tap down on ${details.globalPosition.dx} - ${details.globalPosition.dy}"); + } + + @override + void onTapUp(TapUpDetails details) { + print("Player tap up on ${details.globalPosition.dx} - ${details.globalPosition.dy}"); + } +} +``` + ## Tapable components Flame also offers a simple helper to make it easier to handle tap events on `PositionComponent`, by using the mixin `Tapable` your components can override the following methods, enabling easy to use tap events on your Component. diff --git a/lib/util.dart b/lib/util.dart index d0dab7a11..f8f874337 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -114,6 +114,8 @@ class Util { /// /// Use this in order to get it to work in case your app also contains other widgets. /// + /// Read more at: https://flame-engine.org/docs/input.md + /// /// @Deprecated('This method can lead to confuse behaviour, use the gestures methods provided by the Game class') void addGestureRecognizer(GestureRecognizer recognizer) { if (GestureBinding.instance == null) { From 6f44e0514bd01fe7df2a16a85aff0fa62f111b84 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 31 Oct 2019 20:05:23 -0300 Subject: [PATCH 09/15] Fixing gestures conflicts and adding a gestures example --- doc/examples/gestures/.gitignore | 72 +++++++++++++++++++ doc/examples/gestures/.metadata | 10 +++ doc/examples/gestures/README.md | 3 + doc/examples/gestures/lib/main.dart | 50 +++++++++++++ doc/examples/gestures/pubspec.yaml | 16 +++++ lib/game.dart | 105 ++++++++++++++++++---------- 6 files changed, 221 insertions(+), 35 deletions(-) create mode 100644 doc/examples/gestures/.gitignore create mode 100644 doc/examples/gestures/.metadata create mode 100644 doc/examples/gestures/README.md create mode 100644 doc/examples/gestures/lib/main.dart create mode 100644 doc/examples/gestures/pubspec.yaml diff --git a/doc/examples/gestures/.gitignore b/doc/examples/gestures/.gitignore new file mode 100644 index 000000000..ac4a90645 --- /dev/null +++ b/doc/examples/gestures/.gitignore @@ -0,0 +1,72 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/doc/examples/gestures/.metadata b/doc/examples/gestures/.metadata new file mode 100644 index 000000000..a11a679fc --- /dev/null +++ b/doc/examples/gestures/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 841707365a9be08f2219cbafc52c52d6af5355aa + channel: master + +project_type: app diff --git a/doc/examples/gestures/README.md b/doc/examples/gestures/README.md new file mode 100644 index 000000000..58ae0bc8a --- /dev/null +++ b/doc/examples/gestures/README.md @@ -0,0 +1,3 @@ +# gestures + +A flame game showcasing the use of gestures callbacks diff --git a/doc/examples/gestures/lib/main.dart b/doc/examples/gestures/lib/main.dart new file mode 100644 index 000000000..1a87639bf --- /dev/null +++ b/doc/examples/gestures/lib/main.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flame/game.dart'; + +void main() { + final game = MyGame(); + runApp(game.widget); +} + +class MyGame extends Game { + final _whitePaint = Paint()..color = const Color(0xFFFFFFFF); + final _bluePaint = Paint()..color = const Color(0xFF0000FF); + final _greenPaint = Paint()..color = const Color(0xFF00FF00); + + Paint _paint; + + Rect _rect = const Rect.fromLTWH(50, 50, 50, 50); + + MyGame() { + _paint = _whitePaint; + } + + @override + void onTap() { + _paint = _paint == _whitePaint ? _bluePaint : _whitePaint; + } + + @override + bool useDoubleTapDetectors() => true; + + @override + void onDoubleTap() { + _paint = _greenPaint; + } + + @override + bool usePanDetectors() => true; + + @override + void onPanUpdate(DragUpdateDetails details) { + _rect = _rect.translate(details.delta.dx, details.delta.dy); + } + + @override + void update(double dt) {} + + @override + void render(Canvas canvas) { + canvas.drawRect(_rect, _paint); + } +} diff --git a/doc/examples/gestures/pubspec.yaml b/doc/examples/gestures/pubspec.yaml new file mode 100644 index 000000000..5a74dc912 --- /dev/null +++ b/doc/examples/gestures/pubspec.yaml @@ -0,0 +1,16 @@ +name: gestures +description: A flame game showcasing the use of gestures callbacks + +version: 1.0.0+1 + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flame: + path: ../../../ + +flutter: + uses-material-design: false diff --git a/lib/game.dart b/lib/game.dart index 129ee1315..2e6c41df3 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -19,38 +19,55 @@ import 'position.dart'; /// Subclass this to implement the [update] and [render] methods. /// Flame will deal with calling these methods properly when the game's widget is rendered. abstract class Game { + bool useTapDetectors() => true; void onTap() {} void onTapCancel() {} void onTapDown(TapDownDetails details) {} void onTapUp(TapUpDetails details) {} + + bool useSecondaryTapDetectors() => false; void onSecondaryTapDown(TapDownDetails details) {} void onSecondaryTapUp(TapUpDetails details) {} void onSecondaryTapCancel() {} + + bool useDoubleTapDetectors() => false; void onDoubleTap() {} + + bool useLongPressDetectors() => false; void onLongPress() {} void onLongPressStart(LongPressStartDetails details) {} void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} void onLongPressUp() {} void onLongPressEnd(LongPressEndDetails details) {} + + bool useVerticalDragDetectors() => false; void onVerticalDragDown(DragDownDetails details) {} void onVerticalDragStart(DragStartDetails details) {} void onVerticalDragUpdate(DragUpdateDetails details) {} void onVerticalDragEnd(DragEndDetails details) {} void onVerticalDragCancel() {} + + bool useHorizontalDragDetectors() => false; void onHorizontalDragDown(DragDownDetails details) {} void onHorizontalDragStart(DragStartDetails details) {} void onHorizontalDragUpdate(DragUpdateDetails details) {} void onHorizontalDragEnd(DragEndDetails details) {} void onHorizontalDragCancel() {} + + bool useForcePressDetectors() => false; void onForcePressStart(ForcePressDetails details) {} void onForcePressPeak(ForcePressDetails details) {} void onForcePressUpdate(ForcePressDetails details) {} void onForcePressEnd(ForcePressDetails details) {} + + bool usePanDetectors() => false; void onPanDown(DragDownDetails details) {} void onPanStart(DragStartDetails details) {} void onPanUpdate(DragUpdateDetails details) {} void onPanEnd(DragEndDetails details) {} void onPanCancel() {} + + bool useScaleDetectors() => false; void onScaleStart(ScaleStartDetails details) {} void onScaleUpdate(ScaleUpdateDetails details) {} void onScaleEnd(ScaleEndDetails details) {} @@ -96,45 +113,63 @@ class WidgetBuilder { Widget build(Game game) { return GestureDetector( - onTap: () => game.onTap(), - onTapCancel: () => game.onTapCancel(), - onTapDown: (TapDownDetails d) => game.onTapDown(d), - onTapUp: (TapUpDetails d) => game.onTapUp(d), - onSecondaryTapDown: (TapDownDetails d) => game.onSecondaryTapDown(d), - onSecondaryTapUp: (TapUpDetails d) => game.onSecondaryTapUp(d), - onSecondaryTapCancel: () => game.onSecondaryTapCancel(), - onDoubleTap: () => game.onDoubleTap(), - onLongPress: () => game.onLongPress(), - onLongPressStart: (LongPressStartDetails d) => game.onLongPressStart(d), - onLongPressMoveUpdate: (LongPressMoveUpdateDetails d) => + // Taps + onTap: !game.useTapDetectors() ? null : () => game.onTap(), + onTapCancel: !game.useTapDetectors() ? null : () => game.onTapCancel(), + onTapDown: !game.useTapDetectors() ? null : (TapDownDetails d) => game.onTapDown(d), + onTapUp: !game.useTapDetectors() ? null : (TapUpDetails d) => game.onTapUp(d), + + // Secondary taps + onSecondaryTapDown: !game.useSecondaryTapDetectors() ? null : (TapDownDetails d) => game.onSecondaryTapDown(d), + onSecondaryTapUp: !game.useSecondaryTapDetectors() ? null : (TapUpDetails d) => game.onSecondaryTapUp(d), + onSecondaryTapCancel: !game.useSecondaryTapDetectors() ? null : () => game.onSecondaryTapCancel(), + + // Double tap + onDoubleTap: !game.useDoubleTapDetectors() ? null : () => game.onDoubleTap(), + + // Long presses + onLongPress: !game.useLongPressDetectors() ? null : () => game.onLongPress(), + onLongPressStart: !game.useLongPressDetectors() ? null : (LongPressStartDetails d) => game.onLongPressStart(d), + onLongPressMoveUpdate: !game.useLongPressDetectors() ? null : (LongPressMoveUpdateDetails d) => game.onLongPressMoveUpdate(d), - onLongPressUp: () => game.onLongPressUp(), - onLongPressEnd: (LongPressEndDetails d) => game.onLongPressEnd(d), - onVerticalDragDown: (DragDownDetails d) => game.onVerticalDragDown(d), - onVerticalDragStart: (DragStartDetails d) => game.onVerticalDragStart(d), - onVerticalDragUpdate: (DragUpdateDetails d) => + onLongPressUp: !game.useLongPressDetectors() ? null : () => game.onLongPressUp(), + onLongPressEnd: !game.useLongPressDetectors() ? null : (LongPressEndDetails d) => game.onLongPressEnd(d), + + // Vertical drag + onVerticalDragDown: !game.useVerticalDragDetectors() ? null : (DragDownDetails d) => game.onVerticalDragDown(d), + onVerticalDragStart: !game.useVerticalDragDetectors() ? null : (DragStartDetails d) => game.onVerticalDragStart(d), + onVerticalDragUpdate: !game.useVerticalDragDetectors() ? null : (DragUpdateDetails d) => game.onVerticalDragUpdate(d), - onVerticalDragEnd: (DragEndDetails d) => game.onVerticalDragEnd(d), - onVerticalDragCancel: () => game.onVerticalDragCancel(), - onHorizontalDragDown: (DragDownDetails d) => game.onHorizontalDragDown(d), - onHorizontalDragStart: (DragStartDetails d) => + onVerticalDragEnd: !game.useVerticalDragDetectors() ? null : (DragEndDetails d) => game.onVerticalDragEnd(d), + onVerticalDragCancel: !game.useVerticalDragDetectors() ? null : () => game.onVerticalDragCancel(), + + // Horizontal drag + onHorizontalDragDown: !game.useHorizontalDragDetectors() ? null : (DragDownDetails d) => game.onHorizontalDragDown(d), + onHorizontalDragStart: !game.useHorizontalDragDetectors() ? null : (DragStartDetails d) => game.onHorizontalDragStart(d), - onHorizontalDragUpdate: (DragUpdateDetails d) => + onHorizontalDragUpdate: !game.useHorizontalDragDetectors() ? null : (DragUpdateDetails d) => game.onHorizontalDragUpdate(d), - onHorizontalDragEnd: (DragEndDetails d) => game.onHorizontalDragEnd(d), - onHorizontalDragCancel: () => game.onHorizontalDragCancel(), - onForcePressStart: (ForcePressDetails d) => game.onForcePressStart(d), - onForcePressPeak: (ForcePressDetails d) => game.onForcePressPeak(d), - onForcePressUpdate: (ForcePressDetails d) => game.onForcePressUpdate(d), - onForcePressEnd: (ForcePressDetails d) => game.onForcePressEnd(d), - onPanDown: (DragDownDetails d) => game.onPanDown(d), - onPanStart: (DragStartDetails d) => game.onPanStart(d), - onPanUpdate: (DragUpdateDetails d) => game.onPanUpdate(d), - onPanEnd: (DragEndDetails d) => game.onPanEnd(d), - onPanCancel: () => game.onPanCancel(), - onScaleStart: (ScaleStartDetails d) => game.onScaleStart(d), - onScaleUpdate: (ScaleUpdateDetails d) => game.onScaleUpdate(d), - onScaleEnd: (ScaleEndDetails d) => game.onScaleEnd(d), + onHorizontalDragEnd: !game.useHorizontalDragDetectors() ? null : (DragEndDetails d) => game.onHorizontalDragEnd(d), + onHorizontalDragCancel: !game.useHorizontalDragDetectors() ? null : () => game.onHorizontalDragCancel(), + + // Force presses + onForcePressStart: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressStart(d), + onForcePressPeak: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressPeak(d), + onForcePressUpdate: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressUpdate(d), + onForcePressEnd: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressEnd(d), + + // Pan + onPanDown: !game.usePanDetectors() ? null : (DragDownDetails d) => game.onPanDown(d), + onPanStart: !game.usePanDetectors() ? null : (DragStartDetails d) => game.onPanStart(d), + onPanUpdate: !game.usePanDetectors() ? null : (DragUpdateDetails d) => game.onPanUpdate(d), + onPanEnd: !game.usePanDetectors() ? null : (DragEndDetails d) => game.onPanEnd(d), + onPanCancel: !game.usePanDetectors() ? null : () => game.onPanCancel(), + + // Scales + onScaleStart: !game.useScaleDetectors() ? null : (ScaleStartDetails d) => game.onScaleStart(d), + onScaleUpdate: !game.useScaleDetectors() ? null : (ScaleUpdateDetails d) => game.onScaleUpdate(d), + onScaleEnd: !game.useScaleDetectors() ? null : (ScaleEndDetails d) => game.onScaleEnd(d), + child: Container( color: const Color(0xFF000000), child: Directionality( From ae005ae93dddf3ecad01dfee0b152b00200733d9 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 31 Oct 2019 20:11:42 -0300 Subject: [PATCH 10/15] Adding docs for new methods --- doc/input.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/input.md b/doc/input.md index f438778b6..08254ea6c 100644 --- a/doc/input.md +++ b/doc/input.md @@ -40,6 +40,10 @@ The `Game` class has a vast number of methods for handling touch controls, to us void onScaleEnd(ScaleEndDetails details) {} ``` +Since many detectors conflict with each other, (for example, you can't register both Vertical and Horizontal drags) by default only the __tap detectors__ are already registered. + +To change that behaviour, you can override methods that enable or disable that gesutre detector for the game, for example, to enable vertical drag detectors, you should override the `useVerticalDragDetectors` to return `true`, all the other detectors have equivalent methods and follow the same logic. + All those methods are basically a mirror from the callbacks available on the [GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), you can also read more about Flutter's gestures [here](https://api.flutter.dev/flutter/gestures/gestures-library.html). ## Example From 50fcc2c6968146a7cc491697f8903e7ed8d6d45f Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 31 Oct 2019 20:15:49 -0300 Subject: [PATCH 11/15] Adding reference to the gesture example on the input docs --- doc/input.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/input.md b/doc/input.md index 08254ea6c..480715321 100644 --- a/doc/input.md +++ b/doc/input.md @@ -63,6 +63,7 @@ class MyGame extends Game { } } ``` +You can also check a more complete example [here](/doc/examples/gestures). ## Tapable components From a2336b926d020e0d9d68a603b9c706557943fdb0 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 7 Nov 2019 18:34:13 -0300 Subject: [PATCH 12/15] Refactoring new API to use mixins --- doc/examples/gestures/lib/main.dart | 11 +- lib/game.dart | 215 ++++++++++++++++------------ lib/gestures.dart | 63 ++++++++ 3 files changed, 187 insertions(+), 102 deletions(-) create mode 100644 lib/gestures.dart diff --git a/doc/examples/gestures/lib/main.dart b/doc/examples/gestures/lib/main.dart index 1a87639bf..0232203c2 100644 --- a/doc/examples/gestures/lib/main.dart +++ b/doc/examples/gestures/lib/main.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flame/game.dart'; +import 'package:flame/gestures.dart'; void main() { final game = MyGame(); runApp(game.widget); } -class MyGame extends Game { +class MyGame extends Game with TapDetector, DoubleTapDetector, PanDetector { final _whitePaint = Paint()..color = const Color(0xFFFFFFFF); final _bluePaint = Paint()..color = const Color(0xFF0000FF); final _greenPaint = Paint()..color = const Color(0xFF00FF00); @@ -23,18 +24,10 @@ class MyGame extends Game { void onTap() { _paint = _paint == _whitePaint ? _bluePaint : _whitePaint; } - - @override - bool useDoubleTapDetectors() => true; - @override void onDoubleTap() { _paint = _greenPaint; } - - @override - bool usePanDetectors() => true; - @override void onPanUpdate(DragUpdateDetails details) { _rect = _rect.translate(details.delta.dx, details.delta.dy); diff --git a/lib/game.dart b/lib/game.dart index 2e6c41df3..7ee28b628 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -13,65 +13,13 @@ import 'components/component.dart'; import 'components/mixins/has_game_ref.dart'; import 'components/mixins/tapable.dart'; import 'position.dart'; +import 'gestures.dart'; /// Represents a generic game. /// /// Subclass this to implement the [update] and [render] methods. /// Flame will deal with calling these methods properly when the game's widget is rendered. abstract class Game { - bool useTapDetectors() => true; - void onTap() {} - void onTapCancel() {} - void onTapDown(TapDownDetails details) {} - void onTapUp(TapUpDetails details) {} - - bool useSecondaryTapDetectors() => false; - void onSecondaryTapDown(TapDownDetails details) {} - void onSecondaryTapUp(TapUpDetails details) {} - void onSecondaryTapCancel() {} - - bool useDoubleTapDetectors() => false; - void onDoubleTap() {} - - bool useLongPressDetectors() => false; - void onLongPress() {} - void onLongPressStart(LongPressStartDetails details) {} - void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} - void onLongPressUp() {} - void onLongPressEnd(LongPressEndDetails details) {} - - bool useVerticalDragDetectors() => false; - void onVerticalDragDown(DragDownDetails details) {} - void onVerticalDragStart(DragStartDetails details) {} - void onVerticalDragUpdate(DragUpdateDetails details) {} - void onVerticalDragEnd(DragEndDetails details) {} - void onVerticalDragCancel() {} - - bool useHorizontalDragDetectors() => false; - void onHorizontalDragDown(DragDownDetails details) {} - void onHorizontalDragStart(DragStartDetails details) {} - void onHorizontalDragUpdate(DragUpdateDetails details) {} - void onHorizontalDragEnd(DragEndDetails details) {} - void onHorizontalDragCancel() {} - - bool useForcePressDetectors() => false; - void onForcePressStart(ForcePressDetails details) {} - void onForcePressPeak(ForcePressDetails details) {} - void onForcePressUpdate(ForcePressDetails details) {} - void onForcePressEnd(ForcePressDetails details) {} - - bool usePanDetectors() => false; - void onPanDown(DragDownDetails details) {} - void onPanStart(DragStartDetails details) {} - void onPanUpdate(DragUpdateDetails details) {} - void onPanEnd(DragEndDetails details) {} - void onPanCancel() {} - - bool useScaleDetectors() => false; - void onScaleStart(ScaleStartDetails details) {} - void onScaleUpdate(ScaleUpdateDetails details) {} - void onScaleEnd(ScaleEndDetails details) {} - // Widget Builder for this Game final builder = WidgetBuilder(); @@ -114,61 +62,142 @@ class WidgetBuilder { Widget build(Game game) { return GestureDetector( // Taps - onTap: !game.useTapDetectors() ? null : () => game.onTap(), - onTapCancel: !game.useTapDetectors() ? null : () => game.onTapCancel(), - onTapDown: !game.useTapDetectors() ? null : (TapDownDetails d) => game.onTapDown(d), - onTapUp: !game.useTapDetectors() ? null : (TapUpDetails d) => game.onTapUp(d), + onTap: game is TapDetector ? () => (game as TapDetector).onTap() : null, + onTapCancel: game is TapDetector + ? () => (game as TapDetector).onTapCancel() + : null, + onTapDown: game is TapDetector + ? (TapDownDetails d) => (game as TapDetector).onTapDown(d) + : null, + onTapUp: game is TapDetector + ? (TapUpDetails d) => (game as TapDetector).onTapUp(d) + : null, // Secondary taps - onSecondaryTapDown: !game.useSecondaryTapDetectors() ? null : (TapDownDetails d) => game.onSecondaryTapDown(d), - onSecondaryTapUp: !game.useSecondaryTapDetectors() ? null : (TapUpDetails d) => game.onSecondaryTapUp(d), - onSecondaryTapCancel: !game.useSecondaryTapDetectors() ? null : () => game.onSecondaryTapCancel(), + onSecondaryTapDown: game is SecondaryTapDetector + ? (TapDownDetails d) => + (game as SecondaryTapDetector).onSecondaryTapDown(d) + : null, + onSecondaryTapUp: game is SecondaryTapDetector + ? (TapUpDetails d) => + (game as SecondaryTapDetector).onSecondaryTapUp(d) + : null, + onSecondaryTapCancel: game is SecondaryTapDetector + ? () => (game as SecondaryTapDetector).onSecondaryTapCancel() + : null, // Double tap - onDoubleTap: !game.useDoubleTapDetectors() ? null : () => game.onDoubleTap(), + onDoubleTap: game is DoubleTapDetector + ? () => (game as DoubleTapDetector).onDoubleTap() + : null, // Long presses - onLongPress: !game.useLongPressDetectors() ? null : () => game.onLongPress(), - onLongPressStart: !game.useLongPressDetectors() ? null : (LongPressStartDetails d) => game.onLongPressStart(d), - onLongPressMoveUpdate: !game.useLongPressDetectors() ? null : (LongPressMoveUpdateDetails d) => - game.onLongPressMoveUpdate(d), - onLongPressUp: !game.useLongPressDetectors() ? null : () => game.onLongPressUp(), - onLongPressEnd: !game.useLongPressDetectors() ? null : (LongPressEndDetails d) => game.onLongPressEnd(d), + onLongPress: game is LongPressDetector + ? () => (game as LongPressDetector).onLongPress() + : null, + onLongPressStart: game is LongPressDetector + ? (LongPressStartDetails d) => + (game as LongPressDetector).onLongPressStart(d) + : null, + onLongPressMoveUpdate: game is LongPressDetector + ? (LongPressMoveUpdateDetails d) => + (game as LongPressDetector).onLongPressMoveUpdate(d) + : null, + onLongPressUp: game is LongPressDetector + ? () => (game as LongPressDetector).onLongPressUp() + : null, + onLongPressEnd: game is LongPressDetector + ? (LongPressEndDetails d) => + (game as LongPressDetector).onLongPressEnd(d) + : null, // Vertical drag - onVerticalDragDown: !game.useVerticalDragDetectors() ? null : (DragDownDetails d) => game.onVerticalDragDown(d), - onVerticalDragStart: !game.useVerticalDragDetectors() ? null : (DragStartDetails d) => game.onVerticalDragStart(d), - onVerticalDragUpdate: !game.useVerticalDragDetectors() ? null : (DragUpdateDetails d) => - game.onVerticalDragUpdate(d), - onVerticalDragEnd: !game.useVerticalDragDetectors() ? null : (DragEndDetails d) => game.onVerticalDragEnd(d), - onVerticalDragCancel: !game.useVerticalDragDetectors() ? null : () => game.onVerticalDragCancel(), + onVerticalDragDown: game is VerticalDragDetector + ? (DragDownDetails d) => + (game as VerticalDragDetector).onVerticalDragDown(d) + : null, + onVerticalDragStart: game is VerticalDragDetector + ? (DragStartDetails d) => + (game as VerticalDragDetector).onVerticalDragStart(d) + : null, + onVerticalDragUpdate: game is VerticalDragDetector + ? (DragUpdateDetails d) => + (game as VerticalDragDetector).onVerticalDragUpdate(d) + : null, + onVerticalDragEnd: game is VerticalDragDetector + ? (DragEndDetails d) => + (game as VerticalDragDetector).onVerticalDragEnd(d) + : null, + onVerticalDragCancel: game is VerticalDragDetector + ? () => (game as VerticalDragDetector).onVerticalDragCancel() + : null, // Horizontal drag - onHorizontalDragDown: !game.useHorizontalDragDetectors() ? null : (DragDownDetails d) => game.onHorizontalDragDown(d), - onHorizontalDragStart: !game.useHorizontalDragDetectors() ? null : (DragStartDetails d) => - game.onHorizontalDragStart(d), - onHorizontalDragUpdate: !game.useHorizontalDragDetectors() ? null : (DragUpdateDetails d) => - game.onHorizontalDragUpdate(d), - onHorizontalDragEnd: !game.useHorizontalDragDetectors() ? null : (DragEndDetails d) => game.onHorizontalDragEnd(d), - onHorizontalDragCancel: !game.useHorizontalDragDetectors() ? null : () => game.onHorizontalDragCancel(), + onHorizontalDragDown: game is HorizontalDragDetector + ? (DragDownDetails d) => + (game as HorizontalDragDetector).onHorizontalDragDown(d) + : null, + onHorizontalDragStart: game is HorizontalDragDetector + ? (DragStartDetails d) => + (game as HorizontalDragDetector).onHorizontalDragStart(d) + : null, + onHorizontalDragUpdate: game is HorizontalDragDetector + ? (DragUpdateDetails d) => + (game as HorizontalDragDetector).onHorizontalDragUpdate(d) + : null, + onHorizontalDragEnd: game is HorizontalDragDetector + ? (DragEndDetails d) => + (game as HorizontalDragDetector).onHorizontalDragEnd(d) + : null, + onHorizontalDragCancel: game is HorizontalDragDetector + ? () => (game as HorizontalDragDetector).onHorizontalDragCancel() + : null, // Force presses - onForcePressStart: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressStart(d), - onForcePressPeak: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressPeak(d), - onForcePressUpdate: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressUpdate(d), - onForcePressEnd: !game.useForcePressDetectors() ? null : (ForcePressDetails d) => game.onForcePressEnd(d), + onForcePressStart: game is ForcePressDetector + ? (ForcePressDetails d) => + (game as ForcePressDetector).onForcePressStart(d) + : null, + onForcePressPeak: game is ForcePressDetector + ? (ForcePressDetails d) => + (game as ForcePressDetector).onForcePressPeak(d) + : null, + onForcePressUpdate: game is ForcePressDetector + ? (ForcePressDetails d) => + (game as ForcePressDetector).onForcePressUpdate(d) + : null, + onForcePressEnd: game is ForcePressDetector + ? (ForcePressDetails d) => + (game as ForcePressDetector).onForcePressEnd(d) + : null, // Pan - onPanDown: !game.usePanDetectors() ? null : (DragDownDetails d) => game.onPanDown(d), - onPanStart: !game.usePanDetectors() ? null : (DragStartDetails d) => game.onPanStart(d), - onPanUpdate: !game.usePanDetectors() ? null : (DragUpdateDetails d) => game.onPanUpdate(d), - onPanEnd: !game.usePanDetectors() ? null : (DragEndDetails d) => game.onPanEnd(d), - onPanCancel: !game.usePanDetectors() ? null : () => game.onPanCancel(), + onPanDown: game is PanDetector + ? (DragDownDetails d) => (game as PanDetector).onPanDown(d) + : null, + onPanStart: game is PanDetector + ? (DragStartDetails d) => (game as PanDetector).onPanStart(d) + : null, + onPanUpdate: game is PanDetector + ? (DragUpdateDetails d) => (game as PanDetector).onPanUpdate(d) + : null, + onPanEnd: game is PanDetector + ? (DragEndDetails d) => (game as PanDetector).onPanEnd(d) + : null, + onPanCancel: game is PanDetector + ? () => (game as PanDetector).onPanCancel() + : null, // Scales - onScaleStart: !game.useScaleDetectors() ? null : (ScaleStartDetails d) => game.onScaleStart(d), - onScaleUpdate: !game.useScaleDetectors() ? null : (ScaleUpdateDetails d) => game.onScaleUpdate(d), - onScaleEnd: !game.useScaleDetectors() ? null : (ScaleEndDetails d) => game.onScaleEnd(d), + onScaleStart: game is ScaleDetector + ? (ScaleStartDetails d) => (game as ScaleDetector).onScaleStart(d) + : null, + onScaleUpdate: game is ScaleDetector + ? (ScaleUpdateDetails d) => (game as ScaleDetector).onScaleUpdate(d) + : null, + onScaleEnd: game is ScaleDetector + ? (ScaleEndDetails d) => (game as ScaleDetector).onScaleEnd(d) + : null, child: Container( color: const Color(0xFF000000), @@ -184,7 +213,7 @@ class WidgetBuilder { /// It still needs to be subclasses to add your game logic, but the [update], [render] and [resize] methods have default implementations. /// This is the recommended structure to use for most games. /// It is based on the Component system. -abstract class BaseGame extends Game { +abstract class BaseGame extends Game with TapDetector { /// The list of components to be updated and rendered by the base game. OrderedSet components = OrderedSet(Comparing.on((c) => c.priority())); diff --git a/lib/gestures.dart b/lib/gestures.dart new file mode 100644 index 000000000..e2f3339da --- /dev/null +++ b/lib/gestures.dart @@ -0,0 +1,63 @@ +import 'package:flutter/gestures.dart'; + +mixin TapDetector { + void onTap() {} + void onTapCancel() {} + void onTapDown(TapDownDetails details) {} + void onTapUp(TapUpDetails details) {} +} + +mixin SecondaryTapDetector { + void onSecondaryTapDown(TapDownDetails details) {} + void onSecondaryTapUp(TapUpDetails details) {} + void onSecondaryTapCancel() {} +} + +mixin DoubleTapDetector { + void onDoubleTap() {} +} + +mixin LongPressDetector { + void onLongPress() {} + void onLongPressStart(LongPressStartDetails details) {} + void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} + void onLongPressUp() {} + void onLongPressEnd(LongPressEndDetails details) {} +} + +mixin VerticalDragDetector { + void onVerticalDragDown(DragDownDetails details) {} + void onVerticalDragStart(DragStartDetails details) {} + void onVerticalDragUpdate(DragUpdateDetails details) {} + void onVerticalDragEnd(DragEndDetails details) {} + void onVerticalDragCancel() {} +} + +mixin HorizontalDragDetector { + void onHorizontalDragDown(DragDownDetails details) {} + void onHorizontalDragStart(DragStartDetails details) {} + void onHorizontalDragUpdate(DragUpdateDetails details) {} + void onHorizontalDragEnd(DragEndDetails details) {} + void onHorizontalDragCancel() {} +} + +mixin ForcePressDetector { + void onForcePressStart(ForcePressDetails details) {} + void onForcePressPeak(ForcePressDetails details) {} + void onForcePressUpdate(ForcePressDetails details) {} + void onForcePressEnd(ForcePressDetails details) {} +} + +mixin PanDetector { + void onPanDown(DragDownDetails details) {} + void onPanStart(DragStartDetails details) {} + void onPanUpdate(DragUpdateDetails details) {} + void onPanEnd(DragEndDetails details) {} + void onPanCancel() {} +} + +mixin ScaleDetector { + void onScaleStart(ScaleStartDetails details) {} + void onScaleUpdate(ScaleUpdateDetails details) {} + void onScaleEnd(ScaleEndDetails details) {} +} From dea71c98f3639ee284086aadbd84abc2fa045da5 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 7 Nov 2019 18:42:22 -0300 Subject: [PATCH 13/15] Changing docs to reflect the refactoring --- README.md | 4 +- doc/examples/gestures/lib/main.dart | 2 + doc/input.md | 97 +++++++++++++++++------------ 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index b7298848a..2e246fa70 100644 --- a/README.md +++ b/README.md @@ -263,12 +263,12 @@ A very simple `BaseGame` implementation example can be seen below: ### Input -`Game` class provides a whole set of methods which can be overridden to get access to touch events. +Inside `package:flame/gestures.dart` you can find a whole set of `mixin` which can be included on your game class instance to be able to receive touch input events __Example__ ```dart -class MyGame extends Game { +class MyGame extends Game with TapDetector { // Other methods ommited @override diff --git a/doc/examples/gestures/lib/main.dart b/doc/examples/gestures/lib/main.dart index 0232203c2..e138355b0 100644 --- a/doc/examples/gestures/lib/main.dart +++ b/doc/examples/gestures/lib/main.dart @@ -24,10 +24,12 @@ class MyGame extends Game with TapDetector, DoubleTapDetector, PanDetector { void onTap() { _paint = _paint == _whitePaint ? _bluePaint : _whitePaint; } + @override void onDoubleTap() { _paint = _greenPaint; } + @override void onPanUpdate(DragUpdateDetails details) { _rect = _rect.translate(details.delta.dx, details.delta.dy); diff --git a/doc/input.md b/doc/input.md index 480715321..2cacfb30b 100644 --- a/doc/input.md +++ b/doc/input.md @@ -1,55 +1,70 @@ # Input -The `Game` class has a vast number of methods for handling touch controls, to use it, just override the method you want to use, and that method will start receiving events, below there is the whole list of available methods: +Inside `package:flame/gestures.dart` you can find a whole set of `mixin` which can be included on your game class instance to be able to receive touch input events. Bellow you can see the full list of these `mixin`s and its methods: ```dart - void onTap() {} - void onTapCancel() {} - void onTapDown(TapDownDetails details) {} - void onTapUp(TapUpDetails details) {} - void onSecondaryTapDown(TapDownDetails details) {} - void onSecondaryTapUp(TapUpDetails details) {} - void onSecondaryTapCancel() {} - void onDoubleTap() {} - void onLongPress() {} - void onLongPressStart(LongPressStartDetails details) {} - void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {} - void onLongPressUp() {} - void onLongPressEnd(LongPressEndDetails details) {} - void onVerticalDragDown(DragDownDetails details) {} - void onVerticalDragStart(DragStartDetails details) {} - void onVerticalDragUpdate(DragUpdateDetails details) {} - void onVerticalDragEnd(DragEndDetails details) {} - void onVerticalDragCancel() {} - void onHorizontalDragDown(DragDownDetails details) {} - void onHorizontalDragStart(DragStartDetails details) {} - void onHorizontalDragUpdate(DragUpdateDetails details) {} - void onHorizontalDragEnd(DragEndDetails details) {} - void onHorizontalDragCancel() {} - void onForcePressStart(ForcePressDetails details) {} - void onForcePressPeak(ForcePressDetails details) {} - void onForcePressUpdate(ForcePressDetails details) {} - void onForcePressEnd(ForcePressDetails details) {} - void onPanDown(DragDownDetails details) {} - void onPanStart(DragStartDetails details) {} - void onPanUpdate(DragUpdateDetails details) {} - void onPanEnd(DragEndDetails details) {} - void onPanCancel() {} - void onScaleStart(ScaleStartDetails details) {} - void onScaleUpdate(ScaleUpdateDetails details) {} - void onScaleEnd(ScaleEndDetails details) {} +- TapDetector + - onTap + - onTapCancel + - onTapDown + - onTapUp + +- SecondaryTapDetector + - onSecondaryTapDown + - onSecondaryTapUp + - onSecondaryTapCancel + +- DoubleTapDetector + - onDoubleTap + +- LongPressDetector + - onLongPress + - onLongPressStart + - onLongPressMoveUpdate + - onLongPressUp + - onLongPressEnd + +- VerticalDragDetector + - onVerticalDragDown + - onVerticalDragStart + - onVerticalDragUpdate + - onVerticalDragEnd + - onVerticalDragCancel + +- HorizontalDragDetector + - onHorizontalDragDown + - onHorizontalDragStart + - onHorizontalDragUpdate + - onHorizontalDragEnd + - onHorizontalDragCancel + +- ForcePressDetector + - onForcePressStart + - onForcePressPeak + - onForcePressUpdate + - onForcePressEnd + +- PanDetector + - onPanDown + - onPanStart + - onPanUpdate + - onPanEnd + - onPanCancel + +- ScaleDetector + - onScaleStart + - onScaleUpdate + - onScaleEnd ``` -Since many detectors conflict with each other, (for example, you can't register both Vertical and Horizontal drags) by default only the __tap detectors__ are already registered. +Many of these detectors can conflict with each other, for example, you can't register both Vertical and Horizontal drags, so not all of then can be used together. -To change that behaviour, you can override methods that enable or disable that gesutre detector for the game, for example, to enable vertical drag detectors, you should override the `useVerticalDragDetectors` to return `true`, all the other detectors have equivalent methods and follow the same logic. - -All those methods are basically a mirror from the callbacks available on the [GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), you can also read more about Flutter's gestures [here](https://api.flutter.dev/flutter/gestures/gestures-library.html). +All of these methods are basically a mirror from the callbacks available on the [GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), you can also read more about Flutter's gestures [here](https://api.flutter.dev/flutter/gestures/gestures-library.html). ## Example ```dart -class MyGame extends Game { +class MyGame extends Game with TapDetector { // Other methods ommited @override From 235cf67eaeec3954081cef4b94be065b5940af98 Mon Sep 17 00:00:00 2001 From: "Erick (CptBlackPixel)" Date: Mon, 11 Nov 2019 09:23:47 -0300 Subject: [PATCH 14/15] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 340627390..a55db61cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## [next] - Fixing FlareAnimation API to match convention - Fixing FlareComponent renderization -- New GestureDetector api to Game +- New GestureDetector API to Game ## 0.16.1 - Added `Bgm` class for easy looping background music management. From bec146afa5d0172ef873c0fdf7bdcc9fff247c09 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Tue, 12 Nov 2019 00:13:52 -0500 Subject: [PATCH 15/15] bump version --- CHANGELOG.md | 2 ++ README.md | 2 +- doc/README.md | 2 +- pubspec.yaml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a55db61cb..73e9f21f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ## [next] + +## 0.17.0 - Fixing FlareAnimation API to match convention - Fixing FlareComponent renderization - New GestureDetector API to Game diff --git a/README.md b/README.md index 2e246fa70..218a08da5 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Just drop it in your `pubspec.yaml`: ```yaml dependencies: - flame: ^0.16.1 + flame: ^0.17.0 ``` And start using it! diff --git a/doc/README.md b/doc/README.md index 766a4f669..877ee3c6c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -31,7 +31,7 @@ Put the pub package as your dependency by dropping the following in your `pubspe ```yaml dependencies: - flame: ^0.16.1 + flame: ^0.17.0 ``` And start using it! diff --git a/pubspec.yaml b/pubspec.yaml index 797d2bed9..43bad566b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flame description: A minimalist Flutter game engine, provides a nice set of somewhat independent modules you can choose from. -version: 0.16.1 +version: 0.17.0 author: Luan Nico homepage: https://github.com/flame-engine/flame