diff --git a/README.md b/README.md
index 2ac0476ad..3c58b60d8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-#
-
diff --git a/doc/input.md b/doc/input.md
index 3b633d811..4f2eb3ba6 100644
--- a/doc/input.md
+++ b/doc/input.md
@@ -316,7 +316,7 @@ Minimal example:
```dart
import 'package:flame/game.dart';
-import 'package:flame/keyboard.dart';
+import 'package:flame/input.dart';
import 'package:flutter/services.dart';
class MyGame extends Game with KeyboardEvents {
@@ -335,58 +335,17 @@ You can also check a more complete example
## Joystick
-Flame provides a component capable of creating a virtual joystick for taking input for your game. It
-can be configured with a variety of combinations, like using two sticks, or only one stick and some
-pressable buttons, and so on.
-
-To use this feature. You need to create a `JoystickComponent`, configure it the way you want, and
+Flame provides a component capable of creating a virtual joystick for taking input for your game.
+To use this feature you need to create a `JoystickComponent`, configure it the way you want, and
add it to your game.
-To receive the inputs from the joystick component, you need to have a class which implements a
-`JoystickListener`, and that class needs to be added as an observer of the joystick component
-previous created. That implementation of the the `JoystickListener` could be any class, but one
-common practice is for it to be your component that will be controlled by the joystick, like a
-player component for example.
+To receive the inputs from the joystick component, pass your `JoystickComponent` to the component
+that you want it to control, or simply act upon input from it in the update-loop of your game.
Check this example to get a better understanding:
```dart
-import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
-import 'package:flame/joystick/joystick.dart';
-import 'package:flutter/material.dart';
-
-class MyGame extends BaseGame with MultiTouchDragDetector {
- final player = Player();
- final joystick = JoystickComponent(
- componentPriority: 0,
- directional: JoystickDirectional(
- spriteBackgroundDirectional: Sprite('directional_background.png'), // optional
- spriteKnobDirectional: Sprite('directional_knob.png'), // optional
- isFixed: true, // optional
- margin: const EdgeInsets.only(left: 100, bottom: 100), // optional
- size: 80, // optional
- color: Colors.blueGrey, // optional
- opacityBackground: 0.5, // optional
- opacityKnob: 0.8, // optional
- ),
- actions: [
- JoystickAction(
- actionId: 1, // required
- sprite: Sprite('action.png'), // optional
- spritePressed: Sprite('action_pressed.png'), // optional
- spriteBackgroundDirection: Sprite('action_direction_background.png'), // optional
- enableDirection: false, // optional
- size: 50, // optional
- sizeFactorBackgroundDirection: 1.5, // optional
- margin: const EdgeInsets.all(50), // optional
- color: Colors.blueGrey, // optional
- align: JoystickActionAlign.BOTTOM_RIGHT, // optional
- opacityBackground: 0.5, // optional
- opacityKnob: 0.8, // optional
- ),
- ],
- );
+class MyGame extends BaseGame with HasDraggableComponents {
MyGame() {
joystick.addObserver(player);
@@ -395,77 +354,96 @@ class MyGame extends BaseGame with MultiTouchDragDetector {
}
@override
- void onReceiveDrag(DragEvent drag) {
- joystick.onReceiveDrag(drag);
- super.onReceiveDrag(drag);
+ Future onLoad() async {
+ super.onLoad();
+ final image = await images.load('joystick.png');
+ final sheet = SpriteSheet.fromColumnsAndRows(
+ image: image,
+ columns: 6,
+ rows: 1,
+ );
+ final joystick = JoystickComponent(
+ knob: SpriteComponent(
+ sprite: sheet.getSpriteById(1),
+ size: Vector2.all(100),
+ ),
+ background: SpriteComponent(
+ sprite: sheet.getSpriteById(0),
+ size: Vector2.all(150),
+ ),
+ margin: const EdgeInsets.only(left: 40, bottom: 40),
+ );
+
+ final player = Player(joystick);
+ add(player);
+ add(joystick);
}
}
-class Player extends Component implements JoystickListener {
+class JoystickPlayer extends SpriteComponent with HasGameRef {
+ /// Pixels/s
+ double maxSpeed = 300.0;
- @override
- void render(Canvas canvas) {}
+ final JoystickComponent joystick;
- @override
- void update(double dt) {}
-
- @override
- void joystickAction(JoystickActionEvent event) {
- // Do anything when click in action button.
- print('Action: $event');
+ JoystickPlayer(this.joystick)
+ : super(
+ size: Vector2.all(100.0),
+ ) {
+ anchor = Anchor.center;
}
@override
- void joystickChangeDirectional(JoystickDirectionalEvent event) {
- // Do anything when interact with directional.
- print('Directional: $event');
+ Future onLoad() async {
+ super.onLoad();
+ sprite = await gameRef.loadSprite('layers/player.png');
+ position = gameRef.size / 2;
}
+ @override
+ void update(double dt) {
+ super.update(dt);
+ if (joystick.direction != JoystickDirection.idle) {
+ position.add(joystick.velocity * maxSpeed * dt);
+ angle = joystick.delta.screenAngle();
+ }
+ }
}
-
```
-### JoystickDirectionalEvent
+So in this example we create the classes `MyGame` and `Player`. `MyGame` creates a joystick which is
+passed to the `Player` when it is created. In the `Player` class we act upon the current state of
+the joystick.
-```dart
+The joystick has a few fields that change depending on what state it is in.
+These are the fields that should be used to know the state of the joystick:
+ - `intensity`: The percentage [0.0, 1.0] that the knob is dragged from the epicenter to the edge of
+ the joystick (or `knobRadius` if that is set).
+ - `delta`: The absolute amount (defined as a `Vector2`) that the knob is dragged from its epicenter.
+ - `velocity`: The percentage, presented as a `Vector2`, and direction that the knob is currently
+ pulled from its base position to a edge of the joystick.
-JoystickDirectionalEvent({
- JoystickMoveDirectional directional,
- double intensity = 0.0,
- double radAngle = 0.0,
-});
+If you want to create buttons to go with your joystick, check out
+[`MarginButtonComponent`](#HudButtonComponent).
-enum JoystickMoveDirectional {
- MOVE_UP,
- MOVE_UP_LEFT,
- MOVE_UP_RIGHT,
- MOVE_RIGHT,
- MOVE_DOWN,
- MOVE_DOWN_RIGHT,
- MOVE_DOWN_LEFT,
- MOVE_LEFT,
- IDLE
-}
+A full examples of how to use it can be found
+[here](https://github.com/flame-engine/flame/tree/main/examples/lib/stories/controls/joystick).
+And it can be seen running [here](https://examples.flame-engine.org/#/Controls_Joystick).
-```
+## HudButtonComponent
+A `HudButtonComponent` is a button that can be defined with margins to the edge of the `Viewport`
+instead of with a position. It takes two `PositionComponent`s. `button` and `buttonDown`, the first
+is used for when the button is idle and the second is shown when the button is being pressed. The
+second one is optional if you don't want to change the look of the button when it is pressed, or if
+you handle this through the `button` component.
-### JoystickActionEvent
+As the name suggests this button is a hud by default, which means that it will be static on your
+screen even if the camera for the game moves around. You can also use this component as a non-hud by
+setting `hudButtonComponent.isHud = false;`.
-```dart
-
-JoystickActionEvent({
- int id,
- double intensity = 0.0,
- double radAngle = 0.0,
- ActionEvent event,
-});
-
-enum ActionEvent { DOWN, UP, MOVE, CANCEL }
-
-```
-
-You can also check a more complete example
-[here](https://github.com/flame-engine/flame/tree/main/examples/lib/stories/controls/joystick.dart).
+If you want to act upon the button being pressed (which I guess that you do) you can either pass in
+a callback function as the `onPressed` argument, or you extend the component and override
+`onTapDown`, `onTapUp` and/or `onTapCancel` and implement your logic in there.
## Gamepad
diff --git a/examples/lib/main.dart b/examples/lib/main.dart
index 3485556f2..cb0db4775 100644
--- a/examples/lib/main.dart
+++ b/examples/lib/main.dart
@@ -5,8 +5,8 @@ import 'stories/animations/animations.dart';
import 'stories/camera_and_viewport/camera_and_viewport.dart';
import 'stories/collision_detection/collision_detection.dart';
import 'stories/components/components.dart';
-import 'stories/controls/controls.dart';
import 'stories/effects/effects.dart';
+import 'stories/input/input.dart';
import 'stories/parallax/parallax.dart';
import 'stories/rendering/rendering.dart';
import 'stories/sprites/sprites.dart';
@@ -25,7 +25,7 @@ void main() async {
addCollisionDetectionStories(dashbook);
addEffectsStories(dashbook);
addTileMapStories(dashbook);
- addControlsStories(dashbook);
+ addInputStories(dashbook);
addSpritesStories(dashbook);
addRenderingStories(dashbook);
addUtilsStories(dashbook);
diff --git a/examples/lib/stories/animations/animation_group.dart b/examples/lib/stories/animations/animation_group.dart
index 2d8583ef0..abb97206b 100644
--- a/examples/lib/stories/animations/animation_group.dart
+++ b/examples/lib/stories/animations/animation_group.dart
@@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
enum RobotState {
idle,
diff --git a/examples/lib/stories/animations/basic.dart b/examples/lib/stories/animations/basic.dart
index 73cc35d5b..c64b6d27e 100644
--- a/examples/lib/stories/animations/basic.dart
+++ b/examples/lib/stories/animations/basic.dart
@@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
class BasicAnimations extends BaseGame with TapDetector {
late Image creature;
diff --git a/examples/lib/stories/camera_and_viewport/fixed_resolution.dart b/examples/lib/stories/camera_and_viewport/fixed_resolution.dart
index 6224069b1..74d7e43e9 100644
--- a/examples/lib/stories/camera_and_viewport/fixed_resolution.dart
+++ b/examples/lib/stories/camera_and_viewport/fixed_resolution.dart
@@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
class FixedResolutionGame extends BaseGame with ScrollDetector, ScaleDetector {
diff --git a/examples/lib/stories/camera_and_viewport/follow_object.dart b/examples/lib/stories/camera_and_viewport/follow_object.dart
index 937c8c768..2692ebb71 100644
--- a/examples/lib/stories/camera_and_viewport/follow_object.dart
+++ b/examples/lib/stories/camera_and_viewport/follow_object.dart
@@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
-import 'package:flame/keyboard.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
diff --git a/examples/lib/stories/camera_and_viewport/zoom.dart b/examples/lib/stories/camera_and_viewport/zoom.dart
index 86e96ed74..b9281ea69 100644
--- a/examples/lib/stories/camera_and_viewport/zoom.dart
+++ b/examples/lib/stories/camera_and_viewport/zoom.dart
@@ -1,6 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
class ZoomGame extends BaseGame with ScrollDetector, ScaleDetector {
final Vector2 viewportResolution;
diff --git a/examples/lib/stories/collision_detection/circles.dart b/examples/lib/stories/collision_detection/circles.dart
index 0151cbbaa..c60605533 100644
--- a/examples/lib/stories/collision_detection/circles.dart
+++ b/examples/lib/stories/collision_detection/circles.dart
@@ -4,7 +4,7 @@ import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart' hide Image, Draggable;
class MyCollidable extends PositionComponent
diff --git a/examples/lib/stories/collision_detection/multiple_shapes.dart b/examples/lib/stories/collision_detection/multiple_shapes.dart
index eb42856ee..fa2212003 100644
--- a/examples/lib/stories/collision_detection/multiple_shapes.dart
+++ b/examples/lib/stories/collision_detection/multiple_shapes.dart
@@ -5,7 +5,7 @@ import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart' hide Image, Draggable;
diff --git a/examples/lib/stories/collision_detection/only_shapes.dart b/examples/lib/stories/collision_detection/only_shapes.dart
index 5b7c86fdc..b48ced1f4 100644
--- a/examples/lib/stories/collision_detection/only_shapes.dart
+++ b/examples/lib/stories/collision_detection/only_shapes.dart
@@ -5,7 +5,7 @@ import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart' hide Image, Draggable;
diff --git a/examples/lib/stories/components/priority.dart b/examples/lib/stories/components/priority.dart
index 49cf4a7d2..6c4e67aa4 100644
--- a/examples/lib/stories/components/priority.dart
+++ b/examples/lib/stories/components/priority.dart
@@ -4,7 +4,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
class Square extends PositionComponent with HasGameRef, Tappable {
diff --git a/examples/lib/stories/controls/advanced_joystick.dart b/examples/lib/stories/controls/advanced_joystick.dart
deleted file mode 100644
index 39f78de1a..000000000
--- a/examples/lib/stories/controls/advanced_joystick.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-import 'package:flame/components.dart';
-import 'package:flame/extensions.dart';
-import 'package:flame/game.dart';
-import 'package:flame/joystick.dart';
-import 'package:flutter/material.dart';
-
-import 'joystick_player.dart';
-
-class AdvancedJoystickGame extends BaseGame with HasDraggableComponents {
- Future loadJoystick(int idx) async {
- return loadSprite(
- 'joystick.png',
- srcPosition: Vector2(idx * 16.0, 0),
- srcSize: Vector2.all(16),
- );
- }
-
- @override
- Future onLoad() async {
- final joystick = JoystickComponent(
- gameRef: this,
- directional: JoystickDirectional(
- background: JoystickElement.sprite(await loadJoystick(0)),
- knob: JoystickElement.sprite(await loadJoystick(1)),
- ),
- actions: [
- JoystickAction(
- actionId: 1,
- margin: const EdgeInsets.all(50),
- action: JoystickElement.sprite(await loadJoystick(2)),
- actionPressed: JoystickElement.sprite(await loadJoystick(4)),
- ),
- JoystickAction(
- actionId: 2,
- action: JoystickElement.sprite(await loadJoystick(3)),
- actionPressed: JoystickElement.sprite(await loadJoystick(5)),
- margin: const EdgeInsets.only(
- right: 50,
- bottom: 120,
- ),
- ),
- JoystickAction(
- actionId: 3,
- margin: const EdgeInsets.only(bottom: 50, right: 120),
- enableDirection: true,
- color: const Color(0xFFFF00FF),
- opacityBackground: 0.1,
- opacityKnob: 0.9,
- ),
- ],
- );
-
- final player = JoystickPlayer();
- joystick.addObserver(player);
-
- add(player);
- add(joystick);
- }
-}
diff --git a/examples/lib/stories/controls/joystick.dart b/examples/lib/stories/controls/joystick.dart
deleted file mode 100644
index fae09ba41..000000000
--- a/examples/lib/stories/controls/joystick.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'package:flame/components.dart';
-import 'package:flame/game.dart';
-import 'package:flame/joystick.dart';
-import 'package:flame/extensions.dart';
-import 'package:flutter/material.dart';
-
-import 'joystick_player.dart';
-
-class JoystickGame extends BaseGame with HasDraggableComponents {
- @override
- Future onLoad() async {
- final joystick = JoystickComponent(
- gameRef: this,
- directional: JoystickDirectional(),
- actions: [
- JoystickAction(
- actionId: 1,
- margin: const EdgeInsets.all(50),
- color: const Color(0xFF0000FF),
- ),
- JoystickAction(
- actionId: 2,
- color: const Color(0xFF00FF00),
- margin: const EdgeInsets.only(
- right: 50,
- bottom: 120,
- ),
- ),
- JoystickAction(
- actionId: 3,
- margin: const EdgeInsets.only(bottom: 50, right: 120),
- enableDirection: true,
- ),
- ],
- );
-
- final player = JoystickPlayer();
- joystick.addObserver(player);
-
- add(player);
- add(joystick);
- }
-}
diff --git a/examples/lib/stories/controls/joystick_player.dart b/examples/lib/stories/controls/joystick_player.dart
deleted file mode 100644
index 471f94fa1..000000000
--- a/examples/lib/stories/controls/joystick_player.dart
+++ /dev/null
@@ -1,87 +0,0 @@
-import 'dart:math';
-
-import 'package:flame/components.dart';
-import 'package:flame/game.dart';
-import 'package:flame/joystick.dart';
-import 'package:flame/extensions.dart';
-import 'package:flame/palette.dart';
-import 'package:flutter/material.dart';
-
-final _whitePaint = BasicPalette.white.paint();
-final _bluePaint = BasicPalette.blue.paint();
-final _greenPaint = BasicPalette.green.paint();
-
-class JoystickPlayer extends PositionComponent implements JoystickListener {
- static const speed = 64.0;
-
- double currentSpeed = 0;
- bool isMoving = false;
- Paint paint;
- late Rect rect;
-
- JoystickPlayer()
- : paint = _whitePaint,
- super(
- size: Vector2.all(50.0),
- anchor: Anchor.center,
- ) {
- rect = size.toRect();
- }
-
- @override
- void render(Canvas canvas) {
- super.render(canvas);
- canvas.drawRect(rect, paint);
- }
-
- @override
- void update(double dt) {
- super.update(dt);
- if (isMoving) {
- moveFromAngle(dt);
- }
- }
-
- @override
- void onGameResize(Vector2 gameSize) {
- super.onGameResize(gameSize);
- position = gameSize / 2;
- }
-
- @override
- void joystickAction(JoystickActionEvent event) {
- switch (event.event) {
- case ActionEvent.down:
- switch (event.id) {
- case 1:
- paint = paint == _whitePaint ? _bluePaint : _whitePaint;
- break;
- case 2:
- paint = paint == _whitePaint ? _greenPaint : _whitePaint;
- break;
- }
- break;
- case ActionEvent.move:
- if (event.id == 3) {
- angle = event.angle;
- }
- break;
- default:
- // Do nothing
- }
- }
-
- @override
- void joystickChangeDirectional(JoystickDirectionalEvent event) {
- isMoving = event.directional != JoystickMoveDirectional.idle;
- if (isMoving) {
- angle = event.angle;
- currentSpeed = speed * event.intensity;
- }
- }
-
- void moveFromAngle(double dt) {
- final delta = Vector2(cos(angle), sin(angle)) * (currentSpeed * dt);
- position.add(delta);
- }
-}
diff --git a/examples/lib/stories/effects/combined_effect.dart b/examples/lib/stories/effects/combined_effect.dart
index 4fd5d2256..ad08a088e 100644
--- a/examples/lib/stories/effects/combined_effect.dart
+++ b/examples/lib/stories/effects/combined_effect.dart
@@ -1,7 +1,7 @@
import 'package:flame/effects.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/effects/infinite_effect.dart b/examples/lib/stories/effects/infinite_effect.dart
index b5d7e619b..e27ddc27a 100644
--- a/examples/lib/stories/effects/infinite_effect.dart
+++ b/examples/lib/stories/effects/infinite_effect.dart
@@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/effects/move_effect.dart b/examples/lib/stories/effects/move_effect.dart
index 00fbc5c61..d0739d045 100644
--- a/examples/lib/stories/effects/move_effect.dart
+++ b/examples/lib/stories/effects/move_effect.dart
@@ -1,7 +1,7 @@
import 'package:flame/effects.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/effects/rotate_effect.dart b/examples/lib/stories/effects/rotate_effect.dart
index 0a19a0cea..b5aa1122a 100644
--- a/examples/lib/stories/effects/rotate_effect.dart
+++ b/examples/lib/stories/effects/rotate_effect.dart
@@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/effects/scale_effect.dart b/examples/lib/stories/effects/scale_effect.dart
index 9f996b26e..507faa53a 100644
--- a/examples/lib/stories/effects/scale_effect.dart
+++ b/examples/lib/stories/effects/scale_effect.dart
@@ -2,7 +2,7 @@ import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/effects/sequence_effect.dart b/examples/lib/stories/effects/sequence_effect.dart
index cc09a0c7a..5ee172d12 100644
--- a/examples/lib/stories/effects/sequence_effect.dart
+++ b/examples/lib/stories/effects/sequence_effect.dart
@@ -1,7 +1,7 @@
import 'package:flame/effects.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import '../../commons/square_component.dart';
diff --git a/examples/lib/stories/controls/draggables.dart b/examples/lib/stories/input/draggables.dart
similarity index 97%
rename from examples/lib/stories/controls/draggables.dart
rename to examples/lib/stories/input/draggables.dart
index 2914a3c45..fef8e7170 100644
--- a/examples/lib/stories/controls/draggables.dart
+++ b/examples/lib/stories/input/draggables.dart
@@ -1,7 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/material.dart' show Colors;
// Note: this component does not consider the possibility of multiple
diff --git a/examples/lib/stories/controls/hoverables.dart b/examples/lib/stories/input/hoverables.dart
similarity index 96%
rename from examples/lib/stories/controls/hoverables.dart
rename to examples/lib/stories/input/hoverables.dart
index dd23112e8..5919eff77 100644
--- a/examples/lib/stories/controls/hoverables.dart
+++ b/examples/lib/stories/input/hoverables.dart
@@ -1,8 +1,8 @@
import 'package:flame/components.dart';
-import 'package:flame/gestures.dart';
-import 'package:flutter/material.dart';
-import 'package:flame/game.dart';
import 'package:flame/extensions.dart';
+import 'package:flame/game.dart';
+import 'package:flame/input.dart';
+import 'package:flutter/material.dart';
class HoverableSquare extends PositionComponent with Hoverable {
static final Paint _white = Paint()..color = const Color(0xFFFFFFFF);
diff --git a/examples/lib/stories/controls/controls.dart b/examples/lib/stories/input/input.dart
similarity index 66%
rename from examples/lib/stories/controls/controls.dart
rename to examples/lib/stories/input/input.dart
index 5a1a7502b..260df7b9e 100644
--- a/examples/lib/stories/controls/controls.dart
+++ b/examples/lib/stories/input/input.dart
@@ -2,10 +2,10 @@ import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import '../../commons/commons.dart';
-import 'advanced_joystick.dart';
import 'draggables.dart';
import 'hoverables.dart';
import 'joystick.dart';
+import 'joystick_advanced.dart';
import 'keyboard.dart';
import 'mouse_movement.dart';
import 'multitap.dart';
@@ -14,42 +14,42 @@ import 'overlapping_tappables.dart';
import 'scroll.dart';
import 'tappables.dart';
-void addControlsStories(Dashbook dashbook) {
- dashbook.storiesOf('Controls')
+void addInputStories(Dashbook dashbook) {
+ dashbook.storiesOf('Input')
..add(
'Keyboard',
(_) => GameWidget(game: KeyboardGame()),
- codeLink: baseLink('controls/keyboard.dart'),
+ codeLink: baseLink('input/keyboard.dart'),
)
..add(
'Mouse Movement',
(_) => GameWidget(game: MouseMovementGame()),
- codeLink: baseLink('controls/mouse_movement.dart'),
+ codeLink: baseLink('input/mouse_movement.dart'),
)
..add(
'Scroll',
(_) => GameWidget(game: ScrollGame()),
- codeLink: baseLink('controls/scroll.dart'),
+ codeLink: baseLink('input/scroll.dart'),
)
..add(
'Multitap',
(_) => GameWidget(game: MultitapGame()),
- codeLink: baseLink('controls/multitap.dart'),
+ codeLink: baseLink('input/multitap.dart'),
)
..add(
'Multitap Advanced',
(_) => GameWidget(game: MultitapAdvancedGame()),
- codeLink: baseLink('controls/multitap_advanced.dart'),
+ codeLink: baseLink('input/multitap_advanced.dart'),
)
..add(
'Tappables',
(_) => GameWidget(game: TappablesGame()),
- codeLink: baseLink('controls/tappables.dart'),
+ codeLink: baseLink('input/tappables.dart'),
)
..add(
'Overlaping Tappables',
(_) => GameWidget(game: OverlappingTappablesGame()),
- codeLink: baseLink('controls/overlaping_tappables.dart'),
+ codeLink: baseLink('input/overlaping_tappables.dart'),
)
..add(
'Draggables',
@@ -60,22 +60,22 @@ void addControlsStories(Dashbook dashbook) {
),
);
},
- codeLink: baseLink('controls/draggables.dart'),
+ codeLink: baseLink('input/draggables.dart'),
)
..add(
'Hoverables',
(_) => GameWidget(game: HoverablesGame()),
- codeLink: baseLink('controls/hoverables.dart'),
+ codeLink: baseLink('input/hoverables.dart'),
info: 'Add more squares by clicking. Hover squares to change colors.',
)
..add(
'Joystick',
(_) => GameWidget(game: JoystickGame()),
- codeLink: baseLink('controls/joystick.dart'),
+ codeLink: baseLink('input/joystick.dart'),
)
..add(
'Joystick Advanced',
- (_) => GameWidget(game: AdvancedJoystickGame()),
- codeLink: baseLink('controls/advanced_joystick.dart'),
+ (_) => GameWidget(game: JoystickAdvancedGame()),
+ codeLink: baseLink('input/joystick_advanced.dart'),
);
}
diff --git a/examples/lib/stories/input/joystick.dart b/examples/lib/stories/input/joystick.dart
new file mode 100644
index 000000000..e2891d731
--- /dev/null
+++ b/examples/lib/stories/input/joystick.dart
@@ -0,0 +1,28 @@
+import 'package:flame/components.dart';
+import 'package:flame/game.dart';
+import 'package:flame/geometry.dart';
+import 'package:flame/input.dart';
+import 'package:flame/palette.dart';
+import 'package:flutter/painting.dart';
+
+import 'joystick_player.dart';
+
+class JoystickGame extends BaseGame with HasDraggableComponents {
+ late final JoystickPlayer player;
+ late final JoystickComponent joystick;
+
+ @override
+ Future onLoad() async {
+ final knobPaint = BasicPalette.blue.withAlpha(200).paint();
+ final backgroundPaint = BasicPalette.blue.withAlpha(100).paint();
+ joystick = JoystickComponent(
+ knob: Circle(radius: 30).toComponent(paint: knobPaint),
+ background: Circle(radius: 100).toComponent(paint: backgroundPaint),
+ margin: const EdgeInsets.only(left: 40, bottom: 40),
+ );
+ player = JoystickPlayer(joystick);
+
+ add(player);
+ add(joystick);
+ }
+}
diff --git a/examples/lib/stories/input/joystick_advanced.dart b/examples/lib/stories/input/joystick_advanced.dart
new file mode 100644
index 000000000..887e73d44
--- /dev/null
+++ b/examples/lib/stories/input/joystick_advanced.dart
@@ -0,0 +1,145 @@
+import 'dart:math';
+
+import 'package:flame/components.dart';
+import 'package:flame/effects.dart';
+import 'package:flame/extensions.dart';
+import 'package:flame/game.dart';
+import 'package:flame/geometry.dart';
+import 'package:flame/input.dart';
+import 'package:flame/palette.dart';
+import 'package:flame/sprite.dart';
+import 'package:flutter/animation.dart';
+import 'package:flutter/painting.dart';
+
+import 'joystick_player.dart';
+
+class JoystickAdvancedGame extends BaseGame
+ with HasDraggableComponents, HasTappableComponents {
+ late final JoystickPlayer player;
+ late final JoystickComponent joystick;
+ late final TextComponent speedText;
+ late final TextComponent directionText;
+
+ @override
+ Future onLoad() async {
+ final image = await images.load('joystick.png');
+ final sheet = SpriteSheet.fromColumnsAndRows(
+ image: image,
+ columns: 6,
+ rows: 1,
+ );
+ joystick = JoystickComponent(
+ knob: SpriteComponent(
+ sprite: sheet.getSpriteById(1),
+ size: Vector2.all(100),
+ ),
+ background: SpriteComponent(
+ sprite: sheet.getSpriteById(0),
+ size: Vector2.all(150),
+ ),
+ margin: const EdgeInsets.only(left: 40, bottom: 40),
+ );
+ player = JoystickPlayer(joystick);
+
+ final buttonSize = Vector2.all(80);
+ // A button with margin from the edge of the viewport that flips the
+ // rendering of the player on the X-axis.
+ final flipButton = HudButtonComponent(
+ button: SpriteComponent(
+ sprite: sheet.getSpriteById(2),
+ size: buttonSize,
+ ),
+ buttonDown: SpriteComponent(
+ sprite: sheet.getSpriteById(4),
+ size: buttonSize,
+ ),
+ margin: const EdgeInsets.only(
+ right: 80,
+ bottom: 60,
+ ),
+ onPressed: () => player.renderFlipX = !player.renderFlipX,
+ );
+
+ // A button with margin from the edge of the viewport that flips the
+ // rendering of the player on the Y-axis.
+ final flopButton = HudButtonComponent(
+ button: SpriteComponent(
+ sprite: sheet.getSpriteById(3),
+ size: buttonSize,
+ ),
+ buttonDown: SpriteComponent(
+ sprite: sheet.getSpriteById(5),
+ size: buttonSize,
+ ),
+ margin: const EdgeInsets.only(
+ right: 160,
+ bottom: 60,
+ ),
+ onPressed: () => player.renderFlipY = !player.renderFlipY,
+ );
+
+ final rotateEffect = RotateEffect(
+ angle: 0,
+ curve: Curves.bounceOut,
+ isAlternating: true,
+ speed: 2,
+ );
+ final rng = Random();
+ // A button, created from a shape, that adds a rotation effect to the player
+ // when it is pressed.
+ final shapeButton = HudButtonComponent(
+ button: Circle(radius: 35).toComponent(paint: BasicPalette.white.paint()),
+ buttonDown: Rectangle(size: buttonSize)
+ .toComponent(paint: BasicPalette.blue.paint()),
+ margin: const EdgeInsets.only(
+ right: 85,
+ bottom: 150,
+ ),
+ onPressed: () => player.addEffect(
+ rotateEffect..angle = 8 * rng.nextDouble(),
+ ),
+ );
+
+ final _regularTextConfig = TextPaintConfig(color: BasicPalette.white.color);
+ final _regular = TextPaint(config: _regularTextConfig);
+ speedText = TextComponent(
+ 'Speed: 0',
+ textRenderer: _regular,
+ )..isHud = true;
+ directionText = TextComponent(
+ 'Direction: idle',
+ textRenderer: _regular,
+ )..isHud = true;
+
+ final speedWithMargin = HudMarginComponent(
+ margin: const EdgeInsets.only(
+ top: 80,
+ left: 80,
+ ),
+ )..addChild(speedText);
+
+ final directionWithMargin = HudMarginComponent(
+ margin: const EdgeInsets.only(
+ top: 110,
+ left: 80,
+ ),
+ )..addChild(directionText);
+
+ add(player);
+ add(joystick);
+ add(flipButton);
+ add(flopButton);
+ add(shapeButton);
+ add(speedWithMargin);
+ add(directionWithMargin);
+ }
+
+ @override
+ void update(double dt) {
+ super.update(dt);
+ speedText.text = 'Speed: ${(joystick.intensity * player.maxSpeed).round()}';
+ final direction =
+ joystick.direction.toString().replaceAll('JoystickDirection.', '');
+ directionText.text = 'Direction: $direction';
+ }
+}
diff --git a/examples/lib/stories/input/joystick_player.dart b/examples/lib/stories/input/joystick_player.dart
new file mode 100644
index 000000000..182ecb3e4
--- /dev/null
+++ b/examples/lib/stories/input/joystick_player.dart
@@ -0,0 +1,32 @@
+import 'package:flame/components.dart';
+import 'package:flame/game.dart';
+
+class JoystickPlayer extends SpriteComponent with HasGameRef {
+ /// Pixels/s
+ double maxSpeed = 300.0;
+
+ final JoystickComponent joystick;
+
+ JoystickPlayer(this.joystick)
+ : super(
+ size: Vector2.all(100.0),
+ ) {
+ anchor = Anchor.center;
+ }
+
+ @override
+ Future onLoad() async {
+ super.onLoad();
+ sprite = await gameRef.loadSprite('layers/player.png');
+ position = gameRef.size / 2;
+ }
+
+ @override
+ void update(double dt) {
+ super.update(dt);
+ if (!joystick.delta.isZero()) {
+ position.add(joystick.relativeDelta * maxSpeed * dt);
+ angle = joystick.delta.screenAngle();
+ }
+ }
+}
diff --git a/examples/lib/stories/controls/keyboard.dart b/examples/lib/stories/input/keyboard.dart
similarity index 96%
rename from examples/lib/stories/controls/keyboard.dart
rename to examples/lib/stories/input/keyboard.dart
index c37bae05c..39eafd32a 100644
--- a/examples/lib/stories/controls/keyboard.dart
+++ b/examples/lib/stories/input/keyboard.dart
@@ -1,7 +1,7 @@
import 'dart:ui';
import 'package:flame/game.dart';
-import 'package:flame/keyboard.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/services.dart' show RawKeyDownEvent, RawKeyEvent;
diff --git a/examples/lib/stories/controls/mouse_movement.dart b/examples/lib/stories/input/mouse_movement.dart
similarity index 96%
rename from examples/lib/stories/controls/mouse_movement.dart
rename to examples/lib/stories/input/mouse_movement.dart
index 45e67da3e..cd20853c9 100644
--- a/examples/lib/stories/controls/mouse_movement.dart
+++ b/examples/lib/stories/input/mouse_movement.dart
@@ -1,6 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
diff --git a/examples/lib/stories/controls/multitap.dart b/examples/lib/stories/input/multitap.dart
similarity index 95%
rename from examples/lib/stories/controls/multitap.dart
rename to examples/lib/stories/input/multitap.dart
index b726df043..395fb13a0 100644
--- a/examples/lib/stories/controls/multitap.dart
+++ b/examples/lib/stories/input/multitap.dart
@@ -1,6 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
diff --git a/examples/lib/stories/controls/multitap_advanced.dart b/examples/lib/stories/input/multitap_advanced.dart
similarity index 97%
rename from examples/lib/stories/controls/multitap_advanced.dart
rename to examples/lib/stories/input/multitap_advanced.dart
index 85c4f8dcd..35e97a928 100644
--- a/examples/lib/stories/controls/multitap_advanced.dart
+++ b/examples/lib/stories/input/multitap_advanced.dart
@@ -1,6 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
diff --git a/examples/lib/stories/controls/overlapping_tappables.dart b/examples/lib/stories/input/overlapping_tappables.dart
similarity index 100%
rename from examples/lib/stories/controls/overlapping_tappables.dart
rename to examples/lib/stories/input/overlapping_tappables.dart
diff --git a/examples/lib/stories/controls/scroll.dart b/examples/lib/stories/input/scroll.dart
similarity index 96%
rename from examples/lib/stories/controls/scroll.dart
rename to examples/lib/stories/input/scroll.dart
index 7aaf360e4..25e30a380 100644
--- a/examples/lib/stories/controls/scroll.dart
+++ b/examples/lib/stories/input/scroll.dart
@@ -1,8 +1,8 @@
-import 'package:flutter/material.dart';
-import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
-import 'package:flame/palette.dart';
import 'package:flame/extensions.dart';
+import 'package:flame/game.dart';
+import 'package:flame/input.dart';
+import 'package:flame/palette.dart';
+import 'package:flutter/material.dart';
class ScrollGame extends BaseGame with ScrollDetector {
static const speed = 2000.0;
diff --git a/examples/lib/stories/controls/tappables.dart b/examples/lib/stories/input/tappables.dart
similarity index 100%
rename from examples/lib/stories/controls/tappables.dart
rename to examples/lib/stories/input/tappables.dart
diff --git a/examples/lib/stories/tile_maps/isometric_tile_map.dart b/examples/lib/stories/tile_maps/isometric_tile_map.dart
index b9c79f807..7fe1c5990 100644
--- a/examples/lib/stories/tile_maps/isometric_tile_map.dart
+++ b/examples/lib/stories/tile_maps/isometric_tile_map.dart
@@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/sprite.dart';
import 'package:flutter/material.dart' hide Image;
diff --git a/examples/lib/stories/utils/timer.dart b/examples/lib/stories/utils/timer.dart
index dd9056c92..45fc14b4b 100644
--- a/examples/lib/stories/utils/timer.dart
+++ b/examples/lib/stories/utils/timer.dart
@@ -1,7 +1,7 @@
-import 'package:flutter/material.dart';
import 'package:flame/game.dart';
+import 'package:flame/input.dart';
import 'package:flame/timer.dart';
-import 'package:flame/gestures.dart';
+import 'package:flutter/material.dart';
class TimerGame extends Game with TapDetector {
final TextPaint textConfig = TextPaint(
diff --git a/examples/lib/stories/utils/timer_component.dart b/examples/lib/stories/utils/timer_component.dart
index c59e01558..2dc9e19d5 100644
--- a/examples/lib/stories/utils/timer_component.dart
+++ b/examples/lib/stories/utils/timer_component.dart
@@ -1,7 +1,7 @@
-import 'package:flutter/material.dart';
import 'package:flame/game.dart';
+import 'package:flame/input.dart';
import 'package:flame/timer.dart';
-import 'package:flame/gestures.dart';
+import 'package:flutter/material.dart';
class RenderedTimeComponent extends TimerComponent {
final TextPaint textPaint = TextPaint(
diff --git a/examples/lib/stories/widgets/overlay.dart b/examples/lib/stories/widgets/overlay.dart
index f9574ca2b..69ba41a99 100644
--- a/examples/lib/stories/widgets/overlay.dart
+++ b/examples/lib/stories/widgets/overlay.dart
@@ -1,6 +1,6 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
diff --git a/packages/flame/CHANGELOG.md b/packages/flame/CHANGELOG.md
index 0496b3054..a0a4e89e1 100644
--- a/packages/flame/CHANGELOG.md
+++ b/packages/flame/CHANGELOG.md
@@ -1,6 +1,10 @@
# CHANGELOG
-## [next]
+## [Next]
+ - Reset effects after they are done so that they can be repeated
+ - Remove integrated joystick buttons
+ - Add `MarginHudComponent`, used when components need to have a margin to the viewport edge
+ - Refactor `JoystickComponent`
- Add `SpriteAnimationWidget.asset`
- Add `SpriteWidget.asset`
- Add `SpriteButton.asset`
diff --git a/packages/flame/example/lib/main.dart b/packages/flame/example/lib/main.dart
index dc2c0f770..4bb2f4a93 100644
--- a/packages/flame/example/lib/main.dart
+++ b/packages/flame/example/lib/main.dart
@@ -4,7 +4,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
diff --git a/packages/flame/lib/components.dart b/packages/flame/lib/components.dart
index e1e89970b..499db8f02 100644
--- a/packages/flame/lib/components.dart
+++ b/packages/flame/lib/components.dart
@@ -1,8 +1,8 @@
-export 'joystick.dart';
export 'src/anchor.dart';
export 'src/components/base_component.dart';
export 'src/components/component.dart';
export 'src/components/component_set.dart';
+export 'src/components/input/joystick_component.dart';
export 'src/components/isometric_tile_map_component.dart';
export 'src/components/mixins/collidable.dart';
export 'src/components/mixins/draggable.dart';
diff --git a/packages/flame/lib/gestures.dart b/packages/flame/lib/gestures.dart
deleted file mode 100644
index c7a15ea6e..000000000
--- a/packages/flame/lib/gestures.dart
+++ /dev/null
@@ -1,2 +0,0 @@
-export 'src/gestures/detectors.dart';
-export 'src/gestures/events.dart';
diff --git a/packages/flame/lib/input.dart b/packages/flame/lib/input.dart
new file mode 100644
index 000000000..369f27ad1
--- /dev/null
+++ b/packages/flame/lib/input.dart
@@ -0,0 +1,7 @@
+export 'src/components/input/hud_button_component.dart';
+export 'src/components/input/hud_margin_component.dart';
+export 'src/components/input/joystick_component.dart';
+export 'src/extensions/vector2.dart';
+export 'src/game/mixins/keyboard.dart';
+export 'src/gestures/detectors.dart';
+export 'src/gestures/events.dart';
diff --git a/packages/flame/lib/joystick.dart b/packages/flame/lib/joystick.dart
deleted file mode 100644
index 94d28890d..000000000
--- a/packages/flame/lib/joystick.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-export 'src/components/joystick/joystick_action.dart';
-export 'src/components/joystick/joystick_component.dart';
-export 'src/components/joystick/joystick_directional.dart';
-export 'src/components/joystick/joystick_element.dart';
-export 'src/components/joystick/joystick_events.dart';
-export 'src/sprite.dart';
diff --git a/packages/flame/lib/keyboard.dart b/packages/flame/lib/keyboard.dart
deleted file mode 100644
index 16c5a53d3..000000000
--- a/packages/flame/lib/keyboard.dart
+++ /dev/null
@@ -1 +0,0 @@
-export 'src/keyboard.dart';
diff --git a/packages/flame/lib/src/components/base_component.dart b/packages/flame/lib/src/components/base_component.dart
index e6b0e7fb6..02a2d464e 100644
--- a/packages/flame/lib/src/components/base_component.dart
+++ b/packages/flame/lib/src/components/base_component.dart
@@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:meta/meta.dart';
import '../../game.dart';
-import '../../gestures.dart';
+import '../../input.dart';
import '../effects/effects.dart';
import '../effects/effects_handler.dart';
import '../extensions/vector2.dart';
diff --git a/packages/flame/lib/src/components/input/hud_button_component.dart b/packages/flame/lib/src/components/input/hud_button_component.dart
new file mode 100644
index 000000000..ae994b636
--- /dev/null
+++ b/packages/flame/lib/src/components/input/hud_button_component.dart
@@ -0,0 +1,66 @@
+import 'package:flutter/rendering.dart' show EdgeInsets, VoidCallback;
+import 'package:meta/meta.dart';
+
+import '../../../components.dart';
+import '../../../extensions.dart';
+import '../../../input.dart';
+import '../../gestures/events.dart';
+
+class HudButtonComponent extends HudMarginComponent with Tappable {
+ late final PositionComponent button;
+ late final PositionComponent? buttonDown;
+
+ /// Callback for what should happen when the button is pressed.
+ /// If you want to interact with [onTapUp] or [onTapCancel] it is recommended
+ /// to extend [HudButtonComponent].
+ VoidCallback? onPressed;
+
+ HudButtonComponent({
+ required this.button,
+ this.buttonDown,
+ EdgeInsets? margin,
+ Vector2? position,
+ Vector2? size,
+ Anchor anchor = Anchor.center,
+ this.onPressed,
+ }) : super(
+ margin: margin,
+ position: position,
+ size: size ?? button.size,
+ anchor: anchor,
+ );
+
+ @override
+ Future onLoad() async {
+ await super.onLoad();
+ addChild(button);
+ }
+
+ @override
+ @mustCallSuper
+ bool onTapDown(TapDownInfo info) {
+ if (buttonDown != null) {
+ children.remove(button);
+ addChild(buttonDown!);
+ }
+ onPressed?.call();
+ return false;
+ }
+
+ @override
+ @mustCallSuper
+ bool onTapUp(TapUpInfo info) {
+ onTapCancel();
+ return true;
+ }
+
+ @override
+ @mustCallSuper
+ bool onTapCancel() {
+ if (buttonDown != null) {
+ children.remove(buttonDown!);
+ addChild(button);
+ }
+ return false;
+ }
+}
diff --git a/packages/flame/lib/src/components/input/hud_margin_component.dart b/packages/flame/lib/src/components/input/hud_margin_component.dart
new file mode 100644
index 000000000..f575bfe2a
--- /dev/null
+++ b/packages/flame/lib/src/components/input/hud_margin_component.dart
@@ -0,0 +1,65 @@
+import 'package:flutter/widgets.dart' show EdgeInsets;
+import 'package:meta/meta.dart';
+
+import '../../../components.dart';
+import '../../../extensions.dart';
+
+class HudMarginComponent extends PositionComponent with HasGameRef {
+ @override
+ bool isHud = true;
+
+ /// Instead of setting a position of the [HudMarginComponent] a margin
+ /// from the edges of the viewport can be used instead.
+ EdgeInsets? margin;
+
+ HudMarginComponent({
+ this.margin,
+ Vector2? position,
+ Vector2? size,
+ Anchor anchor = Anchor.topLeft,
+ }) : assert(
+ margin != null || position != null,
+ 'Either margin or position must be defined',
+ ),
+ super(
+ size: size,
+ position: position,
+ anchor: anchor,
+ );
+
+ @override
+ @mustCallSuper
+ Future onLoad() async {
+ super.onLoad();
+ if (margin != null) {
+ final margin = this.margin!;
+ final x = margin.left != 0
+ ? margin.left + size.x / 2
+ : gameRef.viewport.effectiveSize.x - margin.right - size.x / 2;
+ final y = margin.top != 0
+ ? margin.top + size.y / 2
+ : gameRef.viewport.effectiveSize.y - margin.bottom - size.y / 2;
+ position.setValues(x, y);
+ position = Anchor.center.toOtherAnchorPosition(center, anchor, size);
+ } else {
+ final topLeft = gameRef.viewport.effectiveSize -
+ anchor.toOtherAnchorPosition(
+ position,
+ Anchor.topLeft,
+ size,
+ );
+ final bottomRight = gameRef.viewport.effectiveSize -
+ anchor.toOtherAnchorPosition(
+ position,
+ Anchor.bottomRight,
+ size,
+ );
+ margin = EdgeInsets.fromLTRB(
+ topLeft.x,
+ topLeft.y,
+ bottomRight.x,
+ bottomRight.y,
+ );
+ }
+ }
+}
diff --git a/packages/flame/lib/src/components/input/joystick_component.dart b/packages/flame/lib/src/components/input/joystick_component.dart
new file mode 100644
index 000000000..8909126c1
--- /dev/null
+++ b/packages/flame/lib/src/components/input/joystick_component.dart
@@ -0,0 +1,154 @@
+import 'dart:math';
+
+import 'package:flutter/rendering.dart' show EdgeInsets;
+
+import '../../../components.dart';
+import '../../../extensions.dart';
+import '../../gestures/events.dart';
+import 'hud_margin_component.dart';
+
+enum JoystickDirection {
+ up,
+ upLeft,
+ upRight,
+ right,
+ down,
+ downRight,
+ downLeft,
+ left,
+ idle,
+}
+
+class JoystickComponent extends HudMarginComponent with Draggable {
+ late final PositionComponent knob;
+ late final PositionComponent? background;
+
+ /// The percentage [0.0, 1.0] the knob is dragged from the center to the edge.
+ double intensity = 0.0;
+
+ /// The amount the knob is dragged from the center.
+ Vector2 delta = Vector2.zero();
+
+ /// The percentage, presented as a [Vector2], and direction that the knob is
+ /// currently pulled from its base position to a edge of the joystick.
+ Vector2 get relativeDelta => delta / knobRadius;
+
+ /// The radius from the center of the knob to the edge of as far as the knob
+ /// can be dragged.
+ late double knobRadius;
+
+ /// The position where the knob rests.
+ late Vector2 _baseKnobPosition;
+
+ JoystickComponent({
+ required this.knob,
+ this.background,
+ EdgeInsets? margin,
+ Vector2? position,
+ double? size,
+ double? knobRadius,
+ Anchor anchor = Anchor.center,
+ }) : assert(
+ size != null || background != null,
+ 'Either size or background must be defined',
+ ),
+ assert(
+ knob.position.isZero() && (background?.position.isZero() ?? true),
+ 'Positions should not be set for the knob or the background',
+ ),
+ super(
+ margin: margin,
+ position: position,
+ size: background?.size ?? Vector2.all(size ?? 0),
+ anchor: anchor,
+ ) {
+ this.knobRadius = knobRadius ?? this.size.x / 2;
+ }
+
+ @override
+ Future onLoad() async {
+ await super.onLoad();
+ knob.anchor = Anchor.center;
+ knob.position.add(size / 2);
+ _baseKnobPosition = knob.position.clone();
+ if (background != null) {
+ addChild(background!);
+ }
+ addChild(knob);
+ }
+
+ @override
+ void update(double dt) {
+ super.update(dt);
+ final knobRadius2 = knobRadius * knobRadius;
+ if (delta.isZero() && _baseKnobPosition != knob.position) {
+ knob.position = _baseKnobPosition;
+ } else if (delta.length2 > knobRadius2) {
+ delta.scaleTo(knobRadius);
+ }
+ if (!delta.isZero()) {
+ knob.position
+ ..setFrom(_baseKnobPosition)
+ ..add(delta);
+ }
+ intensity = delta.length2 / knobRadius2;
+ }
+
+ @override
+ bool onDragStart(int pointerId, DragStartInfo info) {
+ return false;
+ }
+
+ @override
+ bool onDragUpdate(_, DragUpdateInfo info) {
+ delta.add(info.delta.global);
+ return false;
+ }
+
+ @override
+ bool onDragEnd(int id, __) {
+ onDragCancel(id);
+ return false;
+ }
+
+ @override
+ bool onDragCancel(_) {
+ delta.setZero();
+ return false;
+ }
+
+ static const double _eighthOfPi = pi / 8;
+
+ JoystickDirection get direction {
+ if (delta.isZero()) {
+ return JoystickDirection.idle;
+ }
+
+ var knobAngle = delta.screenAngle();
+ // Since screenAngle and angleTo doesn't care about "direction" of the angle
+ // we have to use angleToSigned and create an only increasing angle by
+ // removing negative angles from 2*pi.
+ knobAngle = knobAngle < 0 ? 2 * pi + knobAngle : knobAngle;
+ if (knobAngle >= 0 && knobAngle <= _eighthOfPi) {
+ return JoystickDirection.up;
+ } else if (knobAngle > 1 * _eighthOfPi && knobAngle <= 3 * _eighthOfPi) {
+ return JoystickDirection.upRight;
+ } else if (knobAngle > 3 * _eighthOfPi && knobAngle <= 5 * _eighthOfPi) {
+ return JoystickDirection.right;
+ } else if (knobAngle > 5 * _eighthOfPi && knobAngle <= 7 * _eighthOfPi) {
+ return JoystickDirection.downRight;
+ } else if (knobAngle > 7 * _eighthOfPi && knobAngle <= 9 * _eighthOfPi) {
+ return JoystickDirection.down;
+ } else if (knobAngle > 9 * _eighthOfPi && knobAngle <= 11 * _eighthOfPi) {
+ return JoystickDirection.downLeft;
+ } else if (knobAngle > 11 * _eighthOfPi && knobAngle <= 13 * _eighthOfPi) {
+ return JoystickDirection.left;
+ } else if (knobAngle > 13 * _eighthOfPi && knobAngle <= 15 * _eighthOfPi) {
+ return JoystickDirection.upLeft;
+ } else if (knobAngle > 15 * _eighthOfPi) {
+ return JoystickDirection.up;
+ } else {
+ return JoystickDirection.idle;
+ }
+ }
+}
diff --git a/packages/flame/lib/src/components/joystick/joystick_action.dart b/packages/flame/lib/src/components/joystick/joystick_action.dart
deleted file mode 100644
index 29b59ff6c..000000000
--- a/packages/flame/lib/src/components/joystick/joystick_action.dart
+++ /dev/null
@@ -1,228 +0,0 @@
-import 'dart:math';
-import 'dart:ui';
-
-import 'package:flutter/material.dart' show Colors;
-import 'package:flutter/widgets.dart' show EdgeInsets;
-
-import '../../../components.dart';
-import '../../../game.dart';
-import '../../extensions/offset.dart';
-import '../../extensions/rect.dart';
-import '../../extensions/vector2.dart';
-import '../../gestures/events.dart';
-import 'joystick_component.dart';
-import 'joystick_element.dart';
-import 'joystick_events.dart';
-
-enum JoystickActionAlign { topLeft, bottomLeft, topRight, bottomRight }
-
-class JoystickAction extends BaseComponent with Draggable, HasGameRef {
- @override
- bool isHud = true;
-
- final int actionId;
- final double size;
- final double _sizeBackgroundDirection;
- final EdgeInsets margin;
- final JoystickActionAlign align;
- final bool enableDirection;
-
- bool isPressed = false;
- bool _dragging = false;
- late Vector2 _dragPosition;
- late double _tileSize;
-
- late JoystickElement backgroundDirection;
- late JoystickElement action;
- late JoystickElement actionPressed;
-
- JoystickController get joystickController => parent! as JoystickController;
-
- JoystickAction({
- required this.actionId,
- JoystickElement? backgroundDirection,
- JoystickElement? action,
- JoystickElement? actionPressed,
- this.enableDirection = false,
- this.size = 50,
- this.margin = EdgeInsets.zero,
- this.align = JoystickActionAlign.bottomRight,
- double sizeFactorBackgroundDirection = 1.5,
- Color color = Colors.blueGrey,
- double opacityBackground = 0.5,
- double opacityKnob = 0.8,
- }) : _sizeBackgroundDirection = sizeFactorBackgroundDirection * size {
- this.action = action ??
- JoystickElement.paint(
- Paint()..color = color.withOpacity(opacityKnob),
- );
- this.actionPressed = actionPressed ??
- JoystickElement.paint(
- Paint()..color = color.withOpacity(opacityKnob),
- );
- this.backgroundDirection = backgroundDirection ??
- JoystickElement.paint(
- Paint()..color = color.withOpacity(opacityBackground),
- );
-
- _tileSize = _sizeBackgroundDirection / 2;
- }
-
- @override
- Future onLoad() async {
- initialize(gameRef.size);
- }
-
- @override
- void onGameResize(Vector2 gameSize) {
- super.onGameResize(gameSize);
- initialize(gameSize);
- }
-
- void initialize(Vector2 _screenSize) {
- final radius = size / 2;
- var dx = 0.0, dy = 0.0;
- switch (align) {
- case JoystickActionAlign.topLeft:
- dx = margin.left + radius;
- dy = margin.top + radius;
- break;
- case JoystickActionAlign.bottomLeft:
- dx = margin.left + radius;
- dy = _screenSize.y - (margin.bottom + radius);
- break;
- case JoystickActionAlign.topRight:
- dx = _screenSize.x - (margin.right + radius);
- dy = margin.top + radius;
- break;
- case JoystickActionAlign.bottomRight:
- dx = _screenSize.x - (margin.right + radius);
- dy = _screenSize.y - (margin.bottom + radius);
- break;
- }
- final center = Offset(dx, dy);
- action.rect = Rect.fromCircle(
- center: center,
- radius: radius,
- );
- actionPressed.rect = action.rect;
- backgroundDirection.rect = Rect.fromCircle(
- center: center,
- radius: _sizeBackgroundDirection / 2,
- );
- _dragPosition = action.center;
- }
-
- @override
- void render(Canvas c) {
- super.render(c);
- if (_dragging && enableDirection) {
- backgroundDirection.render(c);
- }
-
- (isPressed ? actionPressed : action).render(c);
- }
-
- @override
- void update(double dt) {
- super.update(dt);
-
- Vector2 diff;
- if (_dragging) {
- // Distance between the center of joystick background & drag position
- final centerPosition = backgroundDirection.center;
-
- final atanDiff = _dragPosition - centerPosition;
- final angle = atan2(atanDiff.y, atanDiff.x);
-
- final unboundDist = centerPosition.distanceTo(_dragPosition);
-
- // The maximum distance for the knob position to the edge of
- // the background + half of its own size. The knob can wander in the
- // background image, but not outside.
- final dist = min(unboundDist, _tileSize);
-
- // Calculate the knob position
- final nextX = cos(angle);
- final nextY = sin(angle);
- final nextPoint = Vector2(nextX, nextY) * dist;
-
- diff = backgroundDirection.center + nextPoint - action.center;
-
- final _intensity = dist / _tileSize;
-
- _sendEvent(ActionEvent.move, intensity: _intensity, angle: angle);
- } else {
- diff = _dragPosition - action.center;
- }
-
- action.shift(diff);
- actionPressed.rect = action.rect;
- }
-
- @override
- bool containsPoint(Vector2 point) {
- return action.rect.containsPoint(point);
- }
-
- @override
- bool onDragStart(int pointerId, DragStartInfo info) {
- if (_dragging) {
- return true;
- }
-
- if (enableDirection) {
- _dragPosition = info.eventPosition.widget;
- _dragging = true;
- }
- _sendEvent(ActionEvent.down);
- tapDown();
- return false;
- }
-
- void tapDown() {
- isPressed = true;
- }
-
- void tapUp() {
- isPressed = false;
- }
-
- @override
- bool onDragUpdate(int pointerId, DragUpdateInfo info) {
- if (_dragging) {
- _dragPosition = info.eventPosition.game;
- return true;
- }
- return false;
- }
-
- @override
- bool onDragEnd(_, __) {
- return _finishDrag(ActionEvent.up);
- }
-
- @override
- bool onDragCancel(int pointerId) {
- return _finishDrag(ActionEvent.cancel);
- }
-
- bool _finishDrag(ActionEvent event) {
- _dragging = false;
- _dragPosition = backgroundDirection.center;
- _sendEvent(event);
- tapUp();
- return true;
- }
-
- void _sendEvent(ActionEvent event, {double? intensity, double? angle}) {
- joystickController.joystickAction(
- JoystickActionEvent(
- id: actionId,
- event: event,
- intensity: intensity ?? 0.0,
- angle: angle ?? 0.0,
- ),
- );
- }
-}
diff --git a/packages/flame/lib/src/components/joystick/joystick_component.dart b/packages/flame/lib/src/components/joystick/joystick_component.dart
deleted file mode 100644
index e42c996cc..000000000
--- a/packages/flame/lib/src/components/joystick/joystick_component.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-import '../../../components.dart';
-import '../../game/base_game.dart';
-import '../mixins/has_game_ref.dart';
-import 'joystick_action.dart';
-import 'joystick_directional.dart';
-import 'joystick_events.dart';
-
-mixin JoystickListener {
- void joystickChangeDirectional(JoystickDirectionalEvent event);
- void joystickAction(JoystickActionEvent event);
-}
-
-abstract class JoystickController extends BaseComponent
- with HasGameRef, Draggable {
- @override
- bool isHud = true;
-
- final List _observers = [];
-
- void joystickChangeDirectional(JoystickDirectionalEvent event) {
- _observers.forEach((o) => o.joystickChangeDirectional(event));
- }
-
- void joystickAction(JoystickActionEvent event) {
- _observers.forEach((o) => o.joystickAction(event));
- }
-
- void addObserver(JoystickListener listener) {
- _observers.add(listener);
- }
-}
-
-class JoystickComponent extends JoystickController {
- @override
- int priority;
-
- JoystickComponent({
- required BaseGame gameRef,
- List actions = const [],
- JoystickDirectional? directional,
- this.priority = 0,
- }) {
- if (directional != null) {
- addChild(directional, gameRef: gameRef);
- }
- actions.forEach((action) => addChild(action, gameRef: gameRef));
- }
-
- void addAction(JoystickAction action) {
- addChild(action, gameRef: gameRef);
- }
-
- void removeAction(int actionId) {
- final action = children
- .firstWhere((e) => e is JoystickAction && e.actionId == actionId);
- children.remove(action);
- }
-}
diff --git a/packages/flame/lib/src/components/joystick/joystick_directional.dart b/packages/flame/lib/src/components/joystick/joystick_directional.dart
deleted file mode 100644
index d5eb5f74a..000000000
--- a/packages/flame/lib/src/components/joystick/joystick_directional.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-import 'dart:math';
-import 'dart:ui';
-
-import 'package:flutter/material.dart' show Colors;
-import 'package:flutter/widgets.dart' show EdgeInsets;
-
-import '../../../components.dart';
-import '../../../extensions.dart';
-import '../../gestures/events.dart';
-import 'joystick_component.dart';
-import 'joystick_element.dart';
-import 'joystick_events.dart';
-
-class JoystickDirectional extends BaseComponent with Draggable, HasGameRef {
- final double size;
- final bool isFixed;
- final EdgeInsets margin;
-
- late JoystickElement background;
- late JoystickElement knob;
- late double _tileSize;
-
- bool _dragging = false;
- late Vector2 _dragPosition;
- Vector2? _screenSize;
-
- JoystickController get joystickController => parent! as JoystickController;
-
- JoystickDirectional({
- JoystickElement? background,
- JoystickElement? knob,
- this.isFixed = true,
- this.margin = const EdgeInsets.only(left: 100, bottom: 100),
- this.size = 80,
- Color color = Colors.blueGrey,
- double opacityBackground = 0.5,
- double opacityKnob = 0.8,
- }) {
- this.background = background ??
- JoystickElement.paint(
- Paint()..color = color.withOpacity(opacityBackground),
- );
- this.knob = knob ??
- JoystickElement.paint(
- Paint()..color = color.withOpacity(opacityKnob),
- );
-
- _tileSize = size / 2;
- }
-
- @override
- Future onLoad() async {
- initialize(gameRef.size);
- }
-
- @override
- void onGameResize(Vector2 gameSize) {
- super.onGameResize(gameSize);
- initialize(gameSize);
- }
-
- void initialize(Vector2 screenSize) {
- _screenSize = screenSize;
-
- final osBackground = Offset(margin.left, screenSize.y - margin.bottom);
- background.rect = Rect.fromCircle(center: osBackground, radius: size / 2);
- knob.rect = Rect.fromCircle(center: osBackground, radius: size / 4);
-
- _dragPosition = osBackground.toVector2();
- }
-
- @override
- void render(Canvas canvas) {
- super.render(canvas);
- background.render(canvas);
- knob.render(canvas);
- }
-
- @override
- void update(double dt) {
- super.update(dt);
- if (_dragging) {
- // Distance between the center of joystick background & drag position
- final centerPosition = background.center;
-
- final delta = _dragPosition - centerPosition;
- final angle = atan2(delta.y, delta.x);
- final angleDegrees = angle * 180 / pi;
-
- // The maximum distance for the knob position the edge of
- // the background + half of its own size. The knob can wander in the
- // background image, but not outside.
- final dist = min(centerPosition.distanceTo(_dragPosition), _tileSize);
-
- // Calculation the knob position
- final nextX = dist * cos(angle);
- final nextY = dist * sin(angle);
- final nextPoint = Vector2(nextX, nextY);
-
- final diff = centerPosition + nextPoint - knob.center;
- knob.shift(diff);
-
- final _intensity = dist / _tileSize;
-
- JoystickMoveDirectional directional;
-
- if (_intensity == 0) {
- directional = JoystickMoveDirectional.idle;
- } else {
- directional = JoystickDirectionalEvent.calculateDirectionalByDegrees(
- angleDegrees,
- );
- }
-
- joystickController.joystickChangeDirectional(JoystickDirectionalEvent(
- directional: directional,
- intensity: _intensity,
- angle: angle,
- ));
- } else {
- final diff = _dragPosition - knob.center;
- knob.shift(diff);
- }
- }
-
- @override
- bool containsPoint(Vector2 point) {
- final directional = background.rect.inflate(50.0);
- return directional.containsPoint(point);
- }
-
- @override
- bool onDragStart(int pointerId, DragStartInfo info) {
- _updateDirectionalRect(info.eventPosition.widget);
- if (!_dragging) {
- _dragging = true;
- _dragPosition = info.eventPosition.widget;
- return true;
- }
- return false;
- }
-
- void _updateDirectionalRect(Vector2 position) {
- final screenSize = _screenSize;
- if (screenSize != null &&
- (position.x > screenSize.x / 2 ||
- position.y < screenSize.y / 2 ||
- isFixed)) {
- return;
- }
-
- background.rect = Rect.fromCircle(
- center: position.toOffset(),
- radius: size / 2,
- );
-
- knob.rect = Rect.fromCircle(
- center: position.toOffset(),
- radius: size / 4,
- );
- }
-
- @override
- bool onDragUpdate(_, DragUpdateInfo info) {
- if (_dragging) {
- _dragPosition = info.eventPosition.game;
- return false;
- }
- return true;
- }
-
- @override
- bool onDragEnd(_, __) {
- _dragging = false;
- _dragPosition = background.center;
- joystickController.joystickChangeDirectional(JoystickDirectionalEvent(
- directional: JoystickMoveDirectional.idle,
- ));
- return true;
- }
-
- @override
- bool onDragCancel(_) {
- _dragging = false;
- _dragPosition = background.center;
- joystickController.joystickChangeDirectional(JoystickDirectionalEvent(
- directional: JoystickMoveDirectional.idle,
- ));
- return true;
- }
-}
diff --git a/packages/flame/lib/src/components/joystick/joystick_element.dart b/packages/flame/lib/src/components/joystick/joystick_element.dart
deleted file mode 100644
index dca10bebc..000000000
--- a/packages/flame/lib/src/components/joystick/joystick_element.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:ui';
-
-import '../../../components.dart';
-import '../../../extensions.dart';
-
-/// This is an element drawn on the canvas on the position [rect];
-///
-/// It can be either a [sprite] or a [paint] (solid color circle). Not both.
-class JoystickElement {
- final Sprite? sprite;
- final Paint? paint;
-
- late Rect rect;
-
- JoystickElement.sprite(this.sprite) : paint = null;
-
- JoystickElement.paint(this.paint) : sprite = null;
-
- Vector2 get center => rect.center.toVector2();
-
- void shift(Vector2 diff) {
- rect = rect.shift(diff.toOffset());
- }
-
- void render(Canvas c) {
- final rect = this.rect;
- final sprite = this.sprite;
- final paint = this.paint;
-
- if (sprite == null) {
- assert(paint != null, '`paint` must not be `null` if `sprite` is `null`');
-
- final radius = rect.width / 2;
- c.drawCircle(rect.center, radius, paint!);
- } else {
- sprite.render(
- c,
- position: rect.topLeft.toVector2(),
- size: rect.size.toVector2(),
- );
- }
- }
-}
diff --git a/packages/flame/lib/src/components/joystick/joystick_events.dart b/packages/flame/lib/src/components/joystick/joystick_events.dart
deleted file mode 100644
index c4e7a5ea9..000000000
--- a/packages/flame/lib/src/components/joystick/joystick_events.dart
+++ /dev/null
@@ -1,81 +0,0 @@
-enum JoystickMoveDirectional {
- moveUp,
- moveUpLeft,
- moveUpRight,
- moveRight,
- moveDown,
- moveDownRight,
- moveDownLeft,
- moveLeft,
- idle,
-}
-
-enum ActionEvent { down, up, move, cancel }
-
-class JoystickDirectionalEvent {
- /// The direction the knob was moved towards, converted to a set of 8
- /// cardinal directions.
- final JoystickMoveDirectional directional;
-
- /// How much the knob was moved, from 0 (center) to 1 (edge).
- final double intensity;
-
- /// The direction the knob was moved towards (in radians).
- ///
- /// It uses the trigonometric circle convention (i.e. start on the
- /// positive x-axis and rotates counter-clockwise).
- final double angle;
-
- JoystickDirectionalEvent({
- required this.directional,
- this.intensity = 0.0,
- this.angle = 0.0,
- });
-
- static JoystickMoveDirectional calculateDirectionalByDegrees(double degrees) {
- if (degrees > -22.5 && degrees <= 22.5) {
- return JoystickMoveDirectional.moveRight;
- } else if (degrees > 22.5 && degrees <= 67.5) {
- return JoystickMoveDirectional.moveDownRight;
- } else if (degrees > 67.5 && degrees <= 112.5) {
- return JoystickMoveDirectional.moveDown;
- } else if (degrees > 112.5 && degrees <= 157.5) {
- return JoystickMoveDirectional.moveDownLeft;
- } else if ((degrees > 157.5 && degrees <= 180) ||
- (degrees >= -180 && degrees <= -157.5)) {
- return JoystickMoveDirectional.moveLeft;
- } else if (degrees > -157.5 && degrees <= -112.5) {
- return JoystickMoveDirectional.moveUpLeft;
- } else if (degrees > -112.5 && degrees <= -67.5) {
- return JoystickMoveDirectional.moveUp;
- } else if (degrees > -67.5 && degrees <= -22.5) {
- return JoystickMoveDirectional.moveUpRight;
- } else {
- return JoystickMoveDirectional.idle;
- }
- }
-}
-
-class JoystickActionEvent {
- /// The id of this action as defined in the setup code.
- final int id;
-
- /// What action was performed in this button.
- final ActionEvent event;
-
- /// How much the knob was moved, from 0 (center) to 1 (edge).
- final double intensity;
-
- /// The direction the knob was moved towards (in radians).
- ///
- /// It uses the trigonometric circle convention (i.e. start on the
- /// positive x-axis and rotates counter-clockwise).
- final double angle;
-
- JoystickActionEvent({
- required this.id,
- required this.event,
- this.intensity = 0.0,
- this.angle = 0.0,
- });
-}
diff --git a/packages/flame/lib/src/components/shape_component.dart b/packages/flame/lib/src/components/shape_component.dart
index 6d518fb5a..465af3859 100644
--- a/packages/flame/lib/src/components/shape_component.dart
+++ b/packages/flame/lib/src/components/shape_component.dart
@@ -12,7 +12,7 @@ class ShapeComponent extends PositionComponent {
ShapeComponent(
this.shape,
this.shapePaint, {
- Anchor anchor = Anchor.center,
+ Anchor anchor = Anchor.topLeft,
int? priority,
}) : super(
position: shape.position,
diff --git a/packages/flame/lib/src/effects/effects.dart b/packages/flame/lib/src/effects/effects.dart
index 6557d48c5..1743bf40c 100644
--- a/packages/flame/lib/src/effects/effects.dart
+++ b/packages/flame/lib/src/effects/effects.dart
@@ -36,6 +36,10 @@ abstract class ComponentEffect {
int curveDirection = 1;
Curve curve;
+ /// If this is set to true the effect will not be set to its original state
+ /// once it is done.
+ bool skipEffectReset = false;
+
double get iterationTime => peakTime * (isAlternating ? 2 : 1);
ComponentEffect(
@@ -74,6 +78,7 @@ abstract class ComponentEffect {
void dispose() => _isDisposed = true;
+ /// Whether the effect has completed or not.
bool hasCompleted() {
return (!isInfinite && !isAlternating && isMax()) ||
(!isInfinite && isAlternating && isMin()) ||
@@ -84,14 +89,20 @@ abstract class ComponentEffect {
bool isMin() => percentage == null ? false : percentage == 0.0;
bool isRootEffect() => component?.effects.contains(this) == true;
+ /// Resets the effect and the component which the effect was added to.
void reset() {
+ resetEffect();
+ setComponentToOriginalState();
+ }
+
+ /// Resets the effect to its original state so that it can be re-run.
+ void resetEffect() {
_isDisposed = false;
percentage = null;
currentTime = 0.0;
curveDirection = 1;
isInfinite = _initialIsInfinite;
isAlternating = _initialIsAlternating;
- setComponentToOriginalState();
}
// When the time overshoots the max and min it needs to add that time to
@@ -107,6 +118,16 @@ abstract class ComponentEffect {
}
}
+ /// Called when the effect is removed from the component.
+ /// Calls the [onComplete] callback if it is defined and sets the effect back
+ /// to its original state so that it can be re-added.
+ void onRemove() {
+ onComplete?.call();
+ if (!skipEffectReset) {
+ resetEffect();
+ }
+ }
+
void setComponentToOriginalState();
void setComponentToEndState();
}
diff --git a/packages/flame/lib/src/effects/effects_handler.dart b/packages/flame/lib/src/effects/effects_handler.dart
index a9e218026..2dcbcdd26 100644
--- a/packages/flame/lib/src/effects/effects_handler.dart
+++ b/packages/flame/lib/src/effects/effects_handler.dart
@@ -16,12 +16,17 @@ class EffectsHandler {
void update(double dt) {
_effects.addAll(_addLater);
_addLater.clear();
- _effects.removeWhere((e) => e.hasCompleted());
+ _effects.removeWhere((e) {
+ final hasCompleted = e.hasCompleted();
+ if (hasCompleted) {
+ e.onRemove();
+ }
+ return hasCompleted;
+ });
_effects.where((e) => !e.isPaused).forEach((e) {
e.update(dt);
if (e.hasCompleted()) {
e.setComponentToEndState();
- e.onComplete?.call();
}
});
}
diff --git a/packages/flame/lib/src/extensions/vector2.dart b/packages/flame/lib/src/extensions/vector2.dart
index 344b66737..0cfb00c59 100644
--- a/packages/flame/lib/src/extensions/vector2.dart
+++ b/packages/flame/lib/src/extensions/vector2.dart
@@ -60,9 +60,11 @@ extension Vector2Extension on Vector2 {
}
}
- /// Changes the [length] of the vector to the length provided, without changing direction.
+ /// Changes the [length] of the vector to the length provided, without
+ /// changing direction.
///
- /// If you try to scale the zero (empty) vector, it will remain unchanged, and no error will be thrown.
+ /// If you try to scale the zero (empty) vector, it will remain unchanged, and
+ /// no error will be thrown.
void scaleTo(double newLength) {
final l = length;
if (l != 0) {
@@ -93,6 +95,19 @@ extension Vector2Extension on Vector2 {
}
}
+ /// Signed angle in a coordinate system where the Y-axis is flipped.
+ ///
+ /// Since on a canvas/screen y is smaller the further up you go, instead of
+ /// larger like on a normal coordinate system, to get an angle that is in that
+ /// coordinate system we have to flip the Y-axis of the [Vector].
+ ///
+ /// Example:
+ /// Up: Vector(0.0, -1.0).screenAngle == 0
+ /// Down: Vector(0.0, 1.0).screenAngle == +-pi
+ /// Left: Vector(-1.0, 0.0).screenAngle == -pi/2
+ /// Right: Vector(-1.0, 0.0).screenAngle == pi/2
+ double screenAngle() => (clone()..y *= -1).angleToSigned(Vector2(0.0, 1.0));
+
/// Modulo/Remainder
Vector2 operator %(Vector2 mod) => Vector2(x % mod.x, y % mod.y);
diff --git a/packages/flame/lib/src/game/game.dart b/packages/flame/lib/src/game/game.dart
index 7b106ee7e..f35a33639 100644
--- a/packages/flame/lib/src/game/game.dart
+++ b/packages/flame/lib/src/game/game.dart
@@ -10,10 +10,10 @@ import '../assets/assets_cache.dart';
import '../assets/images.dart';
import '../extensions/offset.dart';
import '../extensions/vector2.dart';
-import '../keyboard.dart';
import '../sprite.dart';
import '../sprite_animation.dart';
import 'game_render_box.dart';
+import 'mixins/keyboard.dart';
import 'projector.dart';
/// Represents a generic game.
diff --git a/packages/flame/lib/src/keyboard.dart b/packages/flame/lib/src/game/mixins/keyboard.dart
similarity index 81%
rename from packages/flame/lib/src/keyboard.dart
rename to packages/flame/lib/src/game/mixins/keyboard.dart
index 081d379d1..d49525c30 100644
--- a/packages/flame/lib/src/keyboard.dart
+++ b/packages/flame/lib/src/game/mixins/keyboard.dart
@@ -1,6 +1,6 @@
import 'package:flutter/services.dart';
-import 'game/game.dart';
+import '../game.dart';
mixin KeyboardEvents on Game {
void onKeyEvent(RawKeyEvent event);
diff --git a/packages/flame/lib/src/geometry/shape.dart b/packages/flame/lib/src/geometry/shape.dart
index 1ea104a96..d7d41535a 100644
--- a/packages/flame/lib/src/geometry/shape.dart
+++ b/packages/flame/lib/src/geometry/shape.dart
@@ -2,6 +2,7 @@ import 'dart:ui';
import '../../components.dart';
import '../../game.dart';
+import '../../palette.dart';
import '../extensions/vector2.dart';
import 'shape_intersections.dart' as intersection_system;
@@ -109,10 +110,23 @@ abstract class Shape {
void render(Canvas canvas, Paint paint);
- /// Where this Shape has intersection points with another shape
+ /// Where this [Shape] has intersection points with another shape
Set intersections(Shape other) {
return intersection_system.intersections(this, other);
}
+
+ /// Turns a [Shape] into a [ShapeComponent]
+ ///
+ /// Do note that while a [Shape] is defined from the center, a
+ /// [ShapeComponent] like all other components default to an [Anchor] in the
+ /// top left corner.
+ ShapeComponent toComponent({Paint? paint, Anchor anchor = Anchor.topLeft}) {
+ return ShapeComponent(
+ this,
+ paint ?? BasicPalette.white.paint(),
+ anchor: anchor,
+ );
+ }
}
mixin HitboxShape on Shape {
diff --git a/packages/flame/test/components/draggable_test.dart b/packages/flame/test/components/draggable_test.dart
index ffc461ab4..55228c98f 100644
--- a/packages/flame/test/components/draggable_test.dart
+++ b/packages/flame/test/components/draggable_test.dart
@@ -1,6 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
diff --git a/packages/flame/test/components/joystick_component_test.dart b/packages/flame/test/components/joystick_component_test.dart
new file mode 100644
index 000000000..5d8f16e1c
--- /dev/null
+++ b/packages/flame/test/components/joystick_component_test.dart
@@ -0,0 +1,41 @@
+import 'package:flame/components.dart';
+import 'package:flame/game.dart';
+import 'package:flame/geometry.dart';
+import 'package:flame/input.dart';
+import 'package:flutter/widgets.dart';
+import 'package:test/test.dart';
+
+class TestGame extends BaseGame with HasDraggableComponents {}
+
+void main() {
+ group('JoystickDirection tests', () {
+ test('Can convert angle to JoystickDirection', () {
+ final joystick = JoystickComponent(
+ knob: Circle(radius: 5.0).toComponent(),
+ size: 20,
+ margin: const EdgeInsets.only(left: 20, bottom: 20),
+ );
+ final game = TestGame()..onResize(Vector2.all(200));
+ game.add(joystick);
+ game.update(0);
+
+ expect(joystick.direction, JoystickDirection.idle);
+ joystick.delta = Vector2(1.0, 0.0);
+ expect(joystick.direction, JoystickDirection.right);
+ joystick.delta = Vector2(0.0, -1.0);
+ expect(joystick.direction, JoystickDirection.up);
+ joystick.delta = Vector2(1.0, -1.0);
+ expect(joystick.direction, JoystickDirection.upRight);
+ joystick.delta = Vector2(-1.0, -1.0);
+ expect(joystick.direction, JoystickDirection.upLeft);
+ joystick.delta = Vector2(-1.0, 0.0);
+ expect(joystick.direction, JoystickDirection.left);
+ joystick.delta = Vector2(0.0, 1.0);
+ expect(joystick.direction, JoystickDirection.down);
+ joystick.delta = Vector2(1.0, 1.0);
+ expect(joystick.direction, JoystickDirection.downRight);
+ joystick.delta = Vector2(-1.0, 1.0);
+ expect(joystick.direction, JoystickDirection.downLeft);
+ });
+ });
+}
diff --git a/packages/flame/test/effects/combined_effect_test.dart b/packages/flame/test/effects/combined_effect_test.dart
index 0d51d1359..d075b5b9b 100644
--- a/packages/flame/test/effects/combined_effect_test.dart
+++ b/packages/flame/test/effects/combined_effect_test.dart
@@ -33,22 +33,22 @@ void main() {
path: path,
duration: randomDuration(),
isAlternating: hasAlternatingMoveEffect,
- );
+ )..skipEffectReset = true;
final rotate = RotateEffect(
angle: argumentAngle,
duration: randomDuration(),
isAlternating: hasAlternatingRotateEffect,
- );
+ )..skipEffectReset = true;
final scale = ScaleEffect(
size: argumentSize,
duration: randomDuration(),
isAlternating: hasAlternatingScaleEffect,
- );
+ )..skipEffectReset = true;
return CombinedEffect(
effects: [move, scale, rotate],
isInfinite: isInfinite,
isAlternating: isAlternating,
- );
+ )..skipEffectReset = true;
}
testWidgets('CombinedEffect can combine', (WidgetTester tester) async {
diff --git a/packages/flame/test/effects/effect_test_utils.dart b/packages/flame/test/effects/effect_test_utils.dart
index 5ec77ec92..587046b0e 100644
--- a/packages/flame/test/effects/effect_test_utils.dart
+++ b/packages/flame/test/effects/effect_test_utils.dart
@@ -89,6 +89,7 @@ void effectTest(
);
}
expect(effect.hasCompleted(), shouldComplete, reason: 'Effect shouldFinish');
+ game.update(0.0);
expect(
callback.isCalled,
shouldComplete,
diff --git a/packages/flame/test/effects/move_effect_test.dart b/packages/flame/test/effects/move_effect_test.dart
index ab825719f..6b8041830 100644
--- a/packages/flame/test/effects/move_effect_test.dart
+++ b/packages/flame/test/effects/move_effect_test.dart
@@ -18,7 +18,7 @@ void main() {
duration: 1 + random.nextInt(100).toDouble(),
isInfinite: isInfinite,
isAlternating: isAlternating,
- );
+ )..skipEffectReset = true;
}
testWidgets('MoveEffect can move', (WidgetTester tester) async {
diff --git a/packages/flame/test/effects/rotate_effect_test.dart b/packages/flame/test/effects/rotate_effect_test.dart
index 9b6e59400..9f7f39b7b 100644
--- a/packages/flame/test/effects/rotate_effect_test.dart
+++ b/packages/flame/test/effects/rotate_effect_test.dart
@@ -20,7 +20,7 @@ void main() {
isInfinite: isInfinite,
isAlternating: isAlternating,
isRelative: isRelative,
- );
+ )..skipEffectReset = true;
}
testWidgets('RotateEffect can rotate', (WidgetTester tester) async {
diff --git a/packages/flame/test/effects/scale_effect_test.dart b/packages/flame/test/effects/scale_effect_test.dart
index e97a7fc5f..210335cf3 100644
--- a/packages/flame/test/effects/scale_effect_test.dart
+++ b/packages/flame/test/effects/scale_effect_test.dart
@@ -18,7 +18,7 @@ void main() {
duration: 1 + random.nextInt(100).toDouble(),
isInfinite: isInfinite,
isAlternating: isAlternating,
- );
+ )..skipEffectReset = true;
}
testWidgets('ScaleEffect can scale', (WidgetTester tester) async {
diff --git a/packages/flame/test/effects/sequence_effect_test.dart b/packages/flame/test/effects/sequence_effect_test.dart
index 452afca23..1c45bc3d4 100644
--- a/packages/flame/test/effects/sequence_effect_test.dart
+++ b/packages/flame/test/effects/sequence_effect_test.dart
@@ -34,22 +34,22 @@ void main() {
path: path,
duration: randomDuration(),
isAlternating: hasAlternatingMoveEffect,
- );
+ )..skipEffectReset = true;
final rotate = RotateEffect(
angle: argumentAngle,
duration: randomDuration(),
isAlternating: hasAlternatingRotateEffect,
- );
+ )..skipEffectReset = true;
final scale = ScaleEffect(
size: argumentSize,
duration: randomDuration(),
isAlternating: hasAlternatingScaleEffect,
- );
+ )..skipEffectReset = true;
return SequenceEffect(
effects: [move, scale, rotate],
isInfinite: isInfinite,
isAlternating: isAlternating,
- );
+ )..skipEffectReset = true;
}
testWidgets('SequenceEffect can sequence', (WidgetTester tester) async {
diff --git a/packages/flame/test/extensions/vector2_test.dart b/packages/flame/test/extensions/vector2_test.dart
index b2e5b55a8..a409ea1e7 100644
--- a/packages/flame/test/extensions/vector2_test.dart
+++ b/packages/flame/test/extensions/vector2_test.dart
@@ -194,5 +194,20 @@ void main() {
position.rotate(math.pi / 2, center: center);
expectVector2(position, Vector2(4.0, -3.0));
});
+
+ test('screenAngle', () {
+ // Up
+ final position = Vector2(0.0, -1.0);
+ expectDouble(position.screenAngle(), 0.0);
+ // Down
+ position.setValues(0.0, 1.0);
+ expectDouble(position.screenAngle().abs(), math.pi);
+ // Left
+ position.setValues(-1.0, 0.0);
+ expectDouble(position.screenAngle(), -math.pi / 2);
+ // Right
+ position.setValues(1.0, 0.0);
+ expectDouble(position.screenAngle(), math.pi / 2);
+ });
});
}
diff --git a/packages/flame/test/util/mock_gesture_events.dart b/packages/flame/test/util/mock_gesture_events.dart
index aec07316d..26f955e0b 100644
--- a/packages/flame/test/util/mock_gesture_events.dart
+++ b/packages/flame/test/util/mock_gesture_events.dart
@@ -1,7 +1,7 @@
-import 'package:flame/game.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flame/gestures.dart';
import 'package:flame/extensions.dart';
+import 'package:flame/game.dart';
+import 'package:flame/input.dart';
+import 'package:flutter/gestures.dart';
TapDownInfo createTapDownEvent(
Game game, {
diff --git a/packages/flame_audio/example/lib/main.dart b/packages/flame_audio/example/lib/main.dart
index d378f7c7f..ac12b718c 100644
--- a/packages/flame_audio/example/lib/main.dart
+++ b/packages/flame_audio/example/lib/main.dart
@@ -1,7 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flame_audio/audio_pool.dart';
import 'package:flame_audio/flame_audio.dart';
diff --git a/packages/flame_fire_atlas/example/lib/main.dart b/packages/flame_fire_atlas/example/lib/main.dart
index 4bce901b2..65c56af18 100644
--- a/packages/flame_fire_atlas/example/lib/main.dart
+++ b/packages/flame_fire_atlas/example/lib/main.dart
@@ -1,9 +1,9 @@
-import 'package:flutter/material.dart';
+import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
-import 'package:flame/components.dart';
+import 'package:flame/input.dart';
import 'package:flame_fire_atlas/flame_fire_atlas.dart';
+import 'package:flutter/material.dart';
void main() async {
try {
diff --git a/packages/flame_forge2d/example/lib/blob_sample.dart b/packages/flame_forge2d/example/lib/blob_sample.dart
index 633639bac..64363ec7d 100644
--- a/packages/flame_forge2d/example/lib/blob_sample.dart
+++ b/packages/flame_forge2d/example/lib/blob_sample.dart
@@ -1,10 +1,11 @@
import 'dart:math' as math;
-import 'package:flame_forge2d/body_component.dart';
-import 'package:forge2d/forge2d.dart';
+
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
+import 'package:flame_forge2d/body_component.dart';
import 'package:flame_forge2d/forge2d_game.dart';
+import 'package:forge2d/forge2d.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/camera_sample.dart b/packages/flame_forge2d/example/lib/camera_sample.dart
index e1ea54d64..33d3ab005 100644
--- a/packages/flame_forge2d/example/lib/camera_sample.dart
+++ b/packages/flame_forge2d/example/lib/camera_sample.dart
@@ -1,4 +1,5 @@
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
+
import 'domino_sample.dart';
class CameraSample extends DominoSample {
diff --git a/packages/flame_forge2d/example/lib/circle_stress_sample.dart b/packages/flame_forge2d/example/lib/circle_stress_sample.dart
index f94dfae74..04572e90d 100644
--- a/packages/flame_forge2d/example/lib/circle_stress_sample.dart
+++ b/packages/flame_forge2d/example/lib/circle_stress_sample.dart
@@ -1,10 +1,10 @@
import 'dart:math';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/body_component.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
-import 'package:forge2d/forge2d.dart';
-import 'package:flame/gestures.dart';
import 'package:flame_forge2d/forge2d_game.dart';
+import 'package:forge2d/forge2d.dart';
import 'balls.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/contact_callbacks_sample.dart b/packages/flame_forge2d/example/lib/contact_callbacks_sample.dart
index 5fbfd36d6..a04b3f519 100644
--- a/packages/flame_forge2d/example/lib/contact_callbacks_sample.dart
+++ b/packages/flame_forge2d/example/lib/contact_callbacks_sample.dart
@@ -1,8 +1,8 @@
import 'dart:math' as math;
-import 'package:forge2d/forge2d.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/forge2d_game.dart';
+import 'package:forge2d/forge2d.dart';
import 'balls.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/domino_sample.dart b/packages/flame_forge2d/example/lib/domino_sample.dart
index 6613a44b9..8ee39ab94 100644
--- a/packages/flame_forge2d/example/lib/domino_sample.dart
+++ b/packages/flame_forge2d/example/lib/domino_sample.dart
@@ -1,11 +1,11 @@
import 'dart:ui';
+import 'package:flame/components.dart';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/body_component.dart';
+import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flame_forge2d/sprite_body_component.dart';
import 'package:forge2d/forge2d.dart';
-import 'package:flame/components.dart';
-import 'package:flame/gestures.dart';
-import 'package:flame_forge2d/forge2d_game.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/draggable_sample.dart b/packages/flame_forge2d/example/lib/draggable_sample.dart
index 8158b10b4..c3839f834 100644
--- a/packages/flame_forge2d/example/lib/draggable_sample.dart
+++ b/packages/flame_forge2d/example/lib/draggable_sample.dart
@@ -1,10 +1,10 @@
-import 'package:flame_forge2d/flame_forge2d.dart';
-import 'package:flutter/material.dart' hide Draggable;
-import 'package:forge2d/forge2d.dart';
-import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:flame_forge2d/forge2d_game.dart';
+import 'package:flutter/material.dart' hide Draggable;
+import 'package:forge2d/forge2d.dart';
import 'balls.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/mouse_joint_sample.dart b/packages/flame_forge2d/example/lib/mouse_joint_sample.dart
index d7343dcbb..50a9ac200 100644
--- a/packages/flame_forge2d/example/lib/mouse_joint_sample.dart
+++ b/packages/flame_forge2d/example/lib/mouse_joint_sample.dart
@@ -1,7 +1,7 @@
+import 'package:flame/extensions.dart';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_forge2d/forge2d_game.dart';
-import 'package:flame/gestures.dart';
-import 'package:flame/extensions.dart';
import 'balls.dart';
import 'boundaries.dart';
diff --git a/packages/flame_forge2d/example/lib/position_body_sample.dart b/packages/flame_forge2d/example/lib/position_body_sample.dart
index 4aeaf216c..c4458cd61 100644
--- a/packages/flame_forge2d/example/lib/position_body_sample.dart
+++ b/packages/flame_forge2d/example/lib/position_body_sample.dart
@@ -1,7 +1,7 @@
import 'dart:ui';
import 'package:flame/components.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flame_forge2d/position_body_component.dart';
import 'package:forge2d/forge2d.dart';
diff --git a/packages/flame_forge2d/example/lib/sprite_body_sample.dart b/packages/flame_forge2d/example/lib/sprite_body_sample.dart
index 4b5c2991a..425e5b038 100644
--- a/packages/flame_forge2d/example/lib/sprite_body_sample.dart
+++ b/packages/flame_forge2d/example/lib/sprite_body_sample.dart
@@ -1,10 +1,10 @@
import 'dart:ui';
-import 'package:forge2d/forge2d.dart';
import 'package:flame/components.dart';
-import 'package:flame/gestures.dart';
+import 'package:flame/input.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flame_forge2d/sprite_body_component.dart';
+import 'package:forge2d/forge2d.dart';
import 'boundaries.dart';
diff --git a/tutorials/2_sprite_animations_gestures/code/lib/main.dart b/tutorials/2_sprite_animations_gestures/code/lib/main.dart
index 4fb033fca..64c00a17d 100644
--- a/tutorials/2_sprite_animations_gestures/code/lib/main.dart
+++ b/tutorials/2_sprite_animations_gestures/code/lib/main.dart
@@ -1,7 +1,7 @@
-import 'package:flame/gestures.dart';
+import 'package:flame/game.dart';
+import 'package:flame/input.dart';
import 'package:flame/sprite.dart';
import 'package:flutter/material.dart';
-import 'package:flame/game.dart';
void main() {
final myGame = MyGame();