More followups from #667 : Unifying constructor logic + small improvements and docs (#681)

This commit is contained in:
Luan Nico
2021-02-23 17:38:19 -05:00
committed by GitHub
parent 9c38c9bd04
commit 062b73d5f5
14 changed files with 89 additions and 55 deletions

View File

@@ -17,7 +17,7 @@ The `shouldRemove` variable can be overridden or set to true and `BaseGame` will
The `isHUD` variable can be overridden or set to true (default false) to make the `BaseGame` ignore the `camera` for this element, make it static in relation to the screen that is.
The `onMount` method can be overridden to run initialization code for the component. When this method is called, BaseGame ensures that all the mixins which would change this component's behaviour are already resolved.
The `onMount` method can be overridden to run initialization code for the component. When this method is called, BaseGame ensures that all the mixins which would change this component's behavior are already resolved.
The `onRemove` method can be overridden to run code before the component is removed from the game, it is only run once even if the component is removed both by using the `BaseGame` remove method and the ´Component´ remove method.
@@ -70,7 +70,7 @@ When implementing the `render` method for your component that extends `PositionC
Your render method should not handle where on the screen your component should be rendered. To handle where and how your component should be rendered
use the `position`. `angle` and `anchor` properties and flame will automatically handle the rest for you.
If you really want to handle the canvas translations yourself you can just omit the `super.render(canvas)` line and surpress the warning, but for most usecases this is not recommended.
If you really want to handle the canvas translations yourself you can just omit the `super.render(canvas)` line and suppress the warning, but for most use cases this is not recommended.
If you want to know where on the screen the bounding box of the component is you can use the `toRect` method.
@@ -101,32 +101,46 @@ player.render(canvas); // it will render only if the image is loaded and the pos
This class is used to represent a Component that has a sprite that runs a single cyclic animation.
This will create a simple three frame animation
This will create a simple three frame animation using 3 different images:
```dart
List<Sprite> sprites = [0, 1, 2].map((i) => Sprite('player_${i}.png')).toList();
final sprites = [0, 1, 2]
.map((i) => Sprite(images.fromCache('player_$i.png')))
.toList();
final size = Vector2.all(64.0);
this.player = SpriteAnimationComponent(size, new Animation.spriteList(sprites, stepTime: 0.01));
this.player = SpriteAnimationComponent(
SpriteAnimation.spriteList(sprites, stepTime: 0.01),
size: size,
);
```
If you have a sprite sheet, you can use the `sequenced` constructor, identical to the one provided by the `Animation` class (check more details in [the appropriate section](images.md#Animation)):
If you have a sprite sheet, you can use the `sequenced` constructor from the `SpriteAnimationData` class (check more details on [Images &gt; Animation](images.md#Animation)):
```dart
final size = Vector2.all(64.0);
this.player = SpriteAnimationComponent.sequenced(size, 'player.png', 2);
final data = SpriteAnimationData.sequenced(
textureSize: size,
amount: 2,
stepTime: 0.1,
);
this.player = SpriteAnimationComponent.fromFrameData(
await images.load('player.png'),
data,
);
```
If you are not using `BaseGame`, don't forget this component needs to be update'd even if static, because the animation object needs to be ticked to move the frames.
If you are not using `BaseGame`, don't forget this component needs to be updated, because the animation object needs to be ticked to move the frames.
## SvgComponent
*Note*: To use SVG with Flame, use the [`flame_svg`](https://github.com/flame-engine/flame_svg) package.
This component uses an instance of `Svg` class to represent a Component that has a svg that is rendered on the game:
```dart
Svg svg = Svg('android.svg');
SvgComponent android = SvgComponent.fromSvg(100, 100, svg);
android.x = 100;
android.y = 100;
SvgComponent android = SvgComponent.fromSvg(Vector2.all(100.0), svg);
android.position = Vector2.all(100.0);
```
## FlareActorComponent
@@ -232,7 +246,7 @@ For example if you want to move your background images along the X-axis with a f
"closer" the image is:
```dart
final parallaxComponent = await loadParalladComponent(
final parallaxComponent = await loadParallaxComponent(
_paths,
baseVelocity: Vector2(20, 0),
velocityMultiplierDelta: Vector2(1.8, 1.0),
@@ -242,14 +256,14 @@ You can set the baseSpeed and layerDelta at any time, for example if your charac
game speeds up.
```dart
final parallax = parallaxComponen.parallax;
final parallax = parallaxComponent.parallax;
parallax.baseSpeed = Vector2(100, 0);
parallax.velocityMultiplierDelta = Vector2(2.0, 1.0);
```
By default the images are aligned to the bottom left, repeated along the X-axis and scaled
proportionally so that the image covers the height of the screen. If you want to change this
behaviour, for example if you are not making a side scrolling game, you can set the `repeat`,
behavior, for example if you are not making a side scrolling game, you can set the `repeat`,
`alignment` and `fill` parameters for each `ParallaxImage` and add them to `ParallaxLayer`s that you
then pass in to the `ParallaxComponent`'s constructor.
@@ -260,7 +274,7 @@ final images = [
loadParallaxImage('planets.jpg', repeat: ImageRepeat.repeatY, alignment: Alignment.bottomLeft, fill: LayerFill.none),
loadParallaxImage('dust.jpg', repeat: ImageRepeat.repeatX, alignment: Alignment.topRight, fill: LayerFill.height),
];
final layers = images.map((image) => ParallaxLayer(await image, velocityMulitplier: images.indexOf(image) * 2.0));
final layers = images.map((image) => ParallaxLayer(await image, velocityMultiplier: images.indexOf(image) * 2.0));
final parallaxComponent = ParallaxComponent.fromParallax(
Parallax(
await Future.wait(layers),
@@ -287,7 +301,7 @@ Three example implementations can be found in the
## SpriteBodyComponent
See [SpriteBodyComponent](forge2d.md#spritebodycomponent) in the box2d documentation.
See [SpriteBodyComponent](forge2d.md#SpriteBodyComponent) in the box2d documentation.
## TiledComponent

View File

@@ -29,8 +29,6 @@ class MyGame extends BaseGame {
size: Vector2.all(32),
sprite: Sprite(image),
),
).forEach((sprite) {
add(sprite);
});
).forEach(add);
}
}

View File

@@ -38,9 +38,10 @@ class Square extends PositionComponent {
c.drawRect(Rect.fromLTWH(width / 2, height / 2, 3, 3), blue);
}
@override
void update(double dt) {
super.update(dt);
angle += speed * t;
angle += speed * dt;
angle %= 2 * math.pi;
}

View File

@@ -40,7 +40,7 @@ class _Composed {
/// The [ImageComposition] allows for composing multiple images onto a single image.
///
/// **Note:** Composing images is a heavy async operation and should not be called on each [Game.render].
/// **Note:** Composing images is a heavy async operation and should not be called inside the game loop.
class ImageComposition {
/// The values that will be used to compose the image
final List<_Composed> _composes = [];

View File

@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';
import 'extensions/vector2.dart';
/// Represents a relative position inside some 2D object with a rectangular
@@ -14,6 +16,7 @@ import 'extensions/vector2.dart';
/// The Anchor is represented by a fraction of the size (in each axis),
/// where 0 in x-axis means left, 0 in y-axis means top, 1 in x-axis means right
/// and 1 in y-axis means bottom.
@immutable
class Anchor {
static const Anchor topLeft = Anchor(0.0, 0.0);
static const Anchor topCenter = Anchor(0.5, 0.0);
@@ -82,9 +85,9 @@ class Anchor {
if (_valueNames.containsValue(name)) {
return _valueNames.entries.singleWhere((e) => e.value == name).key;
} else {
final regexp = RegExp(r"^\Anchor\(([^,]+), ([^\)]+)\)");
final regexp = RegExp(r'^\Anchor\(([^,]+), ([^\)]+)\)');
final matches = regexp.allMatches(name).first.group;
assert(matches != null, "Bad Anchor format");
assert(matches != null, 'Bad Anchor format');
return Anchor(double.parse(matches(1)), double.parse(matches(2)));
}
}

View File

@@ -32,7 +32,14 @@ class IsometricTileMapComponent extends PositionComponent {
/// Optionally provide a new tile size to render it scaled.
Vector2 destTileSize;
IsometricTileMapComponent(this.tileset, this.matrix, {this.destTileSize});
IsometricTileMapComponent(
this.tileset,
this.matrix, {
this.destTileSize,
Vector2 position,
}) {
this.position = position ?? Vector2.zero();
}
/// This is the size the tiles will be drawn (either original or overwritten).
Vector2 get effectiveTileSize => destTileSize ?? tileset.srcSize;

View File

@@ -47,11 +47,18 @@ class ParallaxComponent extends PositionComponent {
_parallax.resize(size);
}
/// Creates a component with an empty parallax which can be set later
ParallaxComponent();
/// Creates a component with an empty parallax which can be set later.
ParallaxComponent({
Vector2 position,
Vector2 size,
}) : super(position: position, size: size);
/// Creates a component from a [Parallax] object
ParallaxComponent.fromParallax(this._parallax);
/// Creates a component from a [Parallax] object.
ParallaxComponent.fromParallax(
this._parallax, {
Vector2 position,
Vector2 size,
}) : super(position: position, size: size);
@mustCallSuper
@override

View File

@@ -32,10 +32,7 @@ class SpriteAnimationComponent extends PositionComponent {
Vector2 size,
this.removeOnFinish = false,
}) : super(position: position, size: size) {
animation = SpriteAnimation.fromFrameData(
image,
data,
);
animation = SpriteAnimation.fromFrameData(image, data);
}
/// Component will be removed after loop end and [removeOnFinish] is set.

View File

@@ -2,6 +2,7 @@ import 'dart:ui';
import 'package:meta/meta.dart';
import '../extensions/image.dart';
import '../extensions/vector2.dart';
import '../sprite.dart';
import 'component.dart';
@@ -32,11 +33,15 @@ class SpriteComponent extends PositionComponent {
}) : super(position: position, size: size);
factory SpriteComponent.fromImage(
Vector2 size,
Image image, {
Vector2 position,
Vector2 size,
}) =>
SpriteComponent(size: size, sprite: Sprite(image), position: position);
SpriteComponent(
position: position,
size: size ?? image.size,
sprite: Sprite(image),
);
@mustCallSuper
@override

View File

@@ -3,6 +3,7 @@ import 'dart:ui';
import '../flame.dart';
import 'color.dart';
import 'vector2.dart';
export 'dart:ui' show Image;
@@ -15,9 +16,10 @@ extension ImageExtension on Image {
}
/// Returns the bounding [Rect] of the image.
Rect getBoundingRect() {
return Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble());
}
Rect getBoundingRect() => Vector2.zero() & size;
/// Returns a [Vector2] representing the dimensions of this image.
Vector2 get size => Vector2Extension.fromInts(width, height);
/// Change each pixel's color to be darker and return a new [Image].
///

View File

@@ -5,6 +5,7 @@ import 'package:flutter/painting.dart';
import 'assets/images.dart';
import 'extensions/canvas.dart';
import 'extensions/image.dart';
import 'extensions/rect.dart';
import 'extensions/vector2.dart';
import 'flame.dart';
@@ -144,11 +145,7 @@ class ParallaxLayer {
_scale = scale(parallaxImage.fill);
// The image size so that it fulfills the LayerFill parameter
_imageSize = Vector2Extension.fromInts(
parallaxImage.image.width,
parallaxImage.image.height,
) /
_scale;
_imageSize = parallaxImage.image.size / _scale;
// Number of images that can fit on the canvas plus one
// to have something to scroll to without leaving canvas empty

View File

@@ -44,8 +44,7 @@ class Sprite {
Vector2 get srcSize => Vector2(src.width, src.height);
set srcSize(Vector2 size) {
final actualSize =
size ?? Vector2Extension.fromInts(image.width, image.height);
final actualSize = size ?? image.size;
src = srcPosition.toPositionedRect(actualSize);
}
@@ -81,7 +80,7 @@ class Sprite {
/// Return a new Image based on the [src] of the Sprite.
///
/// **Note:** This is a heavy async operation and should not be called on each [Game.render].
/// **Note:** This is a heavy async operation and should not be called inside the game loop.
Future<Image> toImage() async {
final composition = ImageComposition()
..add(image, Vector2.zero(), source: src);

View File

@@ -66,22 +66,25 @@ class SpriteAnimationData {
}
/// Works just like [SpriteAnimationData.variable] but uses the same [stepTime] for all frames
SpriteAnimationData.sequenced({
factory SpriteAnimationData.sequenced({
@required int amount,
@required double stepTime,
@required Vector2 textureSize,
int amountPerRow,
Vector2 texturePosition,
bool loop = true,
}) : this.variable(
}) {
assert(stepTime != null);
return SpriteAnimationData.variable(
amount: amount,
amountPerRow: amountPerRow,
texturePosition: texturePosition,
textureSize: textureSize,
loop: loop,
stepTimes: stepTime != null ? List.filled(amount, stepTime) : null,
stepTimes: List.filled(amount, stepTime),
);
}
}
/// Represents a single sprite animation frame.
class SpriteAnimationFrame {

View File

@@ -4,6 +4,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'assets/images.dart';
import 'extensions/image.dart';
import 'extensions/vector2.dart';
import 'flame.dart';
import 'game/game.dart';
@@ -145,7 +146,7 @@ class SpriteBatch {
int get height => atlas.height;
/// The size of the [atlas].
Vector2 get size => Vector2Extension.fromInts(width, height);
Vector2 get size => atlas.size;
SpriteBatch(
this.atlas, {