Merge branch 'develop' into v1.0.0

This commit is contained in:
Erick Zanardo
2020-08-11 22:07:54 -03:00
35 changed files with 135 additions and 7177 deletions

View File

@ -2,11 +2,15 @@
## 1.0.0
- Move all box2d related code and examples to the flame_box2d repo
## [next]
- Preventing some crashed that could happen on web when some methods were called
- Rename Animation to SpriteAnimation
## 0.25.0
- Externalizing Tiled support to its own package `flame_tiled`
- Preventing some crashs that could happen on web when some methods were called
- Add mustCallSuper to BaseGame `update` and `render` methods
- Moved FPS code from BaseGame to a mixin, BaseGame uses the new mixin.
- Deprecate flare API in favor of the package `flame_flare`
## 0.24.0
- Outsourcing SVG support to an external package
- Adding MemoryCache class

View File

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

View File

@ -78,20 +78,56 @@ This component uses an instance of `Svg` class to represent a Component that has
android.y = 100;
```
## FlareAnimation Component
## FlareActor Component
This component wraps an instance of the [FlareAnimation](/doc/images.md#FlareAnimation), it receives the filename of the Flare animation file, which animation from that file you want to use, and the `width` and `height` of the rendered animation.
*Note*: The previous implementation of a Flare integration API using `FlareAnimation` and `FlareComponent` has been deprecated.
To use Flare within Flame, use the [`flame_flare`](https://github.com/flame-engine/flame_flare) package.
This is the interface to use a [flare animation](https://pub.dev/packages/flare_flutter) within flame.
`FlareActorComponent` has almost the same API as of flare's FlareActor widget. It receives the animation filename (that are loaded by default with `Flame.bundle`),
it also can receive a FlareController that can play multiple animations and control nodes.
```dart
final fileName = "assets/Bob_Minion.flr";
final animation = "Wave";
final width = 306;
final height = 228;
import 'package:flame_flare/flame_flare.dart';
// your implementation of FlareController
class WashingtonController extends FlareControls {
ActorNode rightHandNode;
void initialize(FlutterActorArtboard artboard) {
super.initialize(artboard);
// get flare node
rightHand = artboard.getNode('right_hand');
}
}
final fileName = 'assets/george_washington.flr';
final width = 1776;
final height = 1804;
final controller = WashingtonController(); //instantiate controller
FlareActorComponent flareAnimation = FlareActorComponent(
fileName,
controller: controller,
width: 306,
height: 228,
);
FlareComponent flareAnimation = FlareComponent(fileName, animation, width, height);
flareAnimation.x = 50;
flareAnimation.y = 240;
add(flareAnimation);
// to play an animation
controller.play('rise_up');
// you can add another animation to play at the same time
controller.play('close_door_way_out');
// also, get a flare node and modify it
controller.rightHandNode.rotation = math.pi;
```
You can also change the current playing animation using the `updateAnimation` method.
@ -139,9 +175,9 @@ Create it like this:
```dart
final images = [
ParallaxImage("mountains.jpg"),
ParallaxImage("forest.jpg"),
ParallaxImage("city.jpg"),
ParallaxImage('mountains.jpg'),
ParallaxImage('forest.jpg'),
ParallaxImage('city.jpg'),
];
this.bg = ParallaxComponent(images);
```
@ -163,9 +199,9 @@ By default the images are aligned to the bottom left, repeated along the X-axis
Advanced example:
```dart
final images = [
ParallaxImage("stars.jpg", repeat: ImageRepeat.repeat, alignment: Alignment.center, fill: LayerFill.width),
ParallaxImage("planets.jpg", repeat: ImageRepeat.repeatY, alignment: Alignment.bottomLeft, fill: LayerFill.none),
ParallaxImage("dust.jpg", repeat: ImageRepeat.repeatX, alignment: Alignment.topRight, fill: LayerFill.height),
ParallaxImage('stars.jpg', repeat: ImageRepeat.repeat, alignment: Alignment.center, fill: LayerFill.width),
ParallaxImage('planets.jpg', repeat: ImageRepeat.repeatY, alignment: Alignment.bottomLeft, fill: LayerFill.none),
ParallaxImage('dust.jpg', repeat: ImageRepeat.repeatX, alignment: Alignment.topRight, fill: LayerFill.height),
];
this.bg = ParallaxComponent(images, baseSpeed: Offset(50, 0), layerDelta: Offset(20, 0));
```

View File

@ -1,7 +1,20 @@
## Debug features
# Debug features
## FPS counter
Flame provides the `FPSCounter` mixin for recording the fps, this mixin can be applied on any class that extends from `Game`. When applied you have to implemented the `recordFps` method and it should return `true` if you want to access the current fps by using the `fps` method`.
```dart
class MyGame extends Game with FPS {
@override
bool recordFps() => true;
}
```
## BaseGame features
Flame provides some features for debugging, these features are enabled when the method `debugMode` from the `BaseGame` class is overridden, and returning `true`. When it's enabled all `PositionComponent`s will be wrapped into a rectangle, and have its position rendered on the screen, so you can visually verify the component boundaries and position.
In addition to the debugMode, you can also ask BaseGame to record the fps, that is enabled by overriding the `recordFps` method to return `true`, by doing so, you can access the current fps by using the method `fps`.
In addition to the debugMode, you can also ask `BaseGame` to record the fps(the `BaseGame` used the [FPSCounter](fps-counter) mixin). To enable it you have to override the `recordFps` method to return `true`, by doing so, you can access the current fps by using the method `fps`.
To see a working example of the debugging features, [check this example](/doc/examples/debug).
To see a working example of the debugging features of the `BaseGame`, [check this example](/doc/examples/debug).

View File

@ -1,72 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.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
.flutter-plugins-dependencies

View File

@ -1,10 +0,0 @@
# 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: 0ba67226ee62d6c9d663a6f8410fb4b2f1076046
channel: dev
project_type: app

View File

@ -1,7 +0,0 @@
# flare
Example game application showcasing how to use Flare animations on Flame.
Flare animation used for the example: https://www.2dimensions.com/a/kautuk/files/flare/bob-minion/preview
The `main.dart` is a more generic example, test most of the features as `main_component.dart` is a specific example for the `FlareComponent` class.

File diff suppressed because it is too large Load Diff

View File

@ -1,103 +0,0 @@
import 'dart:ui';
import 'package:flame/gestures.dart';
import 'package:flame/game.dart';
import 'package:flame/flare_animation.dart';
import 'package:flame/components/flare_component.dart';
import 'package:flame/text_config.dart';
import 'package:flame/position.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
final game = MyGame();
runApp(game.widget);
}
class MyGame extends BaseGame with TapDetector {
final TextConfig fpsTextConfig = TextConfig(color: const Color(0xFFFFFFFF));
final paint = Paint()..color = const Color(0xFFE5E5E5E5);
final List<String> _animations = ['Stand', 'Wave', 'Jump', 'Dance'];
int _currentAnimation = 0;
FlareAnimation flareAnimation;
bool loaded = false;
MyGame() {
_start();
}
@override
bool debugMode() => true;
@override
void onTap() {
cycleAnimation();
}
void cycleAnimation() {
if (_currentAnimation == 3) {
_currentAnimation = 0;
} else {
_currentAnimation++;
}
flareAnimation.updateAnimation(_animations[_currentAnimation]);
}
void _start() async {
flareAnimation = await FlareAnimation.load('assets/Bob_Minion.flr');
flareAnimation.updateAnimation('Stand');
flareAnimation.width = 306;
flareAnimation.height = 228;
final flareAnimation2 =
FlareComponent('assets/Bob_Minion.flr', 'Wave', 306, 228);
flareAnimation2.x = 50;
flareAnimation2.y = 240;
add(flareAnimation2);
final flareAnimation3 =
FlareComponent('assets/Bob_Minion.flr', 'Jump', 306, 228);
flareAnimation3.x = 50;
flareAnimation3.y = 400;
add(flareAnimation3);
final flareAnimation4 =
FlareComponent('assets/Bob_Minion.flr', 'Dance', 306, 228);
flareAnimation4.x = 50;
flareAnimation4.y = 550;
add(flareAnimation4);
loaded = true;
}
@override
void render(Canvas canvas) {
super.render(canvas);
if (loaded) {
canvas.drawRect(
Rect.fromLTWH(50, 50, flareAnimation.width, flareAnimation.height),
paint,
);
flareAnimation.render(canvas, x: 50, y: 50);
}
if (debugMode()) {
fpsTextConfig.render(canvas, fps(120).toString(), Position(0, 10));
}
}
@override
void update(double dt) {
super.update(dt);
if (loaded) {
flareAnimation.update(dt);
}
}
}

View File

@ -1,73 +0,0 @@
import 'dart:ui';
import 'package:flame/gestures.dart';
import 'package:flame/game.dart';
import 'package:flame/components/flare_component.dart';
import 'package:flame/text_config.dart';
import 'package:flame/position.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
final game = MyGame();
runApp(game.widget);
}
class MyGame extends BaseGame with TapDetector, DoubleTapDetector {
final TextConfig fpsTextConfig = TextConfig(color: BasicPalette.white.color);
final paint = Paint()..color = const Color(0xFFE5E5E5E5);
final List<String> _animations = ['Stand', 'Wave', 'Jump', 'Dance'];
int _currentAnimation = 0;
FlareComponent flareComponent;
bool loaded = false;
MyGame() {
_start();
}
@override
bool debugMode() => true;
@override
void onTap() {
cycleAnimation();
}
@override
void onDoubleTap() {
flareComponent.width += 10;
flareComponent.height += 10;
flareComponent.x -= 10;
flareComponent.y -= 10;
}
void cycleAnimation() {
if (_currentAnimation == 3) {
_currentAnimation = 0;
} else {
_currentAnimation++;
}
flareComponent.updateAnimation(_animations[_currentAnimation]);
}
void _start() async {
flareComponent = FlareComponent('assets/Bob_Minion.flr', 'Stand', 306, 228);
flareComponent.x = 50;
flareComponent.y = 240;
add(flareComponent);
}
@override
void render(Canvas canvas) {
super.render(canvas);
if (debugMode()) {
fpsTextConfig.render(canvas, fps(120).toString(), Position(0, 10));
}
}
}

View File

@ -1,22 +0,0 @@
name: flare
description: Flame sample for using Flare animations
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flame:
path: ../../../
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
assets:
- assets/Bob_Minion.flr

View File

@ -1,799 +0,0 @@
{
"version": 24,
"artboards": [
{
"name": "Artboard",
"translation": [
0,
0
],
"width": 250,
"height": 250,
"origin": [
0,
0
],
"clipContents": true,
"color": [
0.364705890417099,
0.364705890417099,
0.364705890417099,
1
],
"nodes": [
{
"name": "Polygon",
"translation": [
125,
125
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"isVisible": true,
"blendMode": 3,
"drawOrder": 1,
"transformAffectsStroke": false,
"type": "shape"
},
{
"name": "Color",
"parent": 0,
"opacity": 1,
"color": [
0.4534761905670166,
0.8476190567016602,
0.7884976267814636,
1
],
"fillRule": 1,
"type": "colorFill"
},
{
"name": "Polygon Path",
"parent": 0,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"width": 250,
"height": 250,
"sides": 4,
"type": "polygon"
},
{
"name": "globe",
"translation": [
125,
125
],
"rotation": 0,
"scale": [
10,
10
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"type": "node"
},
{
"name": "Shape",
"parent": 3,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"isVisible": true,
"blendMode": 3,
"drawOrder": 2,
"transformAffectsStroke": false,
"type": "shape"
},
{
"name": "Color",
"parent": 4,
"opacity": 1,
"color": [
0,
0,
0,
1
],
"width": 2,
"cap": 0,
"join": 0,
"trim": 0,
"type": "colorStroke"
},
{
"name": "Ellipse",
"parent": 4,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"width": 20,
"height": 20,
"type": "ellipse"
},
{
"name": "Shape",
"parent": 3,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"isVisible": true,
"blendMode": 3,
"drawOrder": 3,
"transformAffectsStroke": false,
"type": "shape"
},
{
"name": "Color",
"parent": 7,
"opacity": 1,
"color": [
0,
0,
0,
1
],
"width": 2,
"cap": 0,
"join": 0,
"trim": 0,
"type": "colorStroke"
},
{
"name": "Path",
"parent": 7,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"bones": [],
"isVisible": true,
"isClosed": true,
"points": [
{
"pointType": 2,
"translation": [
0,
-10
],
"in": [
0,
-10
],
"out": [
2.5012803077697754,
-7.2616472244262695
]
},
{
"pointType": 2,
"translation": [
4,
1.7763568394002505e-15
],
"in": [
3.922752857208252,
-3.7079660892486572
],
"out": [
3.922752857208252,
3.7079660892486572
]
},
{
"pointType": 2,
"translation": [
-2.6645352591003757e-14,
10
],
"in": [
2.5012803077697754,
7.2616472244262695
],
"out": [
-2.5012803077697754,
7.2616472244262695
]
},
{
"pointType": 2,
"translation": [
-4,
1.7763568394002505e-15
],
"in": [
-3.922752857208252,
3.7079660892486572
],
"out": [
-3.922752857208252,
-3.7079660892486572
]
},
{
"pointType": 2,
"translation": [
3.019806626980426e-14,
-10
],
"in": [
-2.5012803077697754,
-7.2616472244262695
],
"out": [
3.019806626980426e-14,
-10
]
}
],
"type": "path"
},
{
"name": "Ellipse",
"translation": [
125,
125
],
"rotation": 0,
"scale": [
10,
10
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"isVisible": true,
"blendMode": 3,
"drawOrder": 4,
"transformAffectsStroke": false,
"type": "shape"
},
{
"name": "Color",
"parent": 10,
"opacity": 1,
"color": [
1,
1,
1,
1
],
"width": 10,
"cap": 1,
"join": 0,
"trim": 1,
"start": 0,
"end": 0,
"offset": 0,
"type": "colorStroke"
},
{
"name": "Ellipse Path",
"parent": 10,
"translation": [
0,
0
],
"rotation": 0,
"scale": [
1,
1
],
"opacity": 1,
"isCollapsed": false,
"clips": [],
"width": 14,
"height": 14,
"type": "ellipse"
}
],
"animations": [
{
"name": "Spin",
"fps": 60,
"duration": 10,
"isLooping": true,
"keyed": [
{
"component": 0,
"scaleY": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
},
{
"time": 2.5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.49699999999999966
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
},
{
"time": 7.766666666666667,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.49699999999999966
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
}
]
],
"scaleX": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
},
{
"time": 2.5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.5049999999999999
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
},
{
"time": 7.766666666666667,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.5049999999999999
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
}
]
],
"posX": [
[
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 125
}
]
]
},
{
"component": 1,
"fillColor": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0.4534761905670166,
0.6939033269882202,
0.8476190567016602,
1
]
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0.4773571491241455,
0.776190459728241,
0.6058554649353027,
1
]
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0.4534761905670166,
0.6939033269882202,
0.8476190567016602,
1
]
}
]
]
},
{
"component": 3,
"rotation": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 6.283185307179586
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0
}
]
],
"posX": [
[
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 125
}
]
]
},
{
"component": 9,
"pathVertices": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0,
-10,
0,
-10,
2.5012803077697754,
-7.2616472244262695,
4,
1.7763568394002505e-15,
3.922752857208252,
-3.7079660892486572,
3.922752857208252,
3.7079660892486572,
-2.6645352591003757e-14,
10,
2.5012803077697754,
7.2616472244262695,
-2.5012803077697754,
7.2616472244262695,
-4,
1.7763568394002505e-15,
-3.922752857208252,
3.7079660892486572,
-3.922752857208252,
-3.7079660892486572,
3.019806626980426e-14,
-10,
-2.5012803077697754,
-7.2616472244262695,
3.019806626980426e-14,
-10
]
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0,
-10,
0,
-10,
-0.028131680563092232,
-0.08517590165138245,
9.294120788574219,
-0.05882320925593376,
9.216875076293945,
-3.766789197921753,
9.216875076293945,
3.6491427421569824,
-2.6645352591003757e-14,
10,
-0.02813015505671501,
-0.03246975317597389,
-0.03069210611283779,
-0.09129314869642258,
-8.999999046325684,
1.862645149230957e-7,
-8.92275333404541,
3.70796537399292,
-8.92275333404541,
-3.707965612411499,
3.019806626980426e-14,
-10,
-0.08951549977064133,
-0.14400005340576172,
3.019806626980426e-14,
-10
]
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": [
0,
-10,
0,
-10,
2.5012803077697754,
-7.2616472244262695,
4,
1.7763568394002505e-15,
3.922752857208252,
-3.7079660892486572,
3.922752857208252,
3.7079660892486572,
-2.6645352591003757e-14,
10,
2.5012803077697754,
7.2616472244262695,
-2.5012803077697754,
7.2616472244262695,
-4,
1.7763568394002505e-15,
-3.922752857208252,
3.7079660892486572,
-3.922752857208252,
-3.7079660892486572,
3.019806626980426e-14,
-10,
-2.5012803077697754,
-7.2616472244262695,
3.019806626980426e-14,
-10
]
}
]
]
},
{
"component": 10
},
{
"component": 11,
"strokeStart": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0
},
{
"time": 2.5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.25
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.5
},
{
"time": 7.533333333333333,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.6
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
}
]
],
"strokeEnd": [
[
{
"time": 0,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0
},
{
"time": 2.5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.5
},
{
"time": 5,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.8
},
{
"time": 7.533333333333333,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 0.8
},
{
"time": 10,
"interpolatorType": 2,
"cubicX1": 0.5913978494623656,
"cubicY1": 0.0766129032258065,
"cubicX2": 0.17204301075268819,
"cubicY2": 1.1048387096774193,
"value": 1
}
]
]
}
],
"animationStart": 0,
"animationEnd": 10,
"type": "animation"
}
],
"type": "artboard"
}
]
}

View File

@ -4,7 +4,6 @@ import 'dart:ui';
import 'package:flame/sprite_animation.dart';
import 'package:flame/components/component.dart';
import 'package:flame/flare_animation.dart';
import 'package:flame/particles/circle_particle.dart';
import 'package:flame/particles/composed_particle.dart';
import 'package:flame/particles/curved_particle.dart';
@ -18,7 +17,6 @@ import 'package:flame/particles/accelerated_particle.dart';
import 'package:flame/particles/paint_particle.dart';
import 'package:flame/particles/animation_particle.dart';
import 'package:flame/particles/component_particle.dart';
import 'package:flame/particles/flare_particle.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flame/time.dart' as flame_time;
@ -52,11 +50,9 @@ class MyGame extends BaseGame {
Offset cellSize;
Offset halfCellSize;
FlareAnimation flareAnimation;
MyGame({
Size screenSize,
this.flareAnimation,
}) {
size = screenSize;
cellSize = Offset(size.width / gridSize, size.height / gridSize);
@ -92,7 +88,6 @@ class MyGame extends BaseGame {
animationParticle(),
fireworkParticle(),
componentParticle(),
flareParticle(),
];
// Place all the [Particle] instances
@ -461,36 +456,6 @@ class MyGame extends BaseGame {
);
}
/// [FlareParticle] renders fiven [FlareAnimation] inside
/// as you can see, animation could be reused across
/// different particles.
Particle flareParticle() {
final flare = ComposedParticle(children: <Particle>[
// Circle Particle for background
CircleParticle(
paint: Paint()..color = Colors.white12,
radius: flareAnimation.width / 2),
FlareParticle(flare: flareAnimation),
]);
final List<Offset> corners = [
-halfCellSize,
halfCellSize,
];
return RotatingParticle(
to: pi,
child: Particle.generate(
count: 2,
generator: (i) => MovingParticle(
to: corners[i] * .4,
curve: SineCurve(),
child: flare,
),
),
);
}
/// [Particle] base class exposes a number
/// of convenience wrappers to make positioning.
///
@ -523,6 +488,7 @@ class MyGame extends BaseGame {
@override
bool debugMode() => true;
@override
void render(Canvas canvas) {
super.render(canvas);
@ -587,13 +553,8 @@ Future<BaseGame> loadGame() async {
'boom3.png',
]),
]);
const flareSize = 32.0;
final flareAnimation = await FlareAnimation.load('assets/diamond.flr');
flareAnimation.updateAnimation('Spin');
flareAnimation.width = flareSize;
flareAnimation.height = flareSize;
return MyGame(screenSize: gameSize, flareAnimation: flareAnimation);
return MyGame(screenSize: gameSize);
}
/// A curve which maps sinus output (-1..1,0..pi)

View File

@ -19,4 +19,3 @@ dev_dependencies:
flutter:
assets:
- assets/images/
- assets/diamond.flr

View File

@ -1,71 +0,0 @@
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.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

@ -1,10 +0,0 @@
# 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: 7a88fbc5fd987dce78e468ec45e9e841a49f422d
channel: dev
project_type: app

View File

@ -1,3 +0,0 @@
# tiled
An example usage of the Tiled API on Flame.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,45 +0,0 @@
import 'package:flame/sprite_animation.dart';
import 'package:flame/components/sprite_animation_component.dart';
import 'package:flame/components/tiled_component.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart' hide Animation;
import 'package:tiled/tiled.dart' show ObjectGroup, TmxObject;
void main() {
WidgetsFlutterBinding.ensureInitialized();
Flame.images.load('coins.png');
final TiledGame game = TiledGame();
runApp(game.widget);
}
class TiledGame extends BaseGame {
TiledGame() {
final TiledComponent tiledMap = TiledComponent('map.tmx', 16.0);
add(tiledMap);
_addCoinsInMap(tiledMap);
}
void _addCoinsInMap(TiledComponent tiledMap) async {
final ObjectGroup objGroup =
await tiledMap.getObjectGroupFromLayer('AnimatedCoins');
if (objGroup == null) {
return;
}
objGroup.tmxObjects.forEach((TmxObject obj) {
final comp = SpriteAnimationComponent(
20.0,
20.0,
SpriteAnimation.sequenced(
'coins.png',
8,
textureWidth: 20,
textureHeight: 20,
),
);
comp.x = obj.x.toDouble();
comp.y = obj.y.toDouble();
add(comp);
});
}
}

View File

@ -1,25 +0,0 @@
name: tiled_example
description: A simple tiled example
version: 1.0.0+1
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flame:
path: ../../../
tiled: 0.6.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
assets:
- assets/tiles/map.tmx
- assets/images/map-level1.png
- assets/images/map-level2.png
- assets/images/coins.png

View File

@ -305,13 +305,17 @@ class RectComponent extends Component {
```
## Flare Particle
A container for `FlareAnimation`, it propagates `update` and `render` hooks to its child.
To use Flare within Flame, use the [`flame_flare`](https://github.com/flame-engine/flame_flare) package.
It will provide a class `FlareParticle` that is a container for `FlareActorAnimation`, it propagates `update` and `render` hooks to its child.
```dart
import 'package:flame_flare/flame_flare.dart';
// During game initialisation
const flareSize = 32.0;
final flareAnimation = await FlareAnimation.load('assets/sparkle.flr');
flareAnimation.updateAnimation('Shine');
final flareAnimation = FlareActorAnimation('assets/sparkle.flr');
flareAnimation.width = flareSize;
flareAnimation.height = flareSize;

View File

@ -2,10 +2,10 @@
[Tiled](https://www.mapeditor.org/) is an awesome tool to design levels and maps.
Flame bundles a [dart package](https://pub.dev/packages/tiled) that allows you to parse tmx (xml) files and access the tiles, objects and everything in there.
Flame provides a package ([flame_tiled](https://github.com/flame-engine/flame_tiled)) which bundles a [dart package](https://pub.dev/packages/tiled) that allows you to parse tmx (xml) files and access the tiles, objects and everything in there.
Flame also provides a simple TiledComponent for the map rendering, which renders the tiles on the screen and supports rotations and flips.
Flame also provides a simple Tiled class and its component wrapper TiledComponent, for the map rendering, which renders the tiles on the screen and supports rotations and flips.
Other advanced features are not yet supported but you can easily read the objects and other features of the tmx and add custom behaviour (eg regions for triggers and walking areas, custom animated objects).
You can check a working example [here](/doc/examples/tiled).
You can check a working example [here](https://github.com/flame-engine/flame_tiled/tree/master/example).

View File

@ -3,6 +3,7 @@ import 'dart:ui';
import '../flare_animation.dart';
import 'position_component.dart';
@Deprecated("Use flame_flare package instead")
class FlareComponent extends PositionComponent {
FlareAnimation _flareAnimation;

View File

@ -1,176 +0,0 @@
import 'dart:math' as math;
import 'dart:async';
import 'dart:ui';
import 'package:flame/components/component.dart';
import 'package:flame/flame.dart';
import 'package:flutter/material.dart' show Colors;
import 'package:tiled/tiled.dart' hide Image;
/// Tiled represents all flips and rotation using three possible flips: horizontal, vertical and diagonal.
/// This class converts that representation to a simpler one, that uses one angle (with pi/2 steps) and two flips (H or V).
/// More reference: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tile-flipping
class _SimpleFlips {
/// The angle (in steps of pi/2 rads), clockwise, around the center of the tile.
final int angle;
/// Whether to flip across a central vertical axis (passing through the center).
final bool flipH;
/// Whether to flip across a central horizontal axis (passing through the center).
final bool flipV;
_SimpleFlips(this.angle, this.flipH, this.flipV);
/// This is the conversion from the truth table that I drew.
factory _SimpleFlips.fromFlips(Flips flips) {
int angle;
bool flipV, flipH;
if (!flips.diagonally && !flips.vertically && !flips.horizontally) {
angle = 0;
flipV = false;
flipH = false;
} else if (!flips.diagonally && !flips.vertically && flips.horizontally) {
angle = 0;
flipV = false;
flipH = true;
} else if (!flips.diagonally && flips.vertically && !flips.horizontally) {
angle = 0;
flipV = true;
flipH = false;
} else if (!flips.diagonally && flips.vertically && flips.horizontally) {
angle = 2;
flipV = false;
flipH = false;
} else if (flips.diagonally && !flips.vertically && !flips.horizontally) {
angle = 1;
flipV = false;
flipH = true;
} else if (flips.diagonally && !flips.vertically && flips.horizontally) {
angle = 1;
flipV = false;
flipH = false;
} else if (flips.diagonally && flips.vertically && !flips.horizontally) {
angle = 3;
flipV = false;
flipH = false;
} else if (flips.diagonally && flips.vertically && flips.horizontally) {
angle = 1;
flipV = true;
flipH = false;
} else {
// this should be exhaustive
throw 'Invalid combination of booleans: $flips';
}
return _SimpleFlips(angle, flipH, flipV);
}
}
/// This component renders a tile map based on a TMX file from Tiled.
class TiledComponent extends Component {
String filename;
TileMap map;
Image image;
Map<String, Image> images = <String, Image>{};
Future future;
bool _loaded = false;
double destTileSize;
static Paint paint = Paint()..color = Colors.white;
/// Creates this TiledComponent with the filename (for the tmx file resource)
/// and destTileSize is the tile size to be rendered (not the tile size in the texture, that one is configured inside Tiled).
TiledComponent(this.filename, this.destTileSize) {
future = _load();
}
Future _load() async {
map = await _loadMap();
image = await Flame.images.load(map.tilesets[0].image.source);
images = await _loadImages(map);
_loaded = true;
}
Future<TileMap> _loadMap() {
return Flame.bundle.loadString('assets/tiles/$filename').then((contents) {
final parser = TileMapParser();
return parser.parse(contents);
});
}
Future<Map<String, Image>> _loadImages(TileMap map) async {
final Map<String, Image> result = {};
await Future.forEach(map.tilesets, (tileset) async {
await Future.forEach(tileset.images, (tmxImage) async {
result[tmxImage.source] = await Flame.images.load(tmxImage.source);
});
});
return result;
}
@override
bool loaded() => _loaded;
@override
void render(Canvas c) {
if (!loaded()) {
return;
}
map.layers.forEach((layer) {
if (layer.visible) {
_renderLayer(c, layer);
}
});
}
void _renderLayer(Canvas c, Layer layer) {
layer.tiles.forEach((tileRow) {
tileRow.forEach((tile) {
if (tile.gid == 0) {
return;
}
final image = images[tile.image.source];
final rect = tile.computeDrawRect();
final src = Rect.fromLTWH(
rect.left.toDouble(),
rect.top.toDouble(),
rect.width.toDouble(),
rect.height.toDouble(),
);
final dst = Rect.fromLTWH(
tile.x * destTileSize,
tile.y * destTileSize,
destTileSize,
destTileSize,
);
final flips = _SimpleFlips.fromFlips(tile.flips);
c.save();
c.translate(dst.center.dx, dst.center.dy);
c.rotate(flips.angle * math.pi / 2);
c.scale(flips.flipV ? -1.0 : 1.0, flips.flipH ? -1.0 : 1.0);
c.translate(-dst.center.dx, -dst.center.dy);
c.drawImageRect(image, src, dst, paint);
c.restore();
});
});
}
@override
void update(double t) {}
/// This returns an object group fetch by name from a given layer.
/// Use this to add custom behaviour to special objects and groups.
Future<ObjectGroup> getObjectGroupFromLayer(String name) {
return future.then((onValue) {
return map.objectGroups
.firstWhere((objectGroup) => objectGroup.name == name);
});
}
}

View File

@ -5,6 +5,7 @@ import "flame.dart";
import "package:flare_flutter/flare.dart";
import "package:flare_flutter/flare_actor.dart";
@Deprecated("Use flame_flare package instead")
class FlareAnimation {
final FlutterActorArtboard _artboard;

39
lib/fps_counter.dart Normal file
View File

@ -0,0 +1,39 @@
import 'dart:math' as math;
import 'game/game.dart';
mixin FPSCounter on Game {
/// List of deltas used in debug mode to calculate FPS
final List<double> _dts = [];
/// Returns whether this [Game] is should record fps or not
///
/// Returns `false` by default. Override to use the `fps` counter method.
/// In recording fps, the [recordDt] method actually records every `dt` for statistics.
/// Then, you can use the [fps] method to check the game FPS.
bool recordFps();
/// This is a hook that comes from the RenderBox to allow recording of render times and statistics.
@override
void recordDt(double dt) {
if (recordFps()) {
_dts.add(dt);
}
}
/// Returns the average FPS for the last [average] measures.
///
/// The values are only saved if in debug mode (override [recordFps] to use this).
/// Selects the last [average] dts, averages then, and returns the inverse value.
/// So it's technically updates per second, but the relation between updates and renders is 1:1.
/// Returns 0 if empty.
double fps([int average = 1]) {
final List<double> dts = _dts.sublist(math.max(0, _dts.length - average));
if (dts.isEmpty) {
return 0.0;
}
final double dtSum = dts.reduce((s, t) => s + t);
final double averageDt = dtSum / average;
return 1 / averageDt;
}
}

View File

@ -1,7 +1,8 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flame/components/composed_component.dart';
import 'package:flame/fps_counter.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart' hide WidgetBuilder;
import 'package:ordered_set/comparing.dart';
@ -20,7 +21,7 @@ import 'game.dart';
/// 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.
class BaseGame extends Game {
class BaseGame extends Game with FPSCounter {
/// The list of components to be updated and rendered by the base game.
OrderedSet<Component> components =
OrderedSet(Comparing.on((c) => c.priority()));
@ -37,9 +38,6 @@ class BaseGame extends Game {
/// Camera position; every non-HUD component is translated so that the camera position is the top-left corner of the screen.
Position camera = Position.empty();
/// List of deltas used in debug mode to calculate FPS
final List<double> _dts = [];
/// This method is called for every component added, both via [add] and [addLater] methods.
///
/// You can use this to setup your mixins, pre-calculate stuff on every component, or anything you desire.
@ -100,6 +98,7 @@ class BaseGame extends Game {
/// You can override it further to add more custom behaviour.
/// Beware of however you are rendering components if not using this; you must be careful to save and restore the canvas to avoid components messing up with each other.
@override
@mustCallSuper
void render(Canvas canvas) {
canvas.save();
components.forEach((comp) => renderComponent(canvas, comp));
@ -127,6 +126,7 @@ class BaseGame extends Game {
/// It also actually adds the components that were added by the [addLater] method, and remove those that are marked for destruction via the [Component.destroy] method.
/// You can override it further to add more custom behaviour.
@override
@mustCallSuper
void update(double t) {
_removeLater.forEach((c) => components.remove(c));
_removeLater.clear();
@ -155,36 +155,8 @@ class BaseGame extends Game {
/// You can use this value to enable debug behaviors for your game, many components show extra information on screen when on debug mode
bool debugMode() => false;
/// Returns whether this [Game] is should record fps or not
///
/// Returns `false` by default. Override to use the `fps` counter method.
/// In recording fps, the [recordDt] method actually records every `dt` for statistics.
/// Then, you can use the [fps] method to check the game FPS.
bool recordFps() => false;
/// This is a hook that comes from the RenderBox to allow recording of render times and statistics.
@override
void recordDt(double dt) {
if (recordFps()) {
_dts.add(dt);
}
}
/// Returns the average FPS for the last [average] measures.
///
/// The values are only saved if in debug mode (override [recordFps] to use this).
/// Selects the last [average] dts, averages then, and returns the inverse value.
/// So it's technically updates per second, but the relation between updates and renders is 1:1.
/// Returns 0 if empty.
double fps([int average = 1]) {
final List<double> dts = _dts.sublist(math.max(0, _dts.length - average));
if (dts.isEmpty) {
return 0.0;
}
final double dtSum = dts.reduce((s, t) => s + t);
final double averageDt = dtSum / average;
return 1 / averageDt;
}
bool recordFps() => false;
/// Returns the current time in seconds with microseconds precision.
///

View File

@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import '../flare_animation.dart';
import '../particle.dart';
@Deprecated("Use flame_flare package instead")
class FlareParticle extends Particle {
final FlareAnimation flare;

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.24.0
version: 0.25.0
homepage: https://github.com/flame-engine/flame
dependencies:
@ -11,7 +11,6 @@ dependencies:
path_provider: ^1.6.0
box2d_flame: ^0.4.6
synchronized: ^2.1.0
tiled: ^0.6.0
convert: ^2.0.1
flare_flutter: ^2.0.1
meta: ^1.1.8

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,28 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flame/components/tiled_component.dart';
import 'package:flame/flame.dart';
import 'package:flutter/services.dart' show CachingAssetBundle;
import 'package:test/test.dart';
void main() {
test('my first widget test', () async {
await Flame.init(bundle: TestAssetBundle());
final tiled = TiledComponent('x', 16);
await tiled.future;
expect(1, equals(1));
});
}
class TestAssetBundle extends CachingAssetBundle {
@override
Future<ByteData> load(String key) async => File('assets/map-level1.png')
.readAsBytes()
.then((bytes) => ByteData.view(Uint8List.fromList(bytes).buffer));
@override
Future<String> loadString(String key, {bool cache = true}) =>
File('assets/map.tmx').readAsString();
}