diff --git a/CHANGELOG.md b/CHANGELOG.md index e060cac90..db3e28936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - Remove Position class in favor of new Vector2 extension - Remove Box2D as a dependency - Rebuild of Images, Sprite and SpriteAnimation initialization + - 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. diff --git a/doc/components.md b/doc/components.md index 495728210..a0c73ecce 100644 --- a/doc/components.md +++ b/doc/components.md @@ -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,15 +13,14 @@ 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); // 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 + player.render(canvas); // it will render only if the image is loaded and the position and size parameters are not null ``` In the event that you want to easily change the direction of your components rendering, you can also use @@ -55,14 +54,16 @@ This component uses an instance of the [Animation](/doc/images.md#Animation) cla This will create a simple three frame animation ```dart - List sprites = [0, 1, 2].map((i) => new Sprite('player_${i}.png')).toList(); - this.player = AnimationComponent(64.0, 64.0, new Animation.spriteList(sprites, stepTime: 0.01)); + List sprites = [0, 1, 2].map((i) => Sprite('player_${i}.png')).toList(); + final size = Vector2.all(64.0); + this.player = AnimationComponent(size, new Animation.spriteList(sprites, stepTime: 0.01)); ``` 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](/doc/images.md#Animation)): ```dart - this.player = AnimationComponent.sequenced(64.0, 64.0, 'player.png', 2); + final size = Vector2.all(64.0); + this.player = AnimationComponent.sequenced(size, 'player.png', 2); ``` 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. @@ -105,8 +106,7 @@ it also can receive a FlareController that can play multiple animations and cont } final fileName = 'assets/george_washington.flr'; - final width = 1776; - final height = 1804; + final size = Vector2(1776, 1804); final controller = WashingtonController(); //instantiate controller FlareActorComponent flareAnimation = FlareActorComponent( @@ -190,8 +190,8 @@ This creates a static background, if you want it to move you have to set the nam You can set the baseSpeed and layerDelta at any time, for example if your character jumps or your game speeds up. ```dart - this.bg.baseSpeed = Offset(100, 0); - this.bg.layerDelta = Offset(40, 0); + this.bg.baseSpeed = Vector2(100, 0); + this.bg.layerDelta = Vector2(40, 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`, `alignment` and `fill` parameters for each ParallaxImage. @@ -203,7 +203,7 @@ Advanced example: 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)); + this.bg = ParallaxComponent(images, baseSpeed: Vector2(50, 0), layerDelta: Vector2(20, 0)); ``` * The stars image in this example will be repeatedly drawn in both axis, align in the center and be scaled to fill the screen width. diff --git a/doc/effects.md b/doc/effects.md index 5faa1b53c..9cfea0d89 100644 --- a/doc/effects.md +++ b/doc/effects.md @@ -14,6 +14,8 @@ If `isInfinite` is false and `isAlternating` is true the effect will go from the `isInfinite` and `isAlternating` are false by default and then the effect is just applied once, from the beginning of the curve until the end. +`isRelative` is used on some effects, it is false by default. If it is set to true it means that the effect starts at the PositionComponent's value and adds whatever value you give to it. If it is false it will treat the value you give it as absolute and it will go to the value you give it no matter where it started. + When an effect is completed the callback `onComplete` will be called, it can be set as an optional argument to your effect. ## MoveEffect diff --git a/doc/examples/animation_widget/lib/main.dart b/doc/examples/animation_widget/lib/main.dart index 9800d6155..75b74dbf4 100644 --- a/doc/examples/animation_widget/lib/main.dart +++ b/doc/examples/animation_widget/lib/main.dart @@ -15,9 +15,8 @@ SpriteAnimation _animation; void main() async { WidgetsFlutterBinding.ensureInitialized(); final image = await Flame.images.load('minotaur.png'); - _sprite = Sprite(image, width: 96, height: 96); + _sprite = Sprite(image, size: Vector2.all(96)); - await Flame.images.load('minotaur.png'); final _animationSpriteSheet = SpriteSheet( image: image, columns: 19, diff --git a/doc/examples/animations/lib/main.dart b/doc/examples/animations/lib/main.dart index cd8dd54d4..057f74010 100644 --- a/doc/examples/animations/lib/main.dart +++ b/doc/examples/animations/lib/main.dart @@ -29,47 +29,40 @@ class MyGame extends BaseGame with TapDetector { animation = SpriteAnimation.sequenced( chopper, 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, 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); - const s = 100.0; - final animationComponent2 = SpriteAnimationComponent(s, s, animation); - animationComponent2.x = size.x / 2 - s; - animationComponent2.y = s; + final spriteSize = Vector2.all(100.0); + final animationComponent2 = SpriteAnimationComponent(spriteSize, animation); + animationComponent2.x = size.x / 2 - spriteSize.x; + animationComponent2.y = spriteSize.y; final reversedAnimationComponent = SpriteAnimationComponent( - s, - s, + spriteSize, animation.reversed(), ); reversedAnimationComponent.x = size.x / 2; - reversedAnimationComponent.y = s; + reversedAnimationComponent.y = spriteSize.y; add(animationComponent2); add(reversedAnimationComponent); diff --git a/doc/examples/aseprite/lib/main.dart b/doc/examples/aseprite/lib/main.dart index d395aebae..2b85773cc 100644 --- a/doc/examples/aseprite/lib/main.dart +++ b/doc/examples/aseprite/lib/main.dart @@ -23,8 +23,9 @@ class MyGame extends BaseGame { image, '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.all(100); add(animationComponent); } diff --git a/doc/examples/audiopool/lib/main.dart b/doc/examples/audiopool/lib/main.dart index ebcac152d..de6868de7 100644 --- a/doc/examples/audiopool/lib/main.dart +++ b/doc/examples/audiopool/lib/main.dart @@ -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); diff --git a/doc/examples/debug/lib/main.dart b/doc/examples/debug/lib/main.dart index bf917beda..02536fdb8 100644 --- a/doc/examples/debug/lib/main.dart +++ b/doc/examples/debug/lib/main.dart @@ -22,7 +22,7 @@ class AndroidComponent extends SpriteComponent with Resizable { int xDirection = 1; int yDirection = 1; - AndroidComponent(Image image) : super.square(100, image); + AndroidComponent(Image image) : super.fromImage(Vector2.all(100), image); @override void update(double dt) { diff --git a/doc/examples/effects/combined_effects/lib/main.dart b/doc/examples/effects/combined_effects/lib/main.dart index 2e927a4be..c1c35a307 100644 --- a/doc/examples/effects/combined_effects/lib/main.dart +++ b/doc/examples/effects/combined_effects/lib/main.dart @@ -24,8 +24,8 @@ class MyGame extends BaseGame with TapDetector { MyGame() { final green = Paint()..color = const Color(0xAA338833); final red = Paint()..color = const Color(0xAA883333); - greenSquare = Square(green, 100, 100); - final redSquare = Square(red, 100, 100); + greenSquare = Square(green, Vector2.all(100)); + final redSquare = Square(red, Vector2.all(100)); add(greenSquare); add(redSquare); } diff --git a/doc/examples/effects/combined_effects/lib/square.dart b/doc/examples/effects/combined_effects/lib/square.dart index 9692f706b..93154bf4d 100644 --- a/doc/examples/effects/combined_effects/lib/square.dart +++ b/doc/examples/effects/combined_effects/lib/square.dart @@ -3,14 +3,14 @@ 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; - this.x = x; - this.y = y; + Square(this._paint, Vector2 position, {double angle = 0.0}) { + size = Vector2.all(100.0); + this.position = position; this.angle = angle; anchor = Anchor.center; } @@ -18,6 +18,6 @@ class Square extends PositionComponent { @override void render(Canvas canvas) { super.render(canvas); - canvas.drawRect(toOriginRect(), _paint); + canvas.drawRect(size.toRect(), _paint); } } diff --git a/doc/examples/effects/infinite_effects/lib/main.dart b/doc/examples/effects/infinite_effects/lib/main.dart index ad1218f65..50e3422ec 100644 --- a/doc/examples/effects/infinite_effects/lib/main.dart +++ b/doc/examples/effects/infinite_effects/lib/main.dart @@ -26,9 +26,9 @@ class MyGame extends BaseGame with TapDetector { final green = Paint()..color = const Color(0xAA338833); final red = Paint()..color = const Color(0xAA883333); final orange = Paint()..color = const Color(0xAABB6633); - greenSquare = Square(green, 100, 100); - redSquare = Square(red, 200, 200); - orangeSquare = Square(orange, 200, 400); + greenSquare = Square(green, Vector2.all(100)); + redSquare = Square(red, Vector2.all(200)); + orangeSquare = Square(orange, Vector2(200, 400)); add(greenSquare); add(redSquare); add(orangeSquare); diff --git a/doc/examples/effects/infinite_effects/lib/square.dart b/doc/examples/effects/infinite_effects/lib/square.dart index 9692f706b..93154bf4d 100644 --- a/doc/examples/effects/infinite_effects/lib/square.dart +++ b/doc/examples/effects/infinite_effects/lib/square.dart @@ -3,14 +3,14 @@ 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; - this.x = x; - this.y = y; + Square(this._paint, Vector2 position, {double angle = 0.0}) { + size = Vector2.all(100.0); + this.position = position; this.angle = angle; anchor = Anchor.center; } @@ -18,6 +18,6 @@ class Square extends PositionComponent { @override void render(Canvas canvas) { super.render(canvas); - canvas.drawRect(toOriginRect(), _paint); + canvas.drawRect(size.toRect(), _paint); } } diff --git a/doc/examples/effects/sequence_effect/lib/main.dart b/doc/examples/effects/sequence_effect/lib/main.dart index 0727f1eff..03ca751c4 100644 --- a/doc/examples/effects/sequence_effect/lib/main.dart +++ b/doc/examples/effects/sequence_effect/lib/main.dart @@ -23,7 +23,7 @@ class MyGame extends BaseGame with TapDetector { MyGame() { final green = Paint()..color = const Color(0xAA338833); - greenSquare = Square(green, 100, 100); + greenSquare = Square(green, Vector2.all(100)); add(greenSquare); } diff --git a/doc/examples/effects/sequence_effect/lib/square.dart b/doc/examples/effects/sequence_effect/lib/square.dart index 9692f706b..93154bf4d 100644 --- a/doc/examples/effects/sequence_effect/lib/square.dart +++ b/doc/examples/effects/sequence_effect/lib/square.dart @@ -3,14 +3,14 @@ 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; - this.x = x; - this.y = y; + Square(this._paint, Vector2 position, {double angle = 0.0}) { + size = Vector2.all(100.0); + this.position = position; this.angle = angle; anchor = Anchor.center; } @@ -18,6 +18,6 @@ class Square extends PositionComponent { @override void render(Canvas canvas) { super.render(canvas); - canvas.drawRect(toOriginRect(), _paint); + canvas.drawRect(size.toRect(), _paint); } } diff --git a/doc/examples/effects/simple/lib/square.dart b/doc/examples/effects/simple/lib/square.dart index cbdb6c489..b34fab6b7 100644 --- a/doc/examples/effects/simple/lib/square.dart +++ b/doc/examples/effects/simple/lib/square.dart @@ -1,18 +1,21 @@ +import 'package:flame/anchor.dart'; 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); } } diff --git a/doc/examples/gestures/lib/main_tapables.dart b/doc/examples/gestures/lib/main_tapables.dart index dbafba2a1..898f69a83 100644 --- a/doc/examples/gestures/lib/main_tapables.dart +++ b/doc/examples/gestures/lib/main_tapables.dart @@ -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'; @@ -21,16 +22,15 @@ class TapableSquare extends PositionComponent with Tapable { bool _beenPressed = false; - TapableSquare({double y = 100, double x = 100}) { - width = height = 100; - this.x = x; - this.y = y; + TapableSquare({Vector2 position}) { + size = Vector2.all(100); + this.position = position ?? Vector2.all(100); } @override void render(Canvas canvas) { super.render(canvas); - canvas.drawRect(toOriginRect(), _beenPressed ? _grey : _white); + canvas.drawRect(size.toRect(), _beenPressed ? _grey : _white); } @override @@ -51,7 +51,7 @@ class TapableSquare extends PositionComponent with Tapable { class MyGame extends BaseGame with HasTapableComponents { MyGame() { - add(TapableSquare(y: 100)); - add(TapableSquare(y: 250)); + add(TapableSquare()); + add(TapableSquare()..y = 250); } } diff --git a/doc/examples/isometric/lib/main.dart b/doc/examples/isometric/lib/main.dart index c25d4ee0f..10b47c48e 100644 --- a/doc/examples/isometric/lib/main.dart +++ b/doc/examples/isometric/lib/main.dart @@ -1,6 +1,5 @@ import 'package:flame/components/sprite_component.dart'; import 'package:flame/extensions/vector2.dart'; -import 'package:flame/extensions/offset.dart'; import 'package:flame/game.dart'; import 'package:flame/components/isometric_tile_map_component.dart'; import 'package:flame/gestures.dart'; @@ -25,7 +24,8 @@ class Selector extends SpriteComponent { bool show = false; Selector(double s, Image image) - : super.fromSprite(s, s, Sprite(image, width: 32, height: 32)); + : super.fromSprite( + Vector2.all(s), Sprite(image, size: Vector2.all(32.0))); @override void render(Canvas canvas) { @@ -80,9 +80,10 @@ class MyGame extends BaseGame with MouseMovementDetector { if (base == null || selector == null) { return; // loading } - final screenPosition = event.position.toVector2(); + final offset = event.position; + final screenPosition = Vector2(offset.dx, offset.dy); final block = base.getBlock(screenPosition); selector.show = base.containsBlock(block); - selector.setPosition(base.getBlockPosition(block) + topLeft); + selector.position = base.getBlockPosition(block) + topLeft; } } diff --git a/doc/examples/render_flip/lib/main.dart b/doc/examples/render_flip/lib/main.dart index 5ffa54b2c..a9d13ce53 100644 --- a/doc/examples/render_flip/lib/main.dart +++ b/doc/examples/render_flip/lib/main.dart @@ -21,8 +21,7 @@ class MyGame extends BaseGame { animation = SpriteAnimation.sequenced( image, 4, - textureWidth: 48, - textureHeight: 48, + textureSize: Vector2.all(48), stepTime: 0.15, ); @@ -42,7 +41,7 @@ class MyGame extends BaseGame { } SpriteAnimationComponent buildAnimation() { - final ac = SpriteAnimationComponent(100, 100, animation); + final ac = SpriteAnimationComponent(Vector2.all(100), animation); ac.x = size.x / 2 - ac.x / 2; return ac; } diff --git a/doc/examples/sound/lib/main.dart b/doc/examples/sound/lib/main.dart index 873109208..bcb4b5623 100644 --- a/doc/examples/sound/lib/main.dart +++ b/doc/examples/sound/lib/main.dart @@ -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 @@ -50,11 +50,8 @@ class MyGame extends BaseGame { Flame.audio.load('boin.mp3'); Flame.audio.loop('music.mp3', volume: 0.4); - add( - Ball(size) - ..y = (size.y / 2) - 50 - ..width = 100 - ..height = 100, - ); + add(Ball(size) + ..y = (size.y / 2) - 50 + ..size = Vector2.all(100)); } } diff --git a/doc/examples/sprites/lib/main.dart b/doc/examples/sprites/lib/main.dart index 5cc631c79..8ec46027d 100644 --- a/doc/examples/sprites/lib/main.dart +++ b/doc/examples/sprites/lib/main.dart @@ -22,7 +22,7 @@ class MyGame extends BaseGame { Future onLoad() async { final r = Random(); final image = await images.load('test.png'); - List.generate(500, (i) => SpriteComponent.square(32, image)) + List.generate(500, (i) => SpriteComponent.fromImage(Vector2.all(32), image)) .forEach((sprite) { sprite.x = r.nextInt(size.x.toInt()).toDouble(); sprite.y = r.nextInt(size.y.toInt()).toDouble(); diff --git a/doc/examples/spritesheet/lib/main.dart b/doc/examples/spritesheet/lib/main.dart index c61113fc8..571a8355c 100644 --- a/doc/examples/spritesheet/lib/main.dart +++ b/doc/examples/spritesheet/lib/main.dart @@ -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'; @@ -24,12 +25,14 @@ 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) - ..x = 150 - ..y = 100; + final vampireComponent = + SpriteAnimationComponent(spriteSize, vampireAnimation) + ..x = 150 + ..y = 100; - final ghostComponent = SpriteAnimationComponent(80, 90, ghostAnimation) + final ghostComponent = SpriteAnimationComponent(spriteSize, ghostAnimation) ..x = 150 ..y = 220; @@ -38,12 +41,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; diff --git a/doc/examples/text/lib/main.dart b/doc/examples/text/lib/main.dart index 09d3d7f1b..328f2491a 100644 --- a/doc/examples/text/lib/main.dart +++ b/doc/examples/text/lib/main.dart @@ -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.', diff --git a/example/lib/main.dart b/example/lib/main.dart index e9eea1bff..7484d7174 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -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 { 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 { @override void onMount() { - width = height = gameRef.squareSize; + size = Vector2.all(gameRef.squareSize); anchor = Anchor.center; } } diff --git a/lib/anchor.dart b/lib/anchor.dart index 4e5d20657..ff63335e5 100644 --- a/lib/anchor.dart +++ b/lib/anchor.dart @@ -19,7 +19,6 @@ class Anchor { const Anchor(this.x, this.y); Vector2 translate(Vector2 p, Vector2 size) { - return p - relativePosition - ..multiply(size); + return p - (relativePosition..multiply(size)); } } diff --git a/lib/components/flare_component.dart b/lib/components/flare_component.dart index 1b5884f36..5f6855448 100644 --- a/lib/components/flare_component.dart +++ b/lib/components/flare_component.dart @@ -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,14 @@ class FlareComponent extends PositionComponent { } } + @override + set size(Vector2 newSize) { + super.size = newSize; + if (loaded()) { + _flareAnimation.size = size; + } + } + @override bool loaded() => _flareAnimation != null; @@ -50,20 +55,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; - } - } } diff --git a/lib/components/isometric_tile_map_component.dart b/lib/components/isometric_tile_map_component.dart index 7b006b8fa..a4252bf8f 100644 --- a/lib/components/isometric_tile_map_component.dart +++ b/lib/components/isometric_tile_map_component.dart @@ -45,7 +45,8 @@ class IsometricTileset { final i = tileId % columns; final j = tileId ~/ columns; final s = size.toDouble(); - return Sprite(tileset, x: s * i, y: s * j, width: s, height: s); + return Sprite(tileset, + position: Vector2(s * i, s * j), size: Vector2.all(s)); } } @@ -86,17 +87,14 @@ class IsometricTileMapComponent extends PositionComponent { void render(Canvas c) { super.render(c); - final size = Vector2( - effectiveTileSize.toDouble(), - effectiveTileSize.toDouble(), - ); + final size = Vector2.all(effectiveTileSize.toDouble()); for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { final element = matrix[i][j]; if (element != -1) { final sprite = tileset.getTile(element); final p = getBlockPositionInts(j, i); - sprite.renderRect(c, p.toRect(size)); + sprite.renderRect(c, p.toPositionedRect(size)); } } } diff --git a/lib/components/nine_tile_box_component.dart b/lib/components/nine_tile_box_component.dart index caf0d306c..88e489ddd 100644 --- a/lib/components/nine_tile_box_component.dart +++ b/lib/components/nine_tile_box_component.dart @@ -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()); } } diff --git a/lib/components/parallax_component.dart b/lib/components/parallax_component.dart index c16dbce19..b6c6befac 100644 --- a/lib/components/parallax_component.dart +++ b/lib/components/parallax_component.dart @@ -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) { diff --git a/lib/components/position_component.dart b/lib/components/position_component.dart index 0b057d569..69d596ead 100644 --- a/lib/components/position_component.dart +++ b/lib/components/position_component.dart @@ -1,5 +1,4 @@ -import 'dart:ui'; -import 'dart:math'; +import 'dart:ui' hide Offset; import 'package:meta/meta.dart'; import 'package:ordered_set/comparing.dart'; @@ -9,14 +8,15 @@ import '../anchor.dart'; import '../effects/effects.dart'; import '../game.dart'; import '../text_config.dart'; +import '../extensions/offset.dart'; import '../extensions/vector2.dart'; 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,26 +25,43 @@ 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 { - /// X position of this component on the screen (measured from the top left corner). - double x = 0.0; + /// The position of this component on the screen (relative to the anchor). + Vector2 position = Vector2.zero(); - /// Y position of this component on the screen (measured from the top left corner). - double y = 0.0; + /// X position of this component on the screen (relative to the anchor). + double get x => position.x; + set x(double x) => position.x = x; + + /// Y position of this component on the screen (relative to the anchor). + 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; + set width(double width) => size.x = width; + + /// Height (size) that this component is rendered with. + double get height => size.y; + set height(double height) => size.y = height; + + /// Get the top left position regardless of the anchor + Vector2 get topLeftPosition => anchor.translate(position, size); + + /// Set the top left position regardless of the anchor + set topLeftPosition(Vector2 position) { + this.position = position + (anchor.relativePosition..multiply(size)); + } /// 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 [position] is relative to this point inside the component. /// The [angle] is rotated around this point. Anchor anchor = Anchor.topLeft; @@ -72,51 +89,23 @@ 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 toSize() => 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( - x - anchor.relativePosition.x * width, - y - anchor.relativePosition.y * height, - width, - height, - ); + Rect toRect() => topLeftPosition.toPositionedRect(size); - /// Mutates x, y, width and height using the provided [rect] as basis. + /// Mutates position and size 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; + size.setValues(rect.width, rect.height); + topLeftPosition = rect.topLeft.toVector2(); } - 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)}', @@ -137,9 +126,9 @@ abstract class PositionComponent extends Component { canvas.translate(x, y); canvas.rotate(angle); - final double dx = -anchor.relativePosition.x * width; - final double dy = -anchor.relativePosition.y * height; - canvas.translate(dx, dy); + final Vector2 delta = -anchor.relativePosition + ..multiply(size); + canvas.translate(delta.x, delta.y); // Handle inverted rendering by moving center and flipping. if (renderFlipX || renderFlipY) { diff --git a/lib/components/sprite_animation_component.dart b/lib/components/sprite_animation_component.dart index 7308c4fd1..bca6f2eb2 100644 --- a/lib/components/sprite_animation_component.dart +++ b/lib/components/sprite_animation_component.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flame/extensions/vector2.dart'; import 'package:flutter/foundation.dart'; import '../sprite_animation.dart'; @@ -11,71 +12,58 @@ 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, Image image, 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( image, amount, amountPerRow: amountPerRow, - textureX: textureX, - textureY: textureY, - textureWidth: textureWidth, - textureHeight: textureHeight, + texturePosition: texturePosition, + textureSize: textureSize, stepTime: stepTime ?? 0.1, loop: loop, ); } SpriteAnimationComponent.variableSequenced( - double width, - double height, + Vector2 size, Image image, int amount, List stepTimes, { int amountPerRow, - double textureX = 0.0, - double textureY = 0.0, - double textureWidth, - double textureHeight, + Vector2 texturePosition, + Vector2 textureSize, bool loop = true, }) { - this.width = width; - this.height = height; + super.size.setFrom(size); animation = SpriteAnimation.variableSequenced( image, amount, stepTimes, amountPerRow: amountPerRow, - textureX: textureX, - textureY: textureY, - textureWidth: textureWidth, - textureHeight: textureHeight, + texturePosition: texturePosition, + textureSize: textureSize, loop: loop, ); } diff --git a/lib/components/sprite_component.dart b/lib/components/sprite_component.dart index 565331394..73cafc3bb 100644 --- a/lib/components/sprite_component.dart +++ b/lib/components/sprite_component.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flame/extensions/vector2.dart'; import 'package:flutter/foundation.dart'; import '../sprite.dart'; @@ -22,15 +23,11 @@ class SpriteComponent extends PositionComponent { SpriteComponent(); - SpriteComponent.square(double size, Image image) - : this.rectangle(size, size, image); + SpriteComponent.fromImage(Vector2 size, Image image) + : this.fromSprite(size, Sprite(image)); - SpriteComponent.rectangle(double width, double height, Image image) - : this.fromSprite(width, height, Sprite(image)); - - 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 diff --git a/lib/components/text_box_component.dart b/lib/components/text_box_component.dart index 27510d717..0e00110c5 100644 --- a/lib/components/text_box_component.dart +++ b/lib/components/text_box_component.dart @@ -148,7 +148,7 @@ class TextBoxComponent extends PositionComponent with Resizable { Future _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()); } diff --git a/lib/components/text_component.dart b/lib/components/text_component.dart index 08fe5260a..3db84da42 100644 --- a/lib/components/text_component.dart +++ b/lib/components/text_component.dart @@ -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 diff --git a/lib/effects/combined_effect.dart b/lib/effects/combined_effect.dart index afe848ddb..fce5b9517 100644 --- a/lib/effects/combined_effect.dart +++ b/lib/effects/combined_effect.dart @@ -28,7 +28,7 @@ class CombinedEffect extends PositionComponentEffect { super.initialize(_comp); effects.forEach((effect) { effect.initialize(_comp); - final isSameSize = effect.endSize == _comp.toSize(); + final isSameSize = effect.endSize == _comp.size; final isSamePosition = effect.endPosition == _comp.position; final isSameAngle = effect.endAngle == _comp.angle; endSize = isSameSize ? endSize : effect.endSize; diff --git a/lib/effects/effects.dart b/lib/effects/effects.dart index 017cd7b28..15d136925 100644 --- a/lib/effects/effects.dart +++ b/lib/effects/effects.dart @@ -22,6 +22,7 @@ abstract class PositionComponentEffect { /// curve backwards bool isInfinite; bool isAlternating; + final bool isRelative; final bool _initialIsInfinite; final bool _initialIsAlternating; double percentage; @@ -42,6 +43,7 @@ abstract class PositionComponentEffect { PositionComponentEffect( this._initialIsInfinite, this._initialIsAlternating, { + this.isRelative = false, this.onComplete, }) { isInfinite = _initialIsInfinite; @@ -78,7 +80,7 @@ abstract class PositionComponentEffect { /// started in endPosition = _comp.position; endAngle = _comp.angle; - endSize = _comp.toSize(); + endSize = _comp.size; } void dispose() => _isDisposed = true; diff --git a/lib/effects/move_effect.dart b/lib/effects/move_effect.dart index 39ef3f1b3..3b2a5f919 100644 --- a/lib/effects/move_effect.dart +++ b/lib/effects/move_effect.dart @@ -1,26 +1,15 @@ -import 'dart:math'; - import 'package:flutter/animation.dart'; import 'package:meta/meta.dart'; import '../extensions/vector2.dart'; import 'effects.dart'; -double _direction(double p, double d) => (p - d).sign; -double _distance(double a, double b) => (a - b).abs(); - class MoveEffect extends PositionComponentEffect { Vector2 destination; double speed; Curve curve; - - double _xOriginal; - double _xDistance; - double _xDirection; - - double _yOriginal; - double _yDistance; - double _yDirection; + Vector2 _startPosition; + Vector2 _delta; MoveEffect({ @required this.destination, @@ -28,8 +17,14 @@ class MoveEffect extends PositionComponentEffect { this.curve, isInfinite = false, isAlternating = false, + isRelative = false, Function onComplete, - }) : super(isInfinite, isAlternating, onComplete: onComplete); + }) : super( + isInfinite, + isAlternating, + isRelative: isRelative, + onComplete: onComplete, + ); @override void initialize(_comp) { @@ -37,26 +32,15 @@ class MoveEffect extends PositionComponentEffect { if (!isAlternating) { endPosition = destination; } - - _xOriginal = component.x; - _yOriginal = component.y; - - _xDistance = _distance(destination.x, component.x); - _yDistance = _distance(destination.y, component.y); - - _xDirection = _direction(destination.x, component.x); - _yDirection = _direction(destination.y, component.y); - - final totalDistance = sqrt(pow(_xDistance, 2) + pow(_yDistance, 2)); - travelTime = totalDistance / speed; + _startPosition = component.position.clone(); + _delta = isRelative ? destination : destination - _startPosition; + travelTime = _delta.length / speed; } @override void update(double dt) { super.update(dt); - final double c = curve?.transform(percentage) ?? 1.0; - - component.x = _xOriginal + _xDistance * c * _xDirection; - component.y = _yOriginal + _yDistance * c * _yDirection; + final double progress = curve?.transform(percentage) ?? 1.0; + component.position = _startPosition + _delta * progress; } } diff --git a/lib/effects/rotate_effect.dart b/lib/effects/rotate_effect.dart index 30e46adcc..737f34875 100644 --- a/lib/effects/rotate_effect.dart +++ b/lib/effects/rotate_effect.dart @@ -7,10 +7,8 @@ class RotateEffect extends PositionComponentEffect { double radians; double speed; Curve curve; - - double _originalAngle; - double _peakAngle; - double _direction; + double _startAngle; + double _delta; RotateEffect({ @required this.radians, // As many radians as you want to rotate @@ -18,8 +16,14 @@ class RotateEffect extends PositionComponentEffect { this.curve, isInfinite = false, isAlternating = false, + isRelative = false, Function onComplete, - }) : super(isInfinite, isAlternating, onComplete: onComplete); + }) : super( + isInfinite, + isAlternating, + isRelative: isRelative, + onComplete: onComplete, + ); @override void initialize(_comp) { @@ -27,16 +31,15 @@ class RotateEffect extends PositionComponentEffect { if (!isAlternating) { endAngle = _comp.angle + radians; } - _originalAngle = component.angle; - _peakAngle = _originalAngle + radians; - _direction = _peakAngle.sign; - travelTime = (radians / speed).abs(); + _startAngle = component.angle; + _delta = isRelative ? radians : _startAngle + radians; + travelTime = (_delta / speed).abs(); } @override void update(double dt) { super.update(dt); - final double c = curve?.transform(percentage) ?? 1.0; - component.angle = _originalAngle + _peakAngle * c * _direction; + final double progress = curve?.transform(percentage) ?? 1.0; + component.angle = _startAngle + _delta * progress; } } diff --git a/lib/effects/scale_effect.dart b/lib/effects/scale_effect.dart index 4d8c75342..93a6b8936 100644 --- a/lib/effects/scale_effect.dart +++ b/lib/effects/scale_effect.dart @@ -4,17 +4,12 @@ import 'package:meta/meta.dart'; import '../extensions/vector2.dart'; import 'effects.dart'; -double _direction(double p, double d) => (p - d).sign; -double _length(double a, double b) => (a - b).abs(); - class ScaleEffect extends PositionComponentEffect { Vector2 size; double speed; Curve curve; - - Vector2 _original; - Vector2 _diff; - final Vector2 _dir = Vector2.zero(); + Vector2 _startSize; + Vector2 _delta; ScaleEffect({ @required this.size, @@ -22,8 +17,14 @@ class ScaleEffect extends PositionComponentEffect { this.curve, isInfinite = false, isAlternating = false, + isRelative = false, Function onComplete, - }) : super(isInfinite, isAlternating, onComplete: onComplete); + }) : super( + isInfinite, + isAlternating, + isRelative: isRelative, + onComplete: onComplete, + ); @override void initialize(_comp) { @@ -31,24 +32,15 @@ class ScaleEffect extends PositionComponentEffect { if (!isAlternating) { endSize = size.clone(); } - - _original = component.toSize(); - _diff = Vector2( - _length(_original.x, size.x), - _length(_original.y, size.y), - ); - - _dir.x = _direction(size.x, _original.x); - _dir.y = _direction(size.y, _original.y); - - final scaleDistance = _diff.length; - travelTime = scaleDistance / speed; + _startSize = component.size; + _delta = isRelative ? size : size - _startSize; + travelTime = _delta.length / speed; } @override void update(double dt) { super.update(dt); - final double c = curve?.transform(percentage) ?? 1.0; - component.setBySize(_original + (_diff.clone()..multiply(_dir)) * c); + final double progress = curve?.transform(percentage) ?? 1.0; + component.size = _startSize + _delta * progress; } } diff --git a/lib/effects/sequence_effect.dart b/lib/effects/sequence_effect.dart index 3fb9ea1e1..ce7dd306f 100644 --- a/lib/effects/sequence_effect.dart +++ b/lib/effects/sequence_effect.dart @@ -26,13 +26,13 @@ class SequenceEffect extends PositionComponentEffect { void initialize(PositionComponent _comp) { super.initialize(_comp); _currentIndex = 0; - final originalSize = _comp.toSize(); + final originalSize = _comp.size; final originalPosition = _comp.position; 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; diff --git a/lib/extensions/rect.dart b/lib/extensions/rect.dart index d25f9d4ca..5cdcdea1b 100644 --- a/lib/extensions/rect.dart +++ b/lib/extensions/rect.dart @@ -16,11 +16,6 @@ extension RectExtension on Rect { // Until [extension] will allow static methods we need to keep these functions // in a utility class class RectFactory { - /// Creates a [Rect] with the size of [Size] - static Rect fromSize(Size size) { - return Rect.fromLTWH(0, 0, size.width, size.height); - } - /// Creates bounds in from of a [Rect] from a list of [Vector2] static Rect fromBounds(List pts) { final double minx = pts.map((e) => e.x).reduce(min); diff --git a/lib/extensions/vector2.dart b/lib/extensions/vector2.dart index 85085db92..38dbb1b78 100644 --- a/lib/extensions/vector2.dart +++ b/lib/extensions/vector2.dart @@ -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 origin and going the [Vector2] + Rect toRect() => Rect.fromLTWH(0, 0, x, y); /// Linearly interpolate towards another Vector2 void lerp(Vector2 to, double t) { @@ -43,4 +44,7 @@ extension Vector2Extension on Vector2 { scale(newLength.abs() / l); } } + + /// Create a Vector2 with ints as input + static Vector2 fromInts(int x, int y) => Vector2(x.toDouble(), y.toDouble()); } diff --git a/lib/flare_animation.dart b/lib/flare_animation.dart index 27f16c372..b44d06786 100644 --- a/lib/flare_animation.dart +++ b/lib/flare_animation.dart @@ -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 _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(); diff --git a/lib/sprite.dart b/lib/sprite.dart index 96f551db4..c84dfb65c 100644 --- a/lib/sprite.dart +++ b/lib/sprite.dart @@ -10,14 +10,11 @@ class Sprite { Sprite( this.image, { - double x = 0.0, - double y = 0.0, - double width, - double height, + Vector2 position, + Vector2 size, }) : assert(image != null, "image can't be null") { - width ??= image.width.toDouble(); - height ??= image.height.toDouble(); - src = Rect.fromLTWH(x, y, width, height); + size ??= Vector2(image.width.toDouble(), image.height.toDouble()); + src = position.toPositionedRect(size); } double get _imageWidth => image.width.toDouble(); @@ -49,7 +46,7 @@ class Sprite { Paint overridePaint, }) { size ??= this.size; - renderRect(canvas, p.toRect(size), overridePaint: overridePaint); + renderRect(canvas, p.toPositionedRect(size), overridePaint: overridePaint); } void render( diff --git a/lib/sprite_animation.dart b/lib/sprite_animation.dart index 58fa66018..45833b784 100644 --- a/lib/sprite_animation.dart +++ b/lib/sprite_animation.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:ui'; +import 'extensions/vector2.dart'; import 'flame.dart'; import 'sprite.dart'; @@ -76,10 +77,8 @@ class SpriteAnimation { Image image, int amount, { int amountPerRow, - double textureX = 0.0, - double textureY = 0.0, - double textureWidth, - double textureHeight, + Vector2 texturePosition, + Vector2 textureSize, double stepTime = 0.1, bool loop = true, }) : this.variableSequenced( @@ -87,10 +86,8 @@ class SpriteAnimation { amount, List.generate(amount, (_) => stepTime), amountPerRow: amountPerRow, - textureX: textureX, - textureY: textureX, - textureWidth: textureWidth, - textureHeight: textureHeight, + texturePosition: texturePosition, + textureSize: textureSize, loop: loop, ); @@ -100,21 +97,21 @@ class SpriteAnimation { int amount, List 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(amount); - for (var i = 0; i < amount; i++) { + for (int i = 0; i < amount; i++) { + final position = Vector2( + texturePosition.x + (i % amountPerRow) * textureSize.x, + texturePosition.y + (i ~/ amountPerRow) * textureSize.y, + ); final Sprite sprite = Sprite( image, - x: textureX + (i % amountPerRow) * textureWidth, - y: textureY + (i ~/ amountPerRow) * textureHeight, - width: textureWidth, - height: textureHeight, + position: position, + size: textureSize, ); frames[i] = SpriteAnimationFrame(sprite, stepTimes[i]); } @@ -145,10 +142,8 @@ class SpriteAnimation { final Sprite sprite = Sprite( image, - x: x.toDouble(), - y: y.toDouble(), - width: width.toDouble(), - height: height.toDouble(), + position: Vector2Extension.fromInts(x, y), + size: Vector2Extension.fromInts(width, height), ); return SpriteAnimationFrame(sprite, stepTime); diff --git a/lib/spritesheet.dart b/lib/spritesheet.dart index 8e8a8684c..2369cf198 100644 --- a/lib/spritesheet.dart +++ b/lib/spritesheet.dart @@ -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, ) { + final size = Vector2(textureWidth.toDouble(), textureHeight.toDouble()); return Sprite( image, - x: (x * textureWidth).toDouble(), - y: (y * textureHeight).toDouble(), - width: textureWidth.toDouble(), - height: textureHeight.toDouble(), + position: Vector2(x.toDouble(), y.toDouble())..multiply(size), + size: size, ); } diff --git a/lib/widgets/nine_tile_box.dart b/lib/widgets/nine_tile_box.dart index ba484e672..b73834d0b 100644 --- a/lib/widgets/nine_tile_box.dart +++ b/lib/widgets/nine_tile_box.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart' as widgets; import 'package:meta/meta.dart'; +import '../extensions/vector2.dart'; import '../sprite.dart'; class _Painter extends widgets.CustomPainter { @@ -18,7 +19,7 @@ class _Painter extends widgets.CustomPainter { }); Sprite _getSpriteTile(double x, double y) => - Sprite(image, x: x, y: y, width: tileSize, height: tileSize); + Sprite(image, position: Vector2(x, y), size: Vector2.all(tileSize)); @override void paint(Canvas canvas, Size size) { diff --git a/test/components/component_test.dart b/test/components/component_test.dart index bf003f09f..e209fbf90 100644 --- a/test/components/component_test.dart +++ b/test/components/component_test.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flame/anchor.dart'; import 'package:flame/components/position_component.dart'; import 'package:flame/components/sprite_component.dart'; import 'package:flame/extensions/vector2.dart'; @@ -9,38 +10,35 @@ 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 widt/height or size', () { + test('test get/set width/height or size', () { final PositionComponent c = SpriteComponent(); - c.width = 2.2; - c.height = 3.4; - expect(c.toSize().x, 2.2); - expect(c.toSize().y, 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; - expect(c.toRect().left, 0.0); - expect(c.toRect().top, 1.0); - expect(c.toRect().width, 2.0); - expect(c.toRect().height, 2.0); + c.position = Vector2(0.0, 1.0); + c.size = Vector2(2.0, 2.0); + final rect = c.toRect(); + expect(rect.left, 0.0); + expect(rect.top, 1.0); + expect(rect.width, 2.0); + expect(rect.height, 2.0); c.setByRect(const Rect.fromLTWH(10.0, 10.0, 1.0, 1.0)); expect(c.x, 10.0); @@ -48,5 +46,33 @@ void main() { expect(c.width, 1.0); expect(c.height, 1.0); }); + + test('test get/set rect with anchor', () { + final PositionComponent c = SpriteComponent(); + c.position = Vector2(0.0, 1.0); + c.size = Vector2(2.0, 2.0); + c.anchor = Anchor.center; + final rect = c.toRect(); + expect(rect.left, -1.0); + expect(rect.top, 0.0); + expect(rect.width, 2.0); + expect(rect.height, 2.0); + + c.setByRect(const Rect.fromLTWH(10.0, 10.0, 1.0, 1.0)); + expect(c.x, 10.5); + expect(c.y, 10.5); + expect(c.width, 1.0); + expect(c.height, 1.0); + }); + + test('test get/set anchorPosition', () { + final PositionComponent c = SpriteComponent(); + c.position = Vector2(0.0, 1.0); + c.size = Vector2(2.0, 2.0); + c.anchor = Anchor.center; + final anchorPosition = c.topLeftPosition; + expect(anchorPosition.x, -1.0); + expect(anchorPosition.y, 0.0); + }); }); }