Merge branch 'develop' into develop

This commit is contained in:
Luan Nico
2019-11-27 11:37:12 -05:00
committed by GitHub
15 changed files with 477 additions and 80 deletions

View File

@@ -1,7 +1,10 @@
## [next]
## 0.17.0
- Fixing FlareAnimation API to match convention
- Fixing FlareComponent renderization
- Added default render fucntion for Box2D ChainShape
- New GestureDetector API to Game
## 0.16.1
- Added `Bgm` class for easy looping background music management.

6
FAQ.md
View File

@@ -52,6 +52,12 @@ If you are using `BaseGame`, you have a `camera` attribute that allows you to of
For a more in-depth tutorial on how the camera works (in general & in Flame) and how to use it, check [erickzanardo](https://github.com/erickzanardo)'s [excellent tutorial](https://fireslime.xyz/articles/20190911_Basic_Camera_Usage_In_Flame.html), published via FireSlime.
## How to handle touch events on your game?
You can always use all the Widgets and features that Flutter already provide for that, but Flame wraps gesture detector callbacks on its base Game class so it can ben a little easier to handle those events, you can find more about it on the input documentation page:
https://flame-engine.org/docs/input.md
## Other questions?
Didn't find what you needed here? Please head to [FireSlime's Discord channel](https://discord.gg/pxrBmy4) where the community might help you with more questions.

View File

@@ -63,7 +63,7 @@ Just drop it in your `pubspec.yaml`:
```yaml
dependencies:
flame: ^0.16.1
flame: ^0.17.0
```
And start using it!
@@ -263,21 +263,26 @@ A very simple `BaseGame` implementation example can be seen below:
### Input
In order to handle user input, you can use the libraries provided by Flutter for regular apps: [Gesture Recognizers](https://flutter.io/gestures/).
Inside `package:flame/gestures.dart` you can find a whole set of `mixin` which can be included on your game class instance to be able to receive touch input events
However, in order to bind them, use the `Flame.util.addGestureRecognizer` method; in doing so, you'll make sure they are properly unbound when the game widget is not being rendered, and so the rest of your screens will work appropriately.
For example, to add a tap listener ("on click"):
__Example__
```dart
Flame.util.addGestureRecognizer(new TapGestureRecognizer()
..onTapDown = (TapDownDetails evt) => game.handleInput(evt.globalPosition.dx, evt.globalPosition.dy));
class MyGame extends Game with TapDetector {
// Other methods ommited
@override
void onTapDown(TapDownDetails details) {
print("Player tap down on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
}
@override
void onTapUp(TapUpDetails details) {
print("Player tap up on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
}
}
```
Where `game` is a reference to your game object and `handleInput` is a method you create to handle the input inside your game.
If your game doesn't have other screens, just call this after your `runApp` call, in the `main` method.
[Complete Input Guide](doc/input.md)
## Credits

View File

@@ -31,7 +31,7 @@ Put the pub package as your dependency by dropping the following in your `pubspe
```yaml
dependencies:
flame: ^0.16.1
flame: ^0.17.0
```
And start using it!

72
doc/examples/gestures/.gitignore vendored Normal file
View File

@@ -0,0 +1,72 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
/build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 841707365a9be08f2219cbafc52c52d6af5355aa
channel: master
project_type: app

View File

@@ -0,0 +1,3 @@
# gestures
A flame game showcasing the use of gestures callbacks

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
void main() {
final game = MyGame();
runApp(game.widget);
}
class MyGame extends Game with TapDetector, DoubleTapDetector, PanDetector {
final _whitePaint = Paint()..color = const Color(0xFFFFFFFF);
final _bluePaint = Paint()..color = const Color(0xFF0000FF);
final _greenPaint = Paint()..color = const Color(0xFF00FF00);
Paint _paint;
Rect _rect = const Rect.fromLTWH(50, 50, 50, 50);
MyGame() {
_paint = _whitePaint;
}
@override
void onTap() {
_paint = _paint == _whitePaint ? _bluePaint : _whitePaint;
}
@override
void onDoubleTap() {
_paint = _greenPaint;
}
@override
void onPanUpdate(DragUpdateDetails details) {
_rect = _rect.translate(details.delta.dx, details.delta.dy);
}
@override
void update(double dt) {}
@override
void render(Canvas canvas) {
canvas.drawRect(_rect, _paint);
}
}

View File

@@ -0,0 +1,16 @@
name: gestures
description: A flame game showcasing the use of gestures callbacks
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flame:
path: ../../../
flutter:
uses-material-design: false

View File

@@ -1,41 +1,84 @@
# Input
In order to handle user input, you can use the libraries provided by Flutter for regular apps: [Gesture Recognizers](https://flutter.io/gestures/).
However, in order to bind them, use the `Flame.util.addGestureRecognizer` method; in doing so, you'll make sure they are properly unbound when the game widget is not being rendered, and so the rest of your screens will work appropriately.
For example, to add a tap listener ("on click"):
Inside `package:flame/gestures.dart` you can find a whole set of `mixin` which can be included on your game class instance to be able to receive touch input events. Bellow you can see the full list of these `mixin`s and its methods:
```dart
Flame.util.addGestureRecognizer(new TapGestureRecognizer()
..onTapDown = (TapDownDetails evt) => game.handleInput(evt.globalPosition.dx, evt.globalPosition.dy));
- 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
```
Where `game` is a reference to your game object and `handleInput` is a method you create to handle the input inside your game.
Many of these detectors can conflict with each other, for example, you can't register both Vertical and Horizontal drags, so not all of then can be used together.
If your game doesn't have other screens, just call this after your `runApp` call, in the `main` method.
All of these methods are basically a mirror from the callbacks available on the [GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), you can also read more about Flutter's gestures [here](https://api.flutter.dev/flutter/gestures/gestures-library.html).
Here are some example of more complex Gesture Recognizers:
## Example
```dart
MyGame() {
// other init...
class MyGame extends Game with TapDetector {
// Other methods ommited
Flame.util.addGestureRecognizer(createDragRecognizer());
Flame.util.addGestureRecognizer(createTapRecognizer());
@override
void onTapDown(TapDownDetails details) {
print("Player tap down on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
}
GestureRecognizer createDragRecognizer() {
return new ImmediateMultiDragGestureRecognizer()
..onStart = (Offset position) => this.handleDrag(position);
@override
void onTapUp(TapUpDetails details) {
print("Player tap up on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
}
TapGestureRecognizer createTapRecognizer() {
return new TapGestureRecognizer()
..onTapUp = (TapUpDetails details) => this.handleTap(details.globalPosition);;
}
```
__ATTENTION:__ `Flame.util.addGestureRecognizer` must be called after the `runApp`, otherwise Flutter's `GestureBinding` will not be initialized yet and exceptions will occur.
You can also check a more complete example [here](/doc/examples/gestures).
## Tapable components

View File

@@ -55,11 +55,14 @@ Flame provides a simple utility class to help you handle countdowns and event li
__Countdown example:__
```dart
import 'package:flame/game.dart';
import 'package:flame/time.dart';
import 'package:flame/text_config.dart';
import 'dart:ui';
class MyGame extends MyGame {
import 'package:flame/game.dart';
import 'package:flame/position.dart';
import 'package:flame/text_config.dart';
import 'package:flame/time.dart';
class MyGame extends Game {
final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF));
final countdown = Timer(2);
@@ -77,7 +80,8 @@ class MyGame extends MyGame {
@override
void render(Canvas canvas) {
textConfig.render(canvas, "Countdown: ${countdown.current.toString()}", Position(10, 100));
textConfig.render(canvas, "Countdown: ${countdown.current.toString()}",
Position(10, 100));
}
}
@@ -86,11 +90,14 @@ class MyGame extends MyGame {
__Interval example:__
```dart
import 'package:flame/game.dart';
import 'package:flame/time.dart';
import 'package:flame/text_config.dart';
import 'dart:ui';
class MyGame extends MyGame {
import 'package:flame/game.dart';
import 'package:flame/position.dart';
import 'package:flame/text_config.dart';
import 'package:flame/time.dart';
class MyGame extends Game {
final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF));
Timer interval;
@@ -105,6 +112,7 @@ class MyGame extends MyGame {
@override
void update(double dt) {
interval.update(dt);
}
@override

View File

@@ -12,31 +12,14 @@ import 'package:ordered_set/ordered_set.dart';
import 'components/component.dart';
import 'components/mixins/has_game_ref.dart';
import 'components/mixins/tapable.dart';
import 'flame.dart';
import 'position.dart';
import 'gestures.dart';
/// Represents a generic game.
///
/// Subclass this to implement the [update] and [render] methods.
/// Flame will deal with calling these methods properly when the game's widget is rendered.
abstract class Game {
TapGestureRecognizer _createTapGestureRecognizer() => TapGestureRecognizer()
..onTapUp = (TapUpDetails details) {
onTapUp(details);
}
..onTapDown = (TapDownDetails details) {
onTapDown(details);
}
..onTapCancel = () {
onTapCancel();
};
void onTapCancel() {}
void onTapDown(TapDownDetails details) {}
void onTapUp(TapUpDetails details) {}
TapGestureRecognizer _gestureRecognizer;
// Widget Builder for this Game
final builder = WidgetBuilder();
@@ -67,26 +50,162 @@ abstract class Game {
Widget get widget => builder.build(this);
// Called when the Game widget is attached
void onAttach() {
if (_gestureRecognizer != null) {
Flame.util.removeGestureRecognizer(_gestureRecognizer);
}
_gestureRecognizer = _createTapGestureRecognizer();
Flame.util.addGestureRecognizer(_gestureRecognizer);
}
void onAttach() {}
// Called when the Game widget is detached
void onDetach() {
if (_gestureRecognizer != null) {
Flame.util.removeGestureRecognizer(_gestureRecognizer);
}
}
void onDetach() {}
}
class WidgetBuilder {
Offset offset = Offset.zero;
Widget build(Game game) => Directionality(
textDirection: TextDirection.ltr, child: EmbeddedGameWidget(game));
Widget build(Game game) {
return GestureDetector(
// Taps
onTap: game is TapDetector ? () => (game as TapDetector).onTap() : null,
onTapCancel: game is TapDetector
? () => (game as TapDetector).onTapCancel()
: null,
onTapDown: game is TapDetector
? (TapDownDetails d) => (game as TapDetector).onTapDown(d)
: null,
onTapUp: game is TapDetector
? (TapUpDetails d) => (game as TapDetector).onTapUp(d)
: null,
// Secondary taps
onSecondaryTapDown: game is SecondaryTapDetector
? (TapDownDetails d) =>
(game as SecondaryTapDetector).onSecondaryTapDown(d)
: null,
onSecondaryTapUp: game is SecondaryTapDetector
? (TapUpDetails d) =>
(game as SecondaryTapDetector).onSecondaryTapUp(d)
: null,
onSecondaryTapCancel: game is SecondaryTapDetector
? () => (game as SecondaryTapDetector).onSecondaryTapCancel()
: null,
// Double tap
onDoubleTap: game is DoubleTapDetector
? () => (game as DoubleTapDetector).onDoubleTap()
: null,
// Long presses
onLongPress: game is LongPressDetector
? () => (game as LongPressDetector).onLongPress()
: null,
onLongPressStart: game is LongPressDetector
? (LongPressStartDetails d) =>
(game as LongPressDetector).onLongPressStart(d)
: null,
onLongPressMoveUpdate: game is LongPressDetector
? (LongPressMoveUpdateDetails d) =>
(game as LongPressDetector).onLongPressMoveUpdate(d)
: null,
onLongPressUp: game is LongPressDetector
? () => (game as LongPressDetector).onLongPressUp()
: null,
onLongPressEnd: game is LongPressDetector
? (LongPressEndDetails d) =>
(game as LongPressDetector).onLongPressEnd(d)
: null,
// Vertical drag
onVerticalDragDown: game is VerticalDragDetector
? (DragDownDetails d) =>
(game as VerticalDragDetector).onVerticalDragDown(d)
: null,
onVerticalDragStart: game is VerticalDragDetector
? (DragStartDetails d) =>
(game as VerticalDragDetector).onVerticalDragStart(d)
: null,
onVerticalDragUpdate: game is VerticalDragDetector
? (DragUpdateDetails d) =>
(game as VerticalDragDetector).onVerticalDragUpdate(d)
: null,
onVerticalDragEnd: game is VerticalDragDetector
? (DragEndDetails d) =>
(game as VerticalDragDetector).onVerticalDragEnd(d)
: null,
onVerticalDragCancel: game is VerticalDragDetector
? () => (game as VerticalDragDetector).onVerticalDragCancel()
: null,
// Horizontal drag
onHorizontalDragDown: game is HorizontalDragDetector
? (DragDownDetails d) =>
(game as HorizontalDragDetector).onHorizontalDragDown(d)
: null,
onHorizontalDragStart: game is HorizontalDragDetector
? (DragStartDetails d) =>
(game as HorizontalDragDetector).onHorizontalDragStart(d)
: null,
onHorizontalDragUpdate: game is HorizontalDragDetector
? (DragUpdateDetails d) =>
(game as HorizontalDragDetector).onHorizontalDragUpdate(d)
: null,
onHorizontalDragEnd: game is HorizontalDragDetector
? (DragEndDetails d) =>
(game as HorizontalDragDetector).onHorizontalDragEnd(d)
: null,
onHorizontalDragCancel: game is HorizontalDragDetector
? () => (game as HorizontalDragDetector).onHorizontalDragCancel()
: null,
// Force presses
onForcePressStart: game is ForcePressDetector
? (ForcePressDetails d) =>
(game as ForcePressDetector).onForcePressStart(d)
: null,
onForcePressPeak: game is ForcePressDetector
? (ForcePressDetails d) =>
(game as ForcePressDetector).onForcePressPeak(d)
: null,
onForcePressUpdate: game is ForcePressDetector
? (ForcePressDetails d) =>
(game as ForcePressDetector).onForcePressUpdate(d)
: null,
onForcePressEnd: game is ForcePressDetector
? (ForcePressDetails d) =>
(game as ForcePressDetector).onForcePressEnd(d)
: null,
// Pan
onPanDown: game is PanDetector
? (DragDownDetails d) => (game as PanDetector).onPanDown(d)
: null,
onPanStart: game is PanDetector
? (DragStartDetails d) => (game as PanDetector).onPanStart(d)
: null,
onPanUpdate: game is PanDetector
? (DragUpdateDetails d) => (game as PanDetector).onPanUpdate(d)
: null,
onPanEnd: game is PanDetector
? (DragEndDetails d) => (game as PanDetector).onPanEnd(d)
: null,
onPanCancel: game is PanDetector
? () => (game as PanDetector).onPanCancel()
: null,
// Scales
onScaleStart: game is ScaleDetector
? (ScaleStartDetails d) => (game as ScaleDetector).onScaleStart(d)
: null,
onScaleUpdate: game is ScaleDetector
? (ScaleUpdateDetails d) => (game as ScaleDetector).onScaleUpdate(d)
: null,
onScaleEnd: game is ScaleDetector
? (ScaleEndDetails d) => (game as ScaleDetector).onScaleEnd(d)
: null,
child: Container(
color: const Color(0xFF000000),
child: Directionality(
textDirection: TextDirection.ltr,
child: EmbeddedGameWidget(game))),
);
}
}
/// This is a more complete and opinionated implementation of Game.
@@ -94,7 +213,7 @@ class WidgetBuilder {
/// It still needs to be subclasses to add your game logic, but the [update], [render] and [resize] methods have default implementations.
/// This is the recommended structure to use for most games.
/// It is based on the Component system.
abstract class BaseGame extends Game {
abstract class BaseGame extends Game with TapDetector {
/// The list of components to be updated and rendered by the base game.
OrderedSet<Component> components =
OrderedSet(Comparing.on((c) => c.priority()));

63
lib/gestures.dart Normal file
View File

@@ -0,0 +1,63 @@
import 'package:flutter/gestures.dart';
mixin TapDetector {
void onTap() {}
void onTapCancel() {}
void onTapDown(TapDownDetails details) {}
void onTapUp(TapUpDetails details) {}
}
mixin SecondaryTapDetector {
void onSecondaryTapDown(TapDownDetails details) {}
void onSecondaryTapUp(TapUpDetails details) {}
void onSecondaryTapCancel() {}
}
mixin DoubleTapDetector {
void onDoubleTap() {}
}
mixin LongPressDetector {
void onLongPress() {}
void onLongPressStart(LongPressStartDetails details) {}
void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {}
void onLongPressUp() {}
void onLongPressEnd(LongPressEndDetails details) {}
}
mixin VerticalDragDetector {
void onVerticalDragDown(DragDownDetails details) {}
void onVerticalDragStart(DragStartDetails details) {}
void onVerticalDragUpdate(DragUpdateDetails details) {}
void onVerticalDragEnd(DragEndDetails details) {}
void onVerticalDragCancel() {}
}
mixin HorizontalDragDetector {
void onHorizontalDragDown(DragDownDetails details) {}
void onHorizontalDragStart(DragStartDetails details) {}
void onHorizontalDragUpdate(DragUpdateDetails details) {}
void onHorizontalDragEnd(DragEndDetails details) {}
void onHorizontalDragCancel() {}
}
mixin ForcePressDetector {
void onForcePressStart(ForcePressDetails details) {}
void onForcePressPeak(ForcePressDetails details) {}
void onForcePressUpdate(ForcePressDetails details) {}
void onForcePressEnd(ForcePressDetails details) {}
}
mixin PanDetector {
void onPanDown(DragDownDetails details) {}
void onPanStart(DragStartDetails details) {}
void onPanUpdate(DragUpdateDetails details) {}
void onPanEnd(DragEndDetails details) {}
void onPanCancel() {}
}
mixin ScaleDetector {
void onScaleStart(ScaleStartDetails details) {}
void onScaleUpdate(ScaleUpdateDetails details) {}
void onScaleEnd(ScaleEndDetails details) {}
}

View File

@@ -113,6 +113,10 @@ class Util {
/// This properly binds a gesture recognizer to your game.
///
/// Use this in order to get it to work in case your app also contains other widgets.
///
/// Read more at: https://flame-engine.org/docs/input.md
///
/// @Deprecated('This method can lead to confuse behaviour, use the gestures methods provided by the Game class')
void addGestureRecognizer(GestureRecognizer recognizer) {
if (GestureBinding.instance == null) {
throw Exception(

View File

@@ -1,6 +1,6 @@
name: flame
description: A minimalist Flutter game engine, provides a nice set of somewhat independent modules you can choose from.
version: 0.16.1
version: 0.17.0
author: Luan Nico <luannico27@gmail.com>
homepage: https://github.com/flame-engine/flame