mirror of
				https://github.com/flame-engine/flame.git
				synced 2025-10-31 08:56:01 +08:00 
			
		
		
		
	Follow a Vector2 in Camera and add onPositionUpdate (#716)
* No setter for position and size * Use setter for position and size * Add onPositionUpdate and snapTo to Camera * Fix formatting * Fix size in test * Update packages/flame/CHANGELOG.md Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net> * Update packages/flame/CHANGELOG.md Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net> * Better naming for internal position state * Anchor and angle defaults on effect test utils * No setter for position and size * Fix scale effect * Fix formatting Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net> Co-authored-by: Erick Zanardo <erickzanardoo@gmail.com>
This commit is contained in:
		| @ -6,9 +6,7 @@ import 'package:flame/palette.dart'; | ||||
| class SquareComponent extends PositionComponent { | ||||
|   Paint paint = BasicPalette.white.paint(); | ||||
|  | ||||
|   SquareComponent() { | ||||
|     size = Vector2.all(100.0); | ||||
|   } | ||||
|   SquareComponent() : super(size: Vector2.all(100.0)); | ||||
|  | ||||
|   @override | ||||
|   void render(Canvas c) { | ||||
|  | ||||
| @ -59,8 +59,7 @@ class BasicAnimations extends BaseGame with TapDetector { | ||||
|       removeOnFinish: true, | ||||
|     ); | ||||
|  | ||||
|     animationComponent.position = position; | ||||
|     animationComponent.position = animationComponent.position - size / 2; | ||||
|     animationComponent.position = position - size / 2; | ||||
|     add(animationComponent); | ||||
|   } | ||||
|  | ||||
|  | ||||
| @ -42,7 +42,7 @@ class DraggableSquare extends PositionComponent | ||||
|     final localCoords = gameRef!.convertGlobalToLocalCoordinate( | ||||
|       details.globalPosition.toVector2(), | ||||
|     ); | ||||
|     position = localCoords - dragDeltaPosition; | ||||
|     position.setFrom(localCoords - dragDeltaPosition); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|  | ||||
| @ -16,11 +16,11 @@ class CombinedEffectGame extends BaseGame with TapDetector { | ||||
|   Future<void> onLoad() async { | ||||
|     greenSquare = SquareComponent() | ||||
|       ..paint = green | ||||
|       ..position = Vector2.all(100); | ||||
|       ..position.setValues(100, 100); | ||||
|  | ||||
|     redSquare = SquareComponent() | ||||
|       ..paint = red | ||||
|       ..position = Vector2.all(100); | ||||
|       ..position.setValues(100, 100); | ||||
|  | ||||
|     add(greenSquare); | ||||
|     add(redSquare); | ||||
|  | ||||
| @ -15,7 +15,7 @@ final orange = Paint()..color = const Color(0xAABB6633); | ||||
| SquareComponent makeSquare(Paint paint) { | ||||
|   return SquareComponent() | ||||
|     ..paint = paint | ||||
|     ..position = Vector2.all(100); | ||||
|     ..position.setValues(100, 100); | ||||
| } | ||||
|  | ||||
| class InfiniteEffectGame extends BaseGame with TapDetector { | ||||
|  | ||||
| @ -11,9 +11,8 @@ class MoveEffectGame extends BaseGame with TapDetector { | ||||
|  | ||||
|   @override | ||||
|   Future<void> onLoad() async { | ||||
|     add( | ||||
|       square = SquareComponent()..position = Vector2.all(100), | ||||
|     ); | ||||
|     square = SquareComponent()..position.setValues(100, 100); | ||||
|     add(square); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|  | ||||
| @ -2,7 +2,6 @@ import 'dart:math'; | ||||
|  | ||||
| import 'package:flame/components.dart'; | ||||
| import 'package:flame/effects.dart'; | ||||
| import 'package:flame/extensions.dart'; | ||||
| import 'package:flame/game.dart'; | ||||
| import 'package:flame/gestures.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| @ -14,11 +13,10 @@ class RotateEffectGame extends BaseGame with TapDetector { | ||||
|  | ||||
|   @override | ||||
|   Future<void> onLoad() async { | ||||
|     add( | ||||
|     square = SquareComponent() | ||||
|         ..position = Vector2.all(200) | ||||
|         ..anchor = Anchor.center, | ||||
|     ); | ||||
|       ..position.setValues(200, 200) | ||||
|       ..anchor = Anchor.center; | ||||
|     add(square); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|  | ||||
| @ -13,11 +13,10 @@ class ScaleEffectGame extends BaseGame with TapDetector { | ||||
|  | ||||
|   @override | ||||
|   Future<void> onLoad() async { | ||||
|     add( | ||||
|     square = SquareComponent() | ||||
|         ..position = Vector2.all(200) | ||||
|         ..anchor = Anchor.center, | ||||
|     ); | ||||
|       ..position.setValues(200, 200) | ||||
|       ..anchor = Anchor.center; | ||||
|     add(square); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|  | ||||
| @ -13,11 +13,10 @@ class SequenceEffectGame extends BaseGame with TapDetector { | ||||
|  | ||||
|   @override | ||||
|   Future<void> onLoad() async { | ||||
|     add( | ||||
|     greenSquare = SquareComponent() | ||||
|       ..paint = green | ||||
|         ..position = Vector2.all(100), | ||||
|     ); | ||||
|       ..position.setValues(100, 100); | ||||
|     add(greenSquare); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|  | ||||
| @ -52,13 +52,13 @@ class TextGame extends BaseGame { | ||||
|     add( | ||||
|       TextComponent('center', config: _tiny) | ||||
|         ..anchor = Anchor.center | ||||
|         ..position = size / 2, | ||||
|         ..position.setFrom(size / 2), | ||||
|     ); | ||||
|  | ||||
|     add( | ||||
|       TextComponent('bottomRight', config: _tiny) | ||||
|         ..anchor = Anchor.bottomRight | ||||
|         ..position = size, | ||||
|         ..position.setFrom(size), | ||||
|     ); | ||||
|  | ||||
|     add( | ||||
|  | ||||
| @ -76,6 +76,6 @@ class IsometricTileMapGame extends BaseGame with MouseMovementDetector { | ||||
|     final screenPosition = event.localPosition.toVector2(); | ||||
|     final block = base.getBlock(screenPosition); | ||||
|     selector.show = base.containsBlock(block); | ||||
|     selector.position = base.getBlockPosition(block) + topLeft; | ||||
|     selector.position.setFrom(base.getBlockPosition(block) + topLeft); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -34,7 +34,7 @@ class MovableSquare extends SquareComponent | ||||
|     timer.update(dt); | ||||
|  | ||||
|     final ds = velocity * (speed * dt); | ||||
|     position += ds; | ||||
|     position.add(ds); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
| @ -83,8 +83,8 @@ class Map extends Component { | ||||
|  | ||||
| class Rock extends SquareComponent with Hitbox, Collidable { | ||||
|   Rock(Vector2 position) { | ||||
|     this.position = position; | ||||
|     size = Vector2.all(50); | ||||
|     this.position.setFrom(position); | ||||
|     size.setValues(50, 50); | ||||
|     paint = Paint()..color = const Color(0xFF2222FF); | ||||
|     addShape(HitboxRectangle()); | ||||
|   } | ||||
| @ -104,7 +104,7 @@ class CameraAndViewportGame extends BaseGame | ||||
|  | ||||
|     add(square = MovableSquare()); | ||||
|     camera.cameraSpeed = 1; | ||||
|     camera.followObject(square, worldBounds: Map.bounds); | ||||
|     camera.followComponent(square, worldBounds: Map.bounds); | ||||
|  | ||||
|     for (var i = 0; i < 30; i++) { | ||||
|       add(Rock(Vector2(Map.genCoord(), Map.genCoord()))); | ||||
|  | ||||
| @ -10,6 +10,8 @@ | ||||
|  - Adding some more basic colors entries to the `BasicPalette` | ||||
|  - Fixing Flutter and Dart version constraints | ||||
|  - Exporting Images and AssetsCache | ||||
|  - Make `size` and `position` in `PositionComponent` final | ||||
|  - Add a `snapTo` and `onPositionUpdate` method to the `Camera` | ||||
|  - Remove the SpriteAnimationComponent when the animation is really done, not when it is on the last frame | ||||
|  - Revamp all the docs to be up to date with v1.0.0 | ||||
|  | ||||
|  | ||||
| @ -42,7 +42,7 @@ class Square extends PositionComponent { | ||||
|   @override | ||||
|   void onMount() { | ||||
|     super.onMount(); | ||||
|     size = Vector2.all(squareSize); | ||||
|     size.setValues(squareSize, squareSize); | ||||
|     anchor = Anchor.center; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -64,7 +64,7 @@ class ParallaxComponent extends PositionComponent { | ||||
|   @override | ||||
|   void onGameResize(Vector2 size) { | ||||
|     super.onGameResize(size); | ||||
|     this.size = size; | ||||
|     this.size.setFrom(size); | ||||
|     parallax?.resize(size); | ||||
|   } | ||||
|  | ||||
|  | ||||
| @ -23,19 +23,23 @@ import 'mixins/hitbox.dart'; | ||||
| /// within this component's (width, height). | ||||
| abstract class PositionComponent extends BaseComponent { | ||||
|   /// The position of this component on the screen (relative to the anchor). | ||||
|   Vector2 position; | ||||
|   final Vector2 _position; | ||||
|   Vector2 get position => _position; | ||||
|   set position(Vector2 position) => _position.setFrom(position); | ||||
|  | ||||
|   /// X position of this component on the screen (relative to the anchor). | ||||
|   double get x => position.x; | ||||
|   set x(double x) => position.x = x; | ||||
|   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; | ||||
|   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; | ||||
|   final Vector2 _size; | ||||
|   Vector2 get size => _size; | ||||
|   set size(Vector2 size) => _size.setFrom(size); | ||||
|  | ||||
|   /// Width (size) that this component is rendered with. | ||||
|   double get width => size.x; | ||||
| @ -132,8 +136,8 @@ abstract class PositionComponent extends BaseComponent { | ||||
|     this.anchor = Anchor.topLeft, | ||||
|     this.renderFlipX = false, | ||||
|     this.renderFlipY = false, | ||||
|   })  : position = position ?? Vector2.zero(), | ||||
|         size = size ?? Vector2.zero(); | ||||
|   })  : _position = position ?? Vector2.zero(), | ||||
|         _size = size ?? Vector2.zero(); | ||||
|  | ||||
|   @override | ||||
|   bool containsPoint(Vector2 point) { | ||||
|  | ||||
| @ -126,7 +126,7 @@ class MoveEffect extends SimplePositionComponentEffect { | ||||
|     final lastEndAt = _currentSubPath!.startAt; | ||||
|     final localPercentage = | ||||
|         (curveProgress - lastEndAt) / (_currentSubPath!.endAt - lastEndAt); | ||||
|     component?.position = _currentSubPath!.previous + | ||||
|         ((_currentSubPath!.v - _currentSubPath!.previous) * localPercentage); | ||||
|     component?.position.setFrom(_currentSubPath!.previous + | ||||
|         ((_currentSubPath!.v - _currentSubPath!.previous) * localPercentage)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -39,7 +39,7 @@ class ScaleEffect extends SimplePositionComponentEffect { | ||||
|   @override | ||||
|   void initialize(PositionComponent component) { | ||||
|     super.initialize(component); | ||||
|     _startSize = component.size; | ||||
|     _startSize = component.size.clone(); | ||||
|     _delta = isRelative ? size : size - _startSize; | ||||
|     if (!isAlternating) { | ||||
|       endSize = _startSize + _delta; | ||||
| @ -52,6 +52,6 @@ class ScaleEffect extends SimplePositionComponentEffect { | ||||
|   @override | ||||
|   void update(double dt) { | ||||
|     super.update(dt); | ||||
|     component?.size = _startSize + _delta * curveProgress; | ||||
|     component?.size.setFrom(_startSize + _delta * curveProgress); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -35,7 +35,7 @@ void _moveToTarget( | ||||
| /// There are three major factors that determine the camera position: | ||||
| /// | ||||
| /// * Follow | ||||
| /// If you want, you can call [followObject] at the beginning of your | ||||
| /// If you want, you can call [followComponent] at the beginning of your | ||||
| /// stage/world/level, and provided a [PositionComponent]. | ||||
| /// The camera will follow this component making sure its position is fixed | ||||
| /// on the screen. | ||||
| @ -89,29 +89,36 @@ class Camera { | ||||
|   double cameraSpeed = defaultCameraSpeed; | ||||
|   double shakeIntensity = defaultShakeIntensity; | ||||
|  | ||||
|   /// This is the current position of the camera, ie the world coordinate that is | ||||
|   /// rendered on the top left of the screen (origin of the screen space). | ||||
|   /// This is the current position of the camera, ie the world coordinate that | ||||
|   /// is rendered on the top left of the screen (origin of the screen space). | ||||
|   /// | ||||
|   /// Zero means no translation is applied. | ||||
|   /// You can't change this directly; the camera will handle all ongoing | ||||
|   /// movements so they smoothly transition. | ||||
|   /// If you want to immediately snap the camera to a new place, you can do: | ||||
|   /// ``` | ||||
|   ///   camera.moveTo(newPosition); | ||||
|   ///   camera.snap(); | ||||
|   ///   camera.snapTo(newPosition); | ||||
|   /// ``` | ||||
|   Vector2 get position => _position.clone(); | ||||
|   Vector2 get position => _internalPosition.clone(); | ||||
|  | ||||
|   final Vector2 _position = Vector2.zero(); | ||||
|   /// Do not change this directly since it bypasses [onPositionUpdate] | ||||
|   final Vector2 _internalPosition = Vector2.zero(); | ||||
|  | ||||
|   /// If set, the camera will "follow" this component, making sure that this | ||||
|   /// component is always rendered in a fixed position in the screen, by | ||||
|   /// immediately moving the camera to "focus" on the object. | ||||
|   Vector2 get _position => _internalPosition; | ||||
|   set _position(Vector2 position) { | ||||
|     _internalPosition.setFrom(position); | ||||
|     onPositionUpdate(_internalPosition); | ||||
|   } | ||||
|  | ||||
|   /// If set, the camera will "follow" this vector, making sure that this | ||||
|   /// vector is always rendered in a fixed position in the screen, by | ||||
|   /// immediately moving the camera to "focus" on the where the vector is. | ||||
|   /// | ||||
|   /// You probably want to set it to the player component. | ||||
|   /// Note that this is not smooth because the movement of the follow object | ||||
|   /// You might want to set it to the player component by using the | ||||
|   /// [followComponent] method. | ||||
|   /// Note that this is not smooth because the movement of the followed vector | ||||
|   /// is assumed to be smooth. | ||||
|   PositionComponent? follow; | ||||
|   Vector2? follow; | ||||
|  | ||||
|   /// Where in the screen the follow object should be. | ||||
|   /// | ||||
| @ -146,10 +153,10 @@ class Camera { | ||||
|  | ||||
|     if (_targetCameraDelta != null && _currentCameraDelta != null) { | ||||
|       _moveToTarget(_currentCameraDelta!, _targetCameraDelta!, ds); | ||||
|       _position.setFrom(_currentCameraDelta! + shake); | ||||
|       _position = _currentCameraDelta! + shake; | ||||
|     } else { | ||||
|       _moveToTarget(_currentRelativeOffset, _targetRelativeOffset, ds); | ||||
|       _position.setFrom(_getTarget() + shake); | ||||
|       _position = _target() + shake; | ||||
|     } | ||||
|  | ||||
|     if (shaking) { | ||||
| @ -182,23 +189,23 @@ class Camera { | ||||
|  | ||||
|   // Follow | ||||
|  | ||||
|   /// Immediately snaps the camera to start following the object [follow]. | ||||
|   /// Immediately snaps the camera to start following the [component]. | ||||
|   /// | ||||
|   /// This means that the camera will move so that the [follow] object is | ||||
|   /// in a fixed position on the screen. | ||||
|   /// This means that the camera will move so that the position vector of the | ||||
|   /// component is in a fixed position on the screen. | ||||
|   /// That position is determined by a fraction of screen size defined by | ||||
|   /// [relativeOffset] (default to the center). | ||||
|   /// [worldBounds] can be optionally set to add boundaries to how far the | ||||
|   /// camera is allowed to move. | ||||
|   /// The object is "grabbed" by its anchor (default top left). So for example | ||||
|   /// if you want the center of the object to be at the fixed position, set | ||||
|   /// its anchor to center. | ||||
|   void followObject( | ||||
|     PositionComponent follow, { | ||||
|   /// The component is "grabbed" by its anchor (default top left). | ||||
|   /// So for example if you want the center of the object to be at the fixed | ||||
|   /// position, set the components anchor to center. | ||||
|   void followComponent( | ||||
|     PositionComponent component, { | ||||
|     Vector2? relativeOffset, | ||||
|     Rect? worldBounds, | ||||
|   }) { | ||||
|     this.follow = follow; | ||||
|     follow = component.position; | ||||
|     this.worldBounds = worldBounds; | ||||
|     _targetRelativeOffset.setFrom(relativeOffset ?? Anchor.center.toVector2()); | ||||
|     _currentRelativeOffset.setFrom(_targetRelativeOffset); | ||||
| @ -214,12 +221,12 @@ class Camera { | ||||
|     _targetRelativeOffset.setFrom(newRelativeOffset); | ||||
|   } | ||||
|  | ||||
|   Vector2 _getTarget() { | ||||
|   Vector2 _target() { | ||||
|     if (follow == null) { | ||||
|       return Vector2.zero(); | ||||
|     } | ||||
|     final screenDelta = gameRef.size.clone()..multiply(_currentRelativeOffset); | ||||
|     final attemptedTarget = follow!.position - screenDelta; | ||||
|     final attemptedTarget = follow! - screenDelta; | ||||
|  | ||||
|     final bounds = worldBounds; | ||||
|     if (bounds != null) { | ||||
| @ -259,9 +266,16 @@ class Camera { | ||||
|   /// | ||||
|   /// The camera will be smoothly transitioned to this position. | ||||
|   /// This will replace any previous targets. | ||||
|   void moveTo(Vector2 p) { | ||||
|   void moveTo(Vector2 position) { | ||||
|     _currentCameraDelta = _position; | ||||
|     _targetCameraDelta = p.clone(); | ||||
|     _targetCameraDelta = position.clone(); | ||||
|   } | ||||
|  | ||||
|   /// Instantly moves the camera to the target, bypassing follow. | ||||
|   /// This will replace any previous targets. | ||||
|   void snapTo(Vector2 position) { | ||||
|     moveTo(position); | ||||
|     snap(); | ||||
|   } | ||||
|  | ||||
|   /// Smoothly resets any moveTo targets. | ||||
| @ -290,4 +304,8 @@ class Camera { | ||||
|     } | ||||
|     return 0.0; | ||||
|   } | ||||
|  | ||||
|   /// If you need updated on when the position of the camera is updated you | ||||
|   /// can override this. | ||||
|   void onPositionUpdate(Vector2 position) {} | ||||
| } | ||||
|  | ||||
| @ -7,30 +7,30 @@ void main() { | ||||
|   group('component test', () { | ||||
|     test('test get/set x/y or position', () { | ||||
|       final PositionComponent c = SpriteComponent(); | ||||
|       c.position = Vector2(2.2, 3.4); | ||||
|       c.position.setValues(2.2, 3.4); | ||||
|       expect(c.x, 2.2); | ||||
|       expect(c.y, 3.4); | ||||
|  | ||||
|       c.position = Vector2(1.0, 0.0); | ||||
|       c.position.setValues(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.size = Vector2(2.2, 3.4); | ||||
|       c.size.setValues(2.2, 3.4); | ||||
|       expect(c.size.x, 2.2); | ||||
|       expect(c.size.y, 3.4); | ||||
|  | ||||
|       c.size = Vector2(1.0, 0.0); | ||||
|       c.size.setValues(1.0, 0.0); | ||||
|       expect(c.width, 1.0); | ||||
|       expect(c.height, 0.0); | ||||
|     }); | ||||
|  | ||||
|     test('test get/set rect', () { | ||||
|       final PositionComponent c = SpriteComponent(); | ||||
|       c.position = Vector2(0.0, 1.0); | ||||
|       c.size = Vector2(2.0, 2.0); | ||||
|       c.position.setValues(0.0, 1.0); | ||||
|       c.size.setValues(2.0, 2.0); | ||||
|       final rect = c.toRect(); | ||||
|       expect(rect.left, 0.0); | ||||
|       expect(rect.top, 1.0); | ||||
| @ -46,8 +46,8 @@ void main() { | ||||
|  | ||||
|     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.position.setValues(0.0, 1.0); | ||||
|       c.size.setValues(2.0, 2.0); | ||||
|       c.anchor = Anchor.center; | ||||
|       final rect = c.toRect(); | ||||
|       expect(rect.left, -1.0); | ||||
| @ -64,8 +64,8 @@ void main() { | ||||
|  | ||||
|     test('test get/set anchorPosition', () { | ||||
|       final PositionComponent c = SpriteComponent(); | ||||
|       c.position = Vector2(0.0, 1.0); | ||||
|       c.size = Vector2(2.0, 2.0); | ||||
|       c.position.setValues(0.0, 1.0); | ||||
|       c.size.setValues(2.0, 2.0); | ||||
|       c.anchor = Anchor.center; | ||||
|       final anchorPosition = c.topLeftPosition; | ||||
|       expect(anchorPosition.x, -1.0); | ||||
|  | ||||
| @ -88,7 +88,7 @@ void main() { | ||||
|       final wrapper = MyComposed(); | ||||
|  | ||||
|       game.onResize(size); | ||||
|       child.size = Vector2.all(1); | ||||
|       child.size.setValues(1.0, 1.0); | ||||
|       game.add(wrapper); | ||||
|       wrapper.addChild(child); | ||||
|       game.update(0.0); | ||||
| @ -101,11 +101,11 @@ void main() { | ||||
|     test('tap on offset children', () { | ||||
|       final game = MyGame(); | ||||
|       final child = MyTap() | ||||
|         ..position = Vector2.all(100) | ||||
|         ..size = Vector2.all(100); | ||||
|         ..position.setFrom(Vector2.all(100)) | ||||
|         ..size.setFrom(Vector2.all(100)); | ||||
|       final wrapper = MyComposed() | ||||
|         ..position = Vector2.all(100) | ||||
|         ..size = Vector2.all(300); | ||||
|         ..position.setFrom(Vector2.all(100)) | ||||
|         ..size.setFrom(Vector2.all(300)); | ||||
|  | ||||
|       game.onResize(size); | ||||
|       game.add(wrapper); | ||||
|  | ||||
| @ -12,8 +12,8 @@ void main() { | ||||
|   group('PositionComponent overlap test', () { | ||||
|     test('overlap', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(4.0, 4.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(4.0, 4.0); | ||||
|       component.angle = 0.0; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -23,8 +23,8 @@ void main() { | ||||
|  | ||||
|     test('overlap on edge', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = 0.0; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -34,8 +34,8 @@ void main() { | ||||
|  | ||||
|     test('not overlapping with x', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = 0.0; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -45,8 +45,8 @@ void main() { | ||||
|  | ||||
|     test('not overlapping with y', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = 0.0; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -56,8 +56,8 @@ void main() { | ||||
|  | ||||
|     test('overlapping with angle', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = math.pi / 4; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -67,8 +67,8 @@ void main() { | ||||
|  | ||||
|     test('not overlapping with angle', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = math.pi / 4; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
| @ -78,8 +78,8 @@ void main() { | ||||
|  | ||||
|     test('overlapping with angle and topLeft anchor', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(1.0, 1.0); | ||||
|       component.size = Vector2(2.0, 2.0); | ||||
|       component.position.setValues(1.0, 1.0); | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.angle = math.pi / 4; | ||||
|       component.anchor = Anchor.topLeft; | ||||
|  | ||||
| @ -88,11 +88,10 @@ void main() { | ||||
|     }); | ||||
|  | ||||
|     test('component with hitbox contains point', () { | ||||
|       final size = Vector2(2.0, 2.0); | ||||
|       final Hitbox component = MyHitboxComponent(); | ||||
|       component.position = Vector2(1.0, 1.0); | ||||
|       component.position.setValues(1.0, 1.0); | ||||
|       component.anchor = Anchor.topLeft; | ||||
|       component.size = size; | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       final hitbox = HitboxPolygon([ | ||||
|         Vector2(1, 0), | ||||
|         Vector2(0, -1), | ||||
| @ -106,11 +105,10 @@ void main() { | ||||
|     }); | ||||
|  | ||||
|     test('component with anchor topLeft contains point on edge', () { | ||||
|       final size = Vector2(2.0, 2.0); | ||||
|       final Hitbox component = MyHitboxComponent(); | ||||
|       component.position = Vector2(-1, -1); | ||||
|       component.position.setValues(-1, -1); | ||||
|       component.anchor = Anchor.topLeft; | ||||
|       component.size = size; | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       final hitbox = HitboxRectangle(); | ||||
|       component.addShape(hitbox); | ||||
|  | ||||
| @ -121,11 +119,10 @@ void main() { | ||||
|     }); | ||||
|  | ||||
|     test('component with anchor bottomRight contains point on edge', () { | ||||
|       final size = Vector2(2.0, 2.0); | ||||
|       final Hitbox component = MyHitboxComponent(); | ||||
|       component.position = Vector2(1, 1); | ||||
|       component.position.setValues(1, 1); | ||||
|       component.anchor = Anchor.bottomRight; | ||||
|       component.size = size; | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       final hitbox = HitboxRectangle(); | ||||
|       component.addShape(hitbox); | ||||
|  | ||||
| @ -136,11 +133,10 @@ void main() { | ||||
|     }); | ||||
|  | ||||
|     test('component with anchor topRight does not contain close points', () { | ||||
|       final size = Vector2(2.0, 2.0); | ||||
|       final Hitbox component = MyHitboxComponent(); | ||||
|       component.position = Vector2(1, 1); | ||||
|       component.position.setValues(1, 1); | ||||
|       component.anchor = Anchor.topLeft; | ||||
|       component.size = size; | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       final hitbox = HitboxRectangle(); | ||||
|       component.addShape(hitbox); | ||||
|  | ||||
| @ -151,11 +147,10 @@ void main() { | ||||
|     }); | ||||
|  | ||||
|     test('component with hitbox does not contains point', () { | ||||
|       final size = Vector2(2.0, 2.0); | ||||
|       final Hitbox component = MyHitboxComponent(); | ||||
|       component.position = Vector2(1.0, 1.0); | ||||
|       component.position.setValues(1.0, 1.0); | ||||
|       component.anchor = Anchor.topLeft; | ||||
|       component.size = size; | ||||
|       component.size.setValues(2.0, 2.0); | ||||
|       component.addShape(HitboxPolygon([ | ||||
|         Vector2(1, 0), | ||||
|         Vector2(0, -1), | ||||
| @ -169,8 +164,8 @@ void main() { | ||||
|  | ||||
|     test('component with zero size does not contain point', () { | ||||
|       final PositionComponent component = MyComponent(); | ||||
|       component.position = Vector2(2.0, 2.0); | ||||
|       component.size = Vector2(0.0, 0.0); | ||||
|       component.position.setValues(2.0, 2.0); | ||||
|       component.size.setValues(0.0, 0.0); | ||||
|       component.angle = 0.0; | ||||
|       component.anchor = Anchor.center; | ||||
|  | ||||
|  | ||||
| @ -5,7 +5,7 @@ import 'package:test/test.dart'; | ||||
| class MyComponent extends PositionComponent { | ||||
|   String name; | ||||
|   @override | ||||
|   Vector2 size = Vector2(2.0, 2.0); | ||||
|   final Vector2 size = Vector2(2.0, 2.0); | ||||
|   late Vector2 gameSize; | ||||
|  | ||||
|   MyComponent(this.name); | ||||
|  | ||||
| @ -100,12 +100,12 @@ class TestComponent extends PositionComponent { | ||||
|   TestComponent({ | ||||
|     Vector2? position, | ||||
|     Vector2? size, | ||||
|     double? angle, | ||||
|     Anchor? anchor, | ||||
|   }) { | ||||
|     this.position = position ?? Vector2.zero(); | ||||
|     this.size = size ?? Vector2.all(100.0); | ||||
|     this.angle = angle ?? 0.0; | ||||
|     this.anchor = anchor ?? Anchor.center; | ||||
|   } | ||||
|     double angle = 0.0, | ||||
|     Anchor anchor = Anchor.center, | ||||
|   }) : super( | ||||
|           position: position, | ||||
|           size: size ?? Vector2.all(100.0), | ||||
|           angle: angle, | ||||
|           anchor: anchor, | ||||
|         ); | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,7 @@ void main() { | ||||
|   final argumentSize = randomVector2(); | ||||
|   final argumentAngle = randomAngle(); | ||||
|   final path = List.generate(3, (i) => randomVector2()); | ||||
|  | ||||
|   TestComponent component() { | ||||
|     return TestComponent( | ||||
|       position: randomVector2(), | ||||
|  | ||||
| @ -11,10 +11,13 @@ import '../util/mock_canvas.dart'; | ||||
|  | ||||
| class TestComponent extends PositionComponent { | ||||
|   static final Paint _paint = Paint(); | ||||
|   TestComponent(Vector2 position) { | ||||
|     this.position = position; | ||||
|     size = Vector2.all(1.0); | ||||
|   } | ||||
|  | ||||
|   TestComponent(Vector2 position) | ||||
|       : super( | ||||
|           position: position, | ||||
|           size: Vector2.all(1.0), | ||||
|         ); | ||||
|  | ||||
|   @override | ||||
|   void render(Canvas c) { | ||||
|     super.render(c); | ||||
| @ -181,10 +184,10 @@ void main() { | ||||
|       final p = TestComponent(Vector2.all(10.0))..anchor = Anchor.center; | ||||
|       game.add(p); | ||||
|       game.update(0); | ||||
|       game.camera.followObject(p); | ||||
|       game.camera.followComponent(p); | ||||
|  | ||||
|       expect(game.camera.position, Vector2.all(0.0)); | ||||
|       p.position = Vector2(10.0, 20.0); | ||||
|       p.position.setValues(10.0, 20.0); | ||||
|       // follow happens immediately because the object's movement is assumed to be smooth | ||||
|       game.update(0); | ||||
|       // (10,20) - half screen (50,50) | ||||
| @ -210,10 +213,10 @@ void main() { | ||||
|       game.add(p); | ||||
|       game.update(0); | ||||
|       // this would be a typical vertical shoot-em-up | ||||
|       game.camera.followObject(p, relativeOffset: Vector2(0.5, 0.8)); | ||||
|       game.camera.followComponent(p, relativeOffset: Vector2(0.5, 0.8)); | ||||
|  | ||||
|       expect(game.camera.position, Vector2.all(0.0)); | ||||
|       p.position = Vector2(600.0, 2000.0); | ||||
|       p.position.setValues(600.0, 2000.0); | ||||
|       // follow happens immediately because the object's movement is assumed to be smooth | ||||
|       game.update(0); | ||||
|       // (600,2000) - fractional screen (50,80) | ||||
| @ -238,29 +241,29 @@ void main() { | ||||
|       final p = TestComponent(Vector2.all(10.0))..anchor = Anchor.center; | ||||
|       game.add(p); | ||||
|       game.update(0); | ||||
|       game.camera.followObject( | ||||
|       game.camera.followComponent( | ||||
|         p, | ||||
|         worldBounds: const Rect.fromLTWH(-1000, -1000, 2000, 2000), | ||||
|       ); | ||||
|  | ||||
|       p.position = Vector2(600.0, 700.0); // well within bounds | ||||
|       p.position.setValues(600.0, 700.0); // well within bounds | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(550, 650)); | ||||
|  | ||||
|       // x ok, y starts to get to bounds | ||||
|       p.position = Vector2(600.0, 950.0); // right on the edge | ||||
|       p.position.setValues(600.0, 950.0); // right on the edge | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(550, 900)); | ||||
|  | ||||
|       p.position = Vector2(600.0, 950.0); // stop advancing | ||||
|       p.position.setValues(600.0, 950.0); // stop advancing | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(550, 900)); | ||||
|  | ||||
|       p.position = Vector2(-1100.0, 950.0); | ||||
|       p.position.setValues(-1100.0, 950.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(-1000, 900)); | ||||
|  | ||||
|       p.position = Vector2(1000.0, 1000.0); | ||||
|       p.position.setValues(1000.0, 1000.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(900, 900)); | ||||
|     }); | ||||
| @ -271,7 +274,7 @@ void main() { | ||||
|       final p = TestComponent(Vector2.all(10.0))..anchor = Anchor.center; | ||||
|       game.add(p); | ||||
|       game.update(0); | ||||
|       game.camera.followObject( | ||||
|       game.camera.followComponent( | ||||
|         p, | ||||
|         worldBounds: const Rect.fromLTWH(0, 0, 100, 100), | ||||
|       ); | ||||
| @ -280,11 +283,11 @@ void main() { | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(50, 50)); | ||||
|  | ||||
|       p.position = Vector2(60.0, 50.0); | ||||
|       p.position.setValues(60.0, 50.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(50, 50)); | ||||
|  | ||||
|       p.position = Vector2(-10.0, -20.0); | ||||
|       p.position.setValues(-10.0, -20.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(50, 50)); | ||||
|     }); | ||||
| @ -299,7 +302,7 @@ void main() { | ||||
|  | ||||
|       final p = TestComponent(Vector2.all(10.0))..anchor = Anchor.center; | ||||
|       game.add(p); | ||||
|       game.camera.followObject( | ||||
|       game.camera.followComponent( | ||||
|         p, | ||||
|         // this could be a typical mario-like platformer, where the player is more on the bottom left to allow the scenario to be seem | ||||
|         relativeOffset: Vector2(0.25, 0.25), | ||||
| @ -309,19 +312,19 @@ void main() { | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(0, 0)); | ||||
|  | ||||
|       p.position = Vector2(30.0, 0.0); | ||||
|       p.position.setValues(30.0, 0.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(5, 0)); | ||||
|  | ||||
|       p.position = Vector2(30.0, 100.0); | ||||
|       p.position.setValues(30.0, 100.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(5, 75)); | ||||
|  | ||||
|       p.position = Vector2(30.0, 1000.0); | ||||
|       p.position.setValues(30.0, 1000.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(5, 900)); | ||||
|  | ||||
|       p.position = Vector2(950.0, 20.0); | ||||
|       p.position.setValues(950.0, 20.0); | ||||
|       game.update(0); | ||||
|       expect(game.camera.position, Vector2(900, 0)); | ||||
|     }); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Lukas Klingsbo
					Lukas Klingsbo