mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-13 03:10:42 +08:00
This commit is contained in:
@@ -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 > 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
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ class MyGame extends BaseGame {
|
||||
size: Vector2.all(32),
|
||||
sprite: Sprite(image),
|
||||
),
|
||||
).forEach((sprite) {
|
||||
add(sprite);
|
||||
});
|
||||
).forEach(add);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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].
|
||||
///
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, {
|
||||
|
||||
Reference in New Issue
Block a user