mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-15 12:19:40 +08:00
* Game as a component * Fix component stories * Effects are now components * Update effects docs * Handle swap of parent * Fix reAddChildren * Wait for children to be added * BaseComponent and PositionComponent to be non-abstract * Simplify HasGameRef * Revert so that onLoad can be null * Fix example description * Effects as components * Remove gameRef from addChildren * Fix hasGameRef * Start migrating effects * Updated comments of effect fields * Fix comments * Continue to fix sequence and combined effects * Upgrade ordered_set * Fix position_component_test * BaseComponent -> Component * Fix combined and sequence effects * Await components to be added in tests * Remove unnecessary game.update in tests * Fix some tests related to composition * BaseGame should be used in examples * Fix CombinedEffect test * Keyboard code to be based on Component * Fix keyboard tests * Fix analyze problems * Fix sequence_effect * Fix combined_effect_test * Store peak state instead of end state * Fix sequence_effect tests * Update tutorial * Fix tutorial 1 * Remove SimplePositionComponentEffect * Remove unused test variable * Update docs * Removed onMount * Remove onMount * Add missing dartdoc * Fix dart docs * Add super.update where needed * Move reAddChildren to component * Reorganize method order in game widget * preOffset -> initialDelay, postOffset -> peakDelay * Introduce component.onParentChange * Remove tests in wrong file * Fix composed component test * Add game lifecycle test * Use BaseGame for mouse cursor test * Oxygen should (?) not call super.update * Use BaseGame in keyboard_test * Fix onLoad to be properly cached * Re-add unintentionally removed override * Fix info for collision detection tests * Add test for correct lifecycle on parent change * Fix particles example * Add component lifecycle diagram to the docs * Add docs for the game lifecycle * onRemove should be called when a game is removed from the widget * Fix analyze errors * prepare should be called from the component itself, not its parent * Fix dartdoc * onParentChange -> onMount * onMount should have void as return type * Simplify the loaderFuture in GameWidget * Fix mock_canvas * Fix rebase problem * Remove asComponent * Less complex _loaderFuture * Add super.update to no_fcs parallax example * Fix async tests * Revert _loaderFuture * Fix analyze issues * await gameWithCollidables * Keep epsilon small where it can be * tappable methods should return bool * Game lifecycle is now the same as for Component * Remove mustCallSuper from component.update * Make onLoadCache protected * @internal on onLoadCache * Cache/Memoize debugPaint and debugTextPaint * Fix imports * Fix comments * Always call super.onLoad so that mixins can override it * Add forgotten super.onLoad * Bump coverage percentage * HasCollidables should override update * Fix Game comments * Fix some dartdoc * Apply suggestions from code review Co-authored-by: Erick <erickzanardoo@gmail.com> * Game + Loadable as mixins * Update packages/flame/lib/src/game/game_widget/game_widget.dart Co-authored-by: Luan Nico <luanpotter27@gmail.com> * Update loadable docs * Fix comments * Move fps_counter * Fix keyboard example * Fix dartdoc * Remove tutorials temporarily * Fix game lowlevel graph * Fix resize issue Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Luan Nico <luanpotter27@gmail.com>
334 lines
9.6 KiB
Markdown
334 lines
9.6 KiB
Markdown
# Gesture Input
|
|
|
|
This includes documentation for gesture inputs, which is, mouse and touch pointers.
|
|
|
|
For other input documents, see also:
|
|
|
|
- [Keyboard Input](keyboard-input.md): for keystrokes
|
|
- [Other Inputs](other-inputs.md): For joysticks, game pads, etc.
|
|
|
|
## Intro
|
|
|
|
Inside `package:flame/gestures.dart` you can find a whole set of `mixin`s which can be included on
|
|
your game class instance to be able to receive touch input events. Below you can see the full list
|
|
of these `mixin`s and its methods:
|
|
|
|
## Touch and mouse detectors
|
|
```
|
|
- 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
|
|
|
|
- MultiTouchTapDetector
|
|
- onTap
|
|
- onTapCancel
|
|
- onTapDown
|
|
- onTapUp
|
|
|
|
- MultiTouchDragDetector
|
|
- onReceiveDrag
|
|
```
|
|
|
|
Mouse only events
|
|
|
|
```
|
|
- MouseMovementDetector
|
|
- onMouseMove
|
|
- ScrollDetector
|
|
- onScroll
|
|
```
|
|
|
|
Many of these detectors can conflict with each other. For example, you can't register both vertical
|
|
and horizontal drags, so not all of them can be used together.
|
|
|
|
It is also not possible to mix advanced detectors (`MultiTouch*`) with basic detectors as they will
|
|
*always win the gesture arena* and the basic detectors will never be triggered. So for example, you
|
|
can use both `MultiTouchDragDetector` and `MultiTouchDragDetector` together, but if you try to use
|
|
`MultiTouchTapDetector` and `PanDetector`, no events will be triggered for the latter.
|
|
|
|
Flame's GestureApi is provided by Flutter's Gesture Widgets, including
|
|
GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html),
|
|
[RawGestureDetector widget](https://api.flutter.dev/flutter/widgets/RawGestureDetector-class.html)
|
|
and [MouseRegion widget](https://api.flutter.dev/flutter/widgets/MouseRegion-class.html), you can
|
|
also read more about Flutter's gestures
|
|
[here](https://api.flutter.dev/flutter/gestures/gestures-library.html).
|
|
|
|
It is also possible to change the current mouse cursor displayed on the `GameWidget` region. To do
|
|
so the following code can be used inside the `Game` class
|
|
|
|
```dart
|
|
mouseCursor.value = SystemMouseCursors.move;
|
|
```
|
|
|
|
To already initialize the `GameWidget` with a custom cursor, the `mouseCursor` property can be used
|
|
|
|
```dart
|
|
GameWidget(
|
|
game: MouseCursorGame(),
|
|
mouseCursor: SystemMouseCursors.move,
|
|
);
|
|
```
|
|
|
|
## Event coordinate system
|
|
|
|
On events that have positions, like for example `Tap*` or `Drag`, you will notice that the `eventPosition`
|
|
attribute includes 3 fields: `game`, `widget` and `global`. Below you will find a brief explanation
|
|
about each one of them.
|
|
|
|
### global
|
|
|
|
The position where the event occurred considering the entire screen, same as
|
|
`globalPosition` in Flutter's native events.
|
|
|
|
### widget
|
|
|
|
The position where the event occurred relative to the `GameWidget` position and size
|
|
, same as `localPosition` in Flutter's native events.
|
|
|
|
### game
|
|
|
|
The position where the event ocurred relative to the `GameWidget` and with any
|
|
transformations that the game applied to the game (e.g. camera). If the game doesn't have any
|
|
transformations, this will be equal to the `widget` attribute.
|
|
|
|
## Example
|
|
|
|
```dart
|
|
class MyGame extends Game with TapDetector {
|
|
// Other methods omitted
|
|
|
|
@override
|
|
bool onTapDown(TapDownInfo event) {
|
|
print("Player tap down on ${event.eventPosition.game}");
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
bool onTapUp(TapUpInfo event) {
|
|
print("Player tap up on ${event.eventPosition.game}");
|
|
return true;
|
|
}
|
|
}
|
|
```
|
|
|
|
You can also check more complete examples
|
|
[here](https://github.com/flame-engine/flame/tree/main/examples/lib/stories/controls/).
|
|
|
|
## Tappable, Draggable and Hoverable components
|
|
|
|
Any component derived from `BaseComponent` (most components) can add the `Tappable`, the
|
|
`Draggable`, and/or the `Hoverable` mixins to handle taps, drags and hovers on the component.
|
|
|
|
All overridden methods return a boolean to control if the event should be passed down further along
|
|
to components underneath it. So say that you only want your top visible component to receive a tap
|
|
and not the ones underneath it, then your `onTapDown`, `onTapUp` and `onTapCancel` implementations
|
|
should return `false` and if you want the event to go through more of the components underneath then
|
|
you should return `true`.
|
|
|
|
The same applies if your component has children, then the event is first sent to the leaves in the
|
|
children tree and then passed further down until a method returns `false`.
|
|
|
|
### Tappable components
|
|
|
|
By adding the `HasTappableComponents` mixin to your game, and using the mixin `Tappable` on your
|
|
components, you can override the following methods on your components:
|
|
|
|
```dart
|
|
bool onTapCancel();
|
|
bool onTapDown(TapDownInfo event);
|
|
bool onTapUp(TapUpInfo event);
|
|
```
|
|
|
|
Minimal component example:
|
|
|
|
```dart
|
|
import 'package:flame/components.dart';
|
|
|
|
class TappableComponent extends PositionComponent with Tappable {
|
|
|
|
// update and render omitted
|
|
|
|
@override
|
|
bool onTapUp(TapUpInfo event) {
|
|
print("tap up");
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
bool onTapDown(TapDownInfo event) {
|
|
print("tap down");
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
bool onTapCancel() {
|
|
print("tap cancel");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class MyGame extends FlameGame with HasTappableComponents {
|
|
MyGame() {
|
|
add(TappableComponent());
|
|
}
|
|
}
|
|
```
|
|
|
|
**Note**: `HasTappableComponents` uses an advanced gesture detector under the hood and as explained
|
|
further up on this page it shouldn't be used alongside basic detectors.
|
|
|
|
### Draggable components
|
|
|
|
Just like with `Tappable`, Flame offers a mixin for `Draggable`.
|
|
|
|
By adding the `HasDraggableComponents` mixin to your game, and by using the mixin `Draggable` on
|
|
your components, they can override the simple methods that enable an easy to use drag api on your
|
|
components.
|
|
|
|
```dart
|
|
bool onDragStart(int pointerId, Vector2 startPosition);
|
|
bool onDragUpdate(int pointerId, DragUpdateInfo event);
|
|
bool onDragEnd(int pointerId, DragEndInfo event);
|
|
bool onDragCancel(int pointerId);
|
|
```
|
|
|
|
Note that all events take a uniquely generated pointer id so you can, if desired, distinguish
|
|
between different simultaneous drags.
|
|
|
|
The default implementation provided by `Draggable` will already check:
|
|
|
|
- upon drag start, the component only receives the event if the position is within its bounds; keep
|
|
track of pointerId.
|
|
- when handling updates/end/cancel, the component only receives the event if the pointerId was
|
|
tracked (regardless of position).
|
|
- on end/cancel, stop tracking pointerId.
|
|
|
|
Minimal component example (this example ignores pointerId so it wont work well if you try to
|
|
multi-drag):
|
|
|
|
```dart
|
|
import 'package:flame/components.dart';
|
|
|
|
class DraggableComponent extends PositionComponent with Draggable {
|
|
|
|
// update and render omitted
|
|
|
|
Vector2 dragDeltaPosition;
|
|
bool get isDragging => dragDeltaPosition != null;
|
|
|
|
@override
|
|
bool onDragStart(int pointerId, Vector2 startPosition) {
|
|
dragDeltaPosition = startPosition - position;
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
bool onDragUpdate(int pointerId, DragUpdateInfo event) {
|
|
final localCoords = event.eventPosition.game;
|
|
position = localCoords - dragDeltaPosition;
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
bool onDragEnd(int pointerId, DragEndInfo event) {
|
|
dragDeltaPosition = null;
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
bool onDragCancel(int pointerId) {
|
|
dragDeltaPosition = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class MyGame extends FlameGame with HasDraggableComponents {
|
|
MyGame() {
|
|
add(DraggableComponent());
|
|
}
|
|
}
|
|
```
|
|
|
|
**Note**: `HasDraggableComponents` uses an advanced gesture detector under the hood and as explained
|
|
further up on this page, shouldn't be used alongside basic detectors.
|
|
|
|
### Hoverable components
|
|
|
|
Just like the others, this mixin allows for easy wiring of your component to listen to hover states
|
|
and events.
|
|
|
|
By adding the `HasHoverableComponents` mixin to your base game, and by using the mixin `Hoverable` on
|
|
your components, they get an `isHovered` field and a couple of methods (`onHoverStart`, `onHoverEnd`) that
|
|
you can override if you want to listen to the events.
|
|
|
|
```dart
|
|
bool isHovered = false;
|
|
void onHoverEnter(PointerHoverInfo event) {}
|
|
void onHoverLeave(PointerHoverInfo event) {}
|
|
```
|
|
|
|
The provided event info is from the mouse move that triggered the action (entering or leaving).
|
|
While the mouse movement is kept inside or outside, no events are fired and those mouse move events are
|
|
not propagated. Only when the state is changed the handlers are triggered.
|
|
|
|
## Hitbox
|
|
The `Hitbox` mixin is used to make detection of gestures on top of your `PositionComponent`s more
|
|
accurate. Say that you have a fairly round rock as a `SpriteComponent` for example, then you don't
|
|
want to register input that is in the corner of the image where the rock is not displayed. Then you
|
|
can use the `Hitbox` mixin to define a more accurate polygon for which the input should be within
|
|
for the event to be counted on your component.
|
|
|
|
An example of you to use it can be seen
|
|
[here](https://github.com/flame-engine/flame/tree/main/examples/lib/stories/).
|
|
|
|
|