Vector2 for position and size on PositionComponent

This commit is contained in:
Lukas Klingsbo
2020-09-24 23:35:36 +02:00
parent 1625ff568b
commit f97f20ab73
33 changed files with 194 additions and 229 deletions

View File

@ -7,6 +7,7 @@
- Remove Position class in favor of new Vector2 extension
- Remove Box2D as a dependency
- Use isRelative on effects
- Use Vector2 for position and size on PositionComponent
## 0.27.0
- Improved the accuracy of the `FPSCounter` by using Flutter's internal frame timings.

View File

@ -4,7 +4,7 @@ This class represent a single object on the screen, being a floating rectangle o
The base abstract class has the common expected methods update and render to be implemented.
The intermediate inheritance `PositionComponent` adds `x`, `y`, `width`, `height` and `angle` to your Components, as well as some useful methods like distance and angleBetween.
The intermediate inheritance `PositionComponent` adds `position`, `size` and `angle` to your Components, as well as some useful methods like distance and angleBetween.
The most commonly used implementation, `SpriteComponent`, can be created with a `Sprite`:
@ -13,12 +13,11 @@ The most commonly used implementation, `SpriteComponent`, can be created with a
Sprite sprite = Sprite('player.png');
const size = 128.0;
var player = SpriteComponent.fromSprite(size, size, sprite); // width, height, sprite
final size = Vector2.all(128.0);
var player = SpriteComponent.fromSprite(size, sprite); // width, height, sprite
// screen coordinates
player.x = ... // 0 by default
player.y = ... // 0 by default
player.position = ... // Vector2(0.0, 0,0) by default
player.angle = ... // 0 by default
player.render(canvas); // it will render only if the image is loaded and the x, y, width and height parameters are not null

View File

@ -20,32 +20,26 @@ class MyGame extends BaseGame with TapDetector {
final animation = SpriteAnimation.sequenced(
'chopper.png',
4,
textureWidth: 48,
textureHeight: 48,
textureSize: Vector2.all(48),
stepTime: 0.15,
loop: true,
);
void addAnimation(double x, double y) {
const textureWidth = 291.0;
const textureHeight = 178.0;
final size = Vector2(291, 178);
final animationComponent = SpriteAnimationComponent.sequenced(
291,
178,
size,
'creature.png',
18,
amountPerRow: 10,
textureWidth: textureWidth,
textureHeight: textureHeight,
textureSize: size,
stepTime: 0.15,
loop: false,
destroyOnFinish: true,
);
animationComponent.x = x - textureWidth / 2;
animationComponent.y = y - textureHeight / 2;
animationComponent.position = animationComponent.position - size / 2;
add(animationComponent);
}
@ -57,18 +51,17 @@ class MyGame extends BaseGame with TapDetector {
MyGame(Vector2 screenSize) {
size = screenSize;
const s = 100.0;
final animationComponent = SpriteAnimationComponent(s, s, animation);
animationComponent.x = size.x / 2 - s;
animationComponent.y = s;
final spriteSize = Vector2.all(100.0);
final animationComponent = SpriteAnimationComponent(spriteSize, animation);
animationComponent.x = size.x / 2 - spriteSize.x;
animationComponent.y = spriteSize.y;
final reversedAnimationComponent = SpriteAnimationComponent(
s,
s,
spriteSize,
animation.reversed(),
);
reversedAnimationComponent.x = size.x / 2;
reversedAnimationComponent.y = s;
reversedAnimationComponent.y = spriteSize.y;
add(animationComponent);
add(reversedAnimationComponent);

View File

@ -22,8 +22,9 @@ class MyGame extends BaseGame {
'chopper.png',
'chopper.json',
);
final animationComponent = SpriteAnimationComponent(200, 200, animation)
..setPosition(size / 2 - Vector2(100, 100));
final spriteSize = Vector2.all(200);
final animationComponent = SpriteAnimationComponent(spriteSize, animation)
..position = size / 2 - Vector2(100, 100);
add(animationComponent);
}

View File

@ -29,7 +29,7 @@ class MyGame extends BaseGame with TapDetector {
@override
void render(Canvas canvas) {
canvas.drawRect(size.toOriginRect(), black);
canvas.drawRect(size.toRect(), black);
final p = size / 2;
regular.render(canvas, 'hit me!', p, anchor: Anchor.center);
super.render(canvas);

View File

@ -3,12 +3,13 @@ import 'package:flame/components/position_component.dart';
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
class Square extends PositionComponent {
final Paint _paint;
Square(this._paint, double x, double y, {double angle = 0.0}) {
width = 100;
height = 100;
size = Vector2(width, height);
this.x = x;
this.y = y;
this.angle = angle;
@ -18,6 +19,6 @@ class Square extends PositionComponent {
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(toOriginRect(), _paint);
canvas.drawRect(size.toRect(), _paint);
}
}

View File

@ -3,12 +3,13 @@ import 'package:flame/components/position_component.dart';
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
class Square extends PositionComponent {
final Paint _paint;
Square(this._paint, double x, double y, {double angle = 0.0}) {
width = 100;
height = 100;
size = Vector2(width, height);
this.x = x;
this.y = y;
this.angle = angle;
@ -18,6 +19,6 @@ class Square extends PositionComponent {
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(toOriginRect(), _paint);
canvas.drawRect(size.toRect(), _paint);
}
}

View File

@ -3,12 +3,13 @@ import 'package:flame/components/position_component.dart';
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
class Square extends PositionComponent {
final Paint _paint;
Square(this._paint, double x, double y, {double angle = 0.0}) {
width = 100;
height = 100;
size = Vector2(width, height);
this.x = x;
this.y = y;
this.angle = angle;
@ -18,6 +19,6 @@ class Square extends PositionComponent {
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(toOriginRect(), _paint);
canvas.drawRect(size.toRect(), _paint);
}
}

View File

@ -3,18 +3,19 @@ import 'package:flame/components/position_component.dart';
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
class Square extends PositionComponent {
static final _paint = Paint()..color = const Color(0xFFFFFFFF);
Square() {
width = 100;
height = 100;
size = Vector2.all(100);
anchor = Anchor.center;
}
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(toOriginRect(), _paint);
canvas.drawRect(size.toRect(), _paint);
}
}

View File

@ -1,3 +1,4 @@
import 'package:flame/extensions/vector2.dart';
import 'package:flutter/material.dart';
import 'package:flame/game.dart';
import 'package:flame/components/position_component.dart';
@ -22,7 +23,7 @@ class TapableSquare extends PositionComponent with Tapable {
bool _beenPressed = false;
TapableSquare({double y = 100, double x = 100}) {
width = height = 100;
size = Vector2.all(100);
this.x = x;
this.y = y;
}
@ -30,7 +31,7 @@ class TapableSquare extends PositionComponent with Tapable {
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(toOriginRect(), _beenPressed ? _grey : _white);
canvas.drawRect(size.toRect(), _beenPressed ? _grey : _white);
}
@override

View File

@ -16,13 +16,12 @@ class MyGame extends BaseGame {
final animation = SpriteAnimation.sequenced(
'chopper.png',
4,
textureWidth: 48,
textureHeight: 48,
textureSize: Vector2.all(48),
stepTime: 0.15,
);
SpriteAnimationComponent buildAnimation() {
final ac = SpriteAnimationComponent(100, 100, animation);
final ac = SpriteAnimationComponent(Vector2.all(100), animation);
ac.x = size.x / 2 - ac.width / 2;
return ac;
}

View File

@ -21,7 +21,7 @@ class Ball extends PositionComponent {
@override
void render(Canvas c) {
super.render(c);
c.drawOval(toOriginRect(), paint);
c.drawOval(size.toRect(), paint);
}
@override
@ -53,8 +53,7 @@ class MyGame extends BaseGame {
add(
Ball(size)
..y = (size.y / 2) - 50
..width = 100
..height = 100,
..size = Vector2(100, 100)
);
}
}

View File

@ -1,3 +1,4 @@
import 'package:flame/extensions/vector2.dart';
import 'package:flutter/material.dart';
import 'package:flame/components/sprite_animation_component.dart';
import 'package:flame/components/sprite_component.dart';
@ -55,12 +56,13 @@ class MyGame extends BaseGame {
final vampireAnimation =
spriteSheet.createAnimation(0, stepTime: 0.1, to: 7);
final ghostAnimation = spriteSheet.createAnimation(1, stepTime: 0.1, to: 7);
final spriteSize = Vector2(80, 90);
final vampireComponent = SpriteAnimationComponent(80, 90, vampireAnimation)
final vampireComponent = SpriteAnimationComponent(spriteSize, vampireAnimation)
..x = 150
..y = 100;
final ghostComponent = SpriteAnimationComponent(80, 90, ghostAnimation)
final ghostComponent = SpriteAnimationComponent(spriteSize, ghostAnimation)
..x = 150
..y = 220;
@ -69,12 +71,12 @@ class MyGame extends BaseGame {
// Some plain sprites
final vampireSpriteComponent =
SpriteComponent.fromSprite(80, 90, spriteSheet.getSprite(0, 0))
SpriteComponent.fromSprite(spriteSize, spriteSheet.getSprite(0, 0))
..x = 50
..y = 100;
final ghostSpriteComponent =
SpriteComponent.fromSprite(80, 90, spriteSheet.getSprite(1, 0))
SpriteComponent.fromSprite(spriteSize, spriteSheet.getSprite(1, 0))
..x = 50
..y = 220;

View File

@ -44,11 +44,11 @@ class MyGame extends BaseGame {
add(TextComponent('center', config: tiny)
..anchor = Anchor.center
..setPosition(size / 2));
..position = size / 2);
add(TextComponent('bottomRight', config: tiny)
..anchor = Anchor.bottomRight
..setPosition(size));
..position = size);
add(MyTextBox(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ligula eu lectus lobortis condimentum.',

View File

@ -2,6 +2,7 @@ import 'dart:math' as math;
import 'dart:ui';
import 'package:flame/anchor.dart';
import 'package:flame/extensions/vector2.dart';
import 'package:flame/gestures.dart';
import 'package:flame/components/position_component.dart';
import 'package:flame/components/mixins/has_game_ref.dart';
@ -31,7 +32,7 @@ class Square extends PositionComponent with HasGameRef<MyGame> {
void render(Canvas c) {
super.render(c);
c.drawRect(toOriginRect(), white);
c.drawRect(size.toRect(), white);
c.drawRect(const Rect.fromLTWH(0, 0, 3, 3), red);
c.drawRect(Rect.fromLTWH(width / 2, height / 2, 3, 3), blue);
}
@ -45,7 +46,7 @@ class Square extends PositionComponent with HasGameRef<MyGame> {
@override
void onMount() {
width = height = gameRef.squareSize;
size = Vector2.all(gameRef.squareSize);
anchor = Anchor.center;
}
}

View File

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
import 'package:flutter/foundation.dart';
import '../flare_animation.dart';
@ -12,18 +13,14 @@ class FlareComponent extends PositionComponent {
FlareComponent(
String fileName,
String animation,
double width,
double height,
Vector2 size,
) {
this.width = width;
this.height = height;
super.size = size;
FlareAnimation.load(fileName).then((loadedFlareAnimation) {
_flareAnimation = loadedFlareAnimation;
_flareAnimation.updateAnimation(animation);
_flareAnimation.width = width;
_flareAnimation.height = height;
_flareAnimation.size = size;
});
}
@ -33,6 +30,13 @@ class FlareComponent extends PositionComponent {
}
}
void updateSize(Vector2 newSize) {
super.size = newSize;
if (loaded()) {
_flareAnimation.size = size;
}
}
@override
bool loaded() => _flareAnimation != null;
@ -50,20 +54,4 @@ class FlareComponent extends PositionComponent {
_flareAnimation.update(dt);
}
}
@override
set width(_width) {
super.width = _width;
if (loaded()) {
_flareAnimation.width = width;
}
}
@override
set height(_height) {
super.height = _height;
if (loaded()) {
_flareAnimation.height = height;
}
}
}

View File

@ -2,6 +2,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import '../extensions/vector2.dart';
import '../nine_tile_box.dart';
import 'position_component.dart';
@ -18,6 +19,6 @@ class NineTileBoxComponent extends PositionComponent {
@override
void render(Canvas c) {
super.render(c);
nineTileBox.drawRect(c, toOriginRect());
nineTileBox.drawRect(c, size.toRect());
}
}

View File

@ -93,7 +93,7 @@ class ParallaxLayer {
// Size of the area to paint the images on
final paintSize = count..multiply(_imageSize);
_paintArea = paintSize.toOriginRect();
_paintArea = paintSize.toRect();
}
void update(Vector2 delta) {

View File

@ -1,5 +1,4 @@
import 'dart:ui';
import 'dart:math';
import 'package:meta/meta.dart';
import 'package:ordered_set/comparing.dart';
@ -15,8 +14,8 @@ import 'component.dart';
/// A [Component] implementation that represents a component that has a
/// specific, possibly dynamic position on the screen.
///
/// It represents a rectangle of dimension ([width], [height]), on the position
/// ([x], [y]), rotate around its center with angle [angle].
/// It represents a rectangle of dimension [size], on the [position],
/// rotated around its [anchor] with angle [angle].
///
/// It also uses the [anchor] property to properly position itself.
///
@ -25,24 +24,31 @@ import 'component.dart';
/// They are translated by this component's (x,y). They do not need to fit
/// within this component's (width, height).
abstract class PositionComponent extends Component {
/// The position of this component on the screen (measured from the top left corner).
Vector2 position = Vector2.zero();
/// X position of this component on the screen (measured from the top left corner).
double x = 0.0;
double get x => position.x;
set x(double x) => position.x = x;
/// Y position of this component on the screen (measured from the top left corner).
double y = 0.0;
double get y => position.y;
set y(double y) => position.y = y;
/// The size that this component is rendered with.
/// This is not necessarily the source size of the asset.
Vector2 size = Vector2.zero();
/// Width (size) that this component is rendered with.
double get width => size.x;
/// Height (size) that this component is rendered with.
double get height => size.y;
/// Angle (with respect to the x-axis) this component should be rendered with.
/// It is rotated around its anchor.
double angle = 0.0;
/// Width (size) that this component is rendered with.
/// This is not necessarily the source width of the asset.
double width = 0.0;
/// Height (size) that this component is rendered with.
/// This is not necessarily the source height of the asset.
double height = 0.0;
/// Anchor point for this component. This is where flame "grabs it".
/// The [x], [y] coordinates are relative to this point inside the component.
/// The [angle] is rotated around this point.
@ -72,51 +78,33 @@ abstract class PositionComponent extends Component {
TextConfig get debugTextConfig => TextConfig(color: debugColor, fontSize: 12);
Vector2 get position => Vector2(x, y);
void setPosition(Vector2 position) {
x = position.x;
y = position.y;
}
Vector2 get size => Vector2(width, height);
void setBySize(Vector2 size) {
width = size.x;
height = size.y;
}
/// Returns the size of this component starting at (0, 0).
/// Effectively this is it's position with respect to itself.
/// Use this if the canvas is already translated by (x, y).
Rect toOriginRect() => Rect.fromLTWH(0, 0, width, height);
/// Returns the relative position/size of this component.
/// Relative because it might be translated by their parents (which is not considered here).
Rect toRect() => Rect.fromLTWH(
Rect toRect() {
return Rect.fromLTWH(
x - anchor.relativePosition.x * width,
y - anchor.relativePosition.y * height,
width,
height,
);
}
/// Mutates x, y, width and height using the provided [rect] as basis.
/// This is a relative rect, same definition that [toRect] use (therefore both methods are compatible, i.e. setByRect ∘ toRect = identity).
void setByRect(Rect rect) {
x = rect.left + anchor.relativePosition.x * rect.width;
y = rect.top + anchor.relativePosition.y * rect.height;
width = rect.width;
height = rect.height;
position.setValues(
rect.left + anchor.relativePosition.x * rect.width,
rect.top + anchor.relativePosition.y * rect.height,
);
size.setValues(rect.width, rect.height);
}
double angleBetween(PositionComponent c) {
return (atan2(c.x - x, y - c.y) - pi / 2) % (2 * pi);
}
double angleTo(PositionComponent c) => position.angleTo(c.position);
double distance(PositionComponent c) {
return c.position.distanceTo(position);
}
double distance(PositionComponent c) => position.distanceTo(c.position);
void renderDebugMode(Canvas canvas) {
canvas.drawRect(toOriginRect(), _debugPaint);
canvas.drawRect(size.toRect(), _debugPaint);
debugTextConfig.render(
canvas,
'x: ${x.toStringAsFixed(2)} y:${y.toStringAsFixed(2)}',

View File

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
import 'package:flutter/foundation.dart';
import '../sprite_animation.dart';
@ -11,41 +12,34 @@ class SpriteAnimationComponent extends PositionComponent {
bool destroyOnFinish = false;
SpriteAnimationComponent(
double width,
double height,
Vector2 size,
this.animation, {
this.destroyOnFinish = false,
}) {
this.width = width;
this.height = height;
super.size.setFrom(size);
}
SpriteAnimationComponent.empty();
SpriteAnimationComponent.sequenced(
double width,
double height,
Vector2 size,
String imagePath,
int amount, {
int amountPerRow,
double textureX = 0.0,
double textureY = 0.0,
double textureWidth,
double textureHeight,
Vector2 texturePosition,
Vector2 textureSize,
double stepTime,
bool loop = true,
this.destroyOnFinish = false,
}) {
this.width = width;
this.height = height;
super.size.setFrom(size);
texturePosition ??= Vector2.zero();
animation = SpriteAnimation.sequenced(
imagePath,
amount,
amountPerRow: amountPerRow,
textureX: textureX,
textureY: textureY,
textureWidth: textureWidth,
textureHeight: textureHeight,
texturePosition: texturePosition,
textureSize: textureSize,
stepTime: stepTime ?? 0.1,
loop: loop,
);

View File

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flame/extensions/vector2.dart';
import 'package:flutter/foundation.dart';
import '../sprite.dart';
@ -23,14 +24,13 @@ class SpriteComponent extends PositionComponent {
SpriteComponent();
SpriteComponent.square(double size, String imagePath)
: this.rectangle(size, size, imagePath);
: this.rectangle(Vector2(size, size), imagePath);
SpriteComponent.rectangle(double width, double height, String imagePath)
: this.fromSprite(width, height, Sprite(imagePath));
SpriteComponent.rectangle(Vector2 size, String imagePath)
: this.fromSprite(size, Sprite(imagePath));
SpriteComponent.fromSprite(double width, double height, this.sprite) {
this.width = width;
this.height = height;
SpriteComponent.fromSprite(Vector2 size, this.sprite) {
super.size.setFrom(size);
}
@mustCallSuper

View File

@ -148,7 +148,7 @@ class TextBoxComponent extends PositionComponent with Resizable {
Future<Image> _redrawCache() {
final PictureRecorder recorder = PictureRecorder();
final Canvas c = Canvas(recorder, toOriginRect());
final Canvas c = Canvas(recorder, size.toRect());
_fullRender(c);
return recorder.endRecording().toImage(width.toInt(), height.toInt());
}

View File

@ -35,8 +35,7 @@ class TextComponent extends PositionComponent {
void _updateBox() {
_tp = config.toTextPainter(_text);
width = _tp.width;
height = _tp.height;
size.setValues(_tp.width, _tp.height);
}
@mustCallSuper

View File

@ -43,6 +43,6 @@ class MoveEffect extends PositionComponentEffect {
void update(double dt) {
super.update(dt);
final double progress = curve?.transform(percentage) ?? 1.0;
component.setPosition(_startPosition + _delta * progress);
component.position = _startPosition + _delta * progress;
}
}

View File

@ -41,6 +41,6 @@ class ScaleEffect extends PositionComponentEffect {
void update(double dt) {
super.update(dt);
final double progress = curve?.transform(percentage) ?? 1.0;
component.setBySize(_startSize + _delta * progress);
component.size = _startSize + _delta * progress;
}
}

View File

@ -31,8 +31,8 @@ class SequenceEffect extends PositionComponentEffect {
final originalAngle = _comp.angle;
effects.forEach((effect) {
effect.reset();
_comp.setBySize(endSize);
_comp.setPosition(endPosition);
_comp.size = endSize;
_comp.position = endPosition;
_comp.angle = endAngle;
effect.initialize(_comp);
endSize = effect.endSize;
@ -43,8 +43,8 @@ class SequenceEffect extends PositionComponentEffect {
0,
(time, effect) => time + effect.totalTravelTime,
);
component.setBySize(originalSize);
component.setPosition(originalPosition);
component.size = originalSize;
component.position = originalPosition;
component.angle = originalAngle;
currentEffect = effects.first;
_currentWasAlternating = currentEffect.isAlternating;

View File

@ -21,6 +21,12 @@ class RectFactory {
return Rect.fromLTWH(0, 0, size.width, size.height);
}
/// Creates a [Rect] having its left upper corner in start and its right
/// bottom corner in end
static Rect fromVectors(Vector2 start, Vector2 end) {
return Rect.fromLTWH(start.x, start.y, end.x, end.y);
}
/// Creates bounds in from of a [Rect] from a list of [Vector2]
static Rect fromBounds(List<Vector2> pts) {
final double minx = pts.map((e) => e.x).reduce(min);

View File

@ -15,11 +15,12 @@ extension Vector2Extension on Vector2 {
/// Creates a [Point] from the [Vector2]
Point toPoint() => Point(x, y);
/// Creates a [Rect] starting from [x, y] and having size [to].
Rect toRect(Vector2 to) => Rect.fromLTWH(x, y, to.x, to.y);
/// Creates a [Rect] starting from [x, y] and having the size of the
/// argument [Vector2]
Rect toPositionedRect(Vector2 size) => Rect.fromLTWH(x, y, size.x, size.y);
/// Creates a [Rect] starting in origin and having size [to].
Rect toOriginRect() => Rect.fromLTWH(0, 0, x, y);
/// Creates a [Rect] starting in origo and going the [Vector2]
Rect toRect() => Rect.fromLTWH(0, 0, x, y);
/// Linearly interpolate towards another Vector2
void lerp(Vector2 to, double t) {

View File

@ -5,6 +5,7 @@ import "package:flare_flutter/flare.dart";
import "package:flare_flutter/flare_actor.dart";
import "flame.dart";
import "extensions/vector2.dart";
@Deprecated("Use flame_flare package instead")
class FlareAnimation {
@ -13,7 +14,8 @@ class FlareAnimation {
String _animationName;
final List<FlareAnimationLayer> _animationLayers = [];
double _width = 0.0, _height = 0.0, _xScale = 0.0, _yScale = 0.0;
final Vector2 _size = Vector2.zero();
final Vector2 _scale = Vector2.zero();
Picture _picture;
@ -32,23 +34,15 @@ class FlareAnimation {
return FlareAnimation(artboard);
}
double get width {
return _width;
double get width => size.x;
double get height => size.y;
set size(Vector2 newSize) {
_size.setFrom(newSize);
_scale.setValues(_size.x / _artboard.width, _size.y / _artboard.height);
}
double get height {
return _height;
}
set width(double newWidth) {
_width = newWidth;
_xScale = _width / _artboard.width;
}
set height(double newHeight) {
_height = newHeight;
_yScale = _height / _artboard.height;
}
Vector2 get size => _size;
void updateAnimation(String animation) {
_animationName = animation;
@ -73,10 +67,8 @@ class FlareAnimation {
if (_picture == null) {
return;
}
canvas.save();
canvas.translate(x, y);
canvas.drawPicture(_picture);
canvas.restore();
}
@ -125,7 +117,7 @@ class FlareAnimation {
final r = PictureRecorder();
final c = Canvas(r);
c.scale(_xScale, _yScale);
c.scale(_scale.x, _scale.y);
_artboard.draw(c);
_picture = r.endRecording();

View File

@ -12,16 +12,14 @@ class Sprite {
Sprite(
String fileName, {
double x = 0.0,
double y = 0.0,
double width,
double height,
Vector2 position,
Vector2 size,
}) {
position ??= Vector2.zero();
Flame.images.load(fileName).then((img) {
width ??= img.width.toDouble();
height ??= img.height.toDouble();
size ??= Vector2(img.width.toDouble(), img.height.toDouble());
image = img;
src = Rect.fromLTWH(x, y, width, height);
src = position.toPositionedRect(size);
});
}
@ -100,7 +98,7 @@ class Sprite {
return;
}
size ??= this.size;
renderRect(canvas, p.toRect(size), overridePaint: overridePaint);
renderRect(canvas, p.toPositionedRect(size), overridePaint: overridePaint);
}
void render(

View File

@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:flame/extensions/vector2.dart';
import 'flame.dart';
import 'sprite.dart';
@ -75,22 +77,23 @@ class SpriteAnimation {
String imagePath,
int amount, {
int amountPerRow,
double textureX = 0.0,
double textureY = 0.0,
double textureWidth,
double textureHeight,
Vector2 texturePosition,
Vector2 textureSize,
double stepTime = 0.1,
this.loop = true,
}) : assert(amountPerRow == null || amount >= amountPerRow) {
amountPerRow ??= amount;
texturePosition ??= Vector2.zero();
frames = List<SpriteAnimationFrame>(amount);
for (var i = 0; i < amount; i++) {
final position = Vector2(
texturePosition.x + (i % amountPerRow) * textureSize.x,
texturePosition.y + (i ~/ amountPerRow) * textureSize.y,
);
final Sprite sprite = Sprite(
imagePath,
x: textureX + (i % amountPerRow) * textureWidth,
y: textureY + (i ~/ amountPerRow) * textureHeight,
width: textureWidth,
height: textureHeight,
position: position,
size: textureSize,
);
frames[i] = SpriteAnimationFrame(sprite, stepTime);
}
@ -102,21 +105,21 @@ class SpriteAnimation {
int amount,
List<double> stepTimes, {
int amountPerRow,
double textureX = 0.0,
double textureY = 0.0,
double textureWidth,
double textureHeight,
Vector2 texturePosition,
Vector2 textureSize,
this.loop = true,
}) : assert(amountPerRow == null || amount >= amountPerRow) {
amountPerRow ??= amount;
frames = List<SpriteAnimationFrame>(amount);
for (var i = 0; i < amount; i++) {
final position = Vector2(
texturePosition.x + (i % amountPerRow) * textureSize.x,
texturePosition.y + (i ~/ amountPerRow) * textureSize.y,
);
final Sprite sprite = Sprite(
imagePath,
x: textureX + (i % amountPerRow) * textureWidth,
y: textureY + (i ~/ amountPerRow) * textureHeight,
width: textureWidth,
height: textureHeight,
position: position,
size: textureSize,
);
frames[i] = SpriteAnimationFrame(sprite, stepTimes[i]);
}
@ -136,19 +139,17 @@ class SpriteAnimation {
final frames = jsonFrames.values.map((value) {
final frameData = value['frame'];
final int x = frameData['x'];
final int y = frameData['y'];
final int width = frameData['w'];
final int height = frameData['h'];
final double x = frameData['x'];
final double y = frameData['y'];
final double width = frameData['w'];
final double height = frameData['h'];
final stepTime = value['duration'] / 1000;
final Sprite sprite = Sprite(
imagePath,
x: x.toDouble(),
y: y.toDouble(),
width: width.toDouble(),
height: height.toDouble(),
position: Vector2(x, y),
size: Vector2(width, height),
);
return SpriteAnimationFrame(sprite, stepTime);

View File

@ -1,8 +1,10 @@
import 'dart:ui';
import 'package:meta/meta.dart';
import 'sprite.dart';
import 'sprite_animation.dart';
import 'extensions/vector2.dart';
/// Utility class to help extract animations and sprites from a spritesheet image
class SpriteSheet {
@ -36,12 +38,11 @@ class SpriteSheet {
int x,
int y,
) {
Vector2 size = Vector2(textureWidth.toDouble(), textureHeight.toDouble());
return Sprite(
imageName,
x: (x * textureWidth).toDouble(),
y: (y * textureHeight).toDouble(),
width: textureWidth.toDouble(),
height: textureHeight.toDouble(),
position: Vector2(x.toDouble(), y.toDouble())..multiply(size),
size: size,
);
}

View File

@ -9,34 +9,30 @@ void main() {
group('component test', () {
test('test get/set x/y or position', () {
final PositionComponent c = SpriteComponent();
c.x = 2.2;
c.y = 3.4;
expect(c.position.x, 2.2);
expect(c.position.y, 3.4);
c.position = Vector2(2.2, 3.4);
expect(c.x, 2.2);
expect(c.y, 3.4);
c.setPosition(Vector2(1.0, 0.0));
c.position = Vector2(1.0, 0.0);
expect(c.x, 1.0);
expect(c.y, 0.0);
});
test('test get/set width/height or size', () {
final PositionComponent c = SpriteComponent();
c.width = 2.2;
c.height = 3.4;
c.size = Vector2(2.2, 3.4);
expect(c.size.x, 2.2);
expect(c.size.y, 3.4);
c.setBySize(Vector2(1.0, 0.0));
c.size = Vector2(1.0, 0.0);
expect(c.width, 1.0);
expect(c.height, 0.0);
});
test('test get/set rect', () {
final PositionComponent c = SpriteComponent();
c.x = 0.0;
c.y = 1.0;
c.width = 2.0;
c.height = 2.0;
c.position = Vector2(0.0, 1.0);
c.size = Vector2(2.0, 2.0);
expect(c.toRect().left, 0.0);
expect(c.toRect().top, 1.0);
expect(c.toRect().width, 2.0);