mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 19:12:31 +08:00
feat: Added componentsAtPoint() iterable (#1518)
This commit is contained in:
@ -190,6 +190,32 @@ void update(double dt) {
|
||||
```
|
||||
|
||||
|
||||
### Querying components at a specific point on the screen
|
||||
|
||||
The method `componentsAtPoint()` allows you to check which components have been rendered at a
|
||||
specific point on the screen. The returned value is an iterable which contains both the components
|
||||
and the coordinates of the query point in those components' local coordinates. The iterable
|
||||
retrieves the components in the front-to-back order, i.e. first the components in the front,
|
||||
followed by the components in the back.
|
||||
|
||||
This method can only return components that implement the method `containsLocalPoint()`. The
|
||||
`PositionComponent` (which is the base class for many components in Flame) provides such an
|
||||
implementation. However, if you're defining a custom class that derives from `Component`, you'd have
|
||||
to implement the `containsLocalPoint()` method yourself.
|
||||
|
||||
Here is an example of how `componentsAtPoint()` can be used:
|
||||
|
||||
```dart
|
||||
void onDragUpdate(DragUpdateInfo info) {
|
||||
game.componentsAtPoint(info.widget).forEach((p) {
|
||||
if (p.component is DropTarget) {
|
||||
p.component.highlight();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### PositionType
|
||||
If you want to create a HUD (Head-up display) or another component that isn't positioned in relation
|
||||
to the game coordinates, you can change the `PositionType` of the component.
|
||||
@ -370,7 +396,7 @@ use `animation.completed`.
|
||||
Example:
|
||||
|
||||
```dart
|
||||
await animation.completed;
|
||||
await animation.completed;
|
||||
doSomething();
|
||||
|
||||
// or alternatively
|
||||
|
||||
@ -3,8 +3,9 @@ import 'dart:ui';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/experimental.dart';
|
||||
import 'package:flame/game.dart' hide Viewport;
|
||||
import 'package:flame/input.dart';
|
||||
|
||||
class CameraComponentPropertiesExample extends FlameGame {
|
||||
class CameraComponentPropertiesExample extends FlameGame with HasTappables {
|
||||
static const description = '''
|
||||
This example uses FixedSizeViewport which is dynamically sized and
|
||||
positioned based on the size of the game widget.
|
||||
@ -13,6 +14,8 @@ class CameraComponentPropertiesExample extends FlameGame {
|
||||
green dot being the origin. The viewfinder uses custom anchor in order to
|
||||
declare its "center" half-way between the bottom left corner and the true
|
||||
center.
|
||||
|
||||
Click at any point within the viewport to create a circle there.
|
||||
''';
|
||||
|
||||
CameraComponent? _camera;
|
||||
@ -41,6 +44,19 @@ class CameraComponentPropertiesExample extends FlameGame {
|
||||
_camera?.viewport.size = size * 0.7;
|
||||
_camera?.viewport.position = size * 0.6;
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: must_call_super
|
||||
void onTapDown(int pointerId, TapDownInfo info) {
|
||||
final canvasPoint = info.eventPosition.widget;
|
||||
for (final cp in componentsAtPoint(canvasPoint)) {
|
||||
if (cp.component is Background) {
|
||||
cp.component.add(
|
||||
ExpandingCircle(cp.point.toOffset()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ViewportFrame extends Component {
|
||||
@ -64,7 +80,7 @@ class ViewportFrame extends Component {
|
||||
|
||||
class Background extends Component {
|
||||
final bgPaint = Paint()..color = const Color(0xffff0000);
|
||||
final originPaint = Paint()..color = const Color(0xff2f8750);
|
||||
final originPaint = Paint()..color = const Color(0xff19bf57);
|
||||
final axisPaint = Paint()
|
||||
..strokeWidth = 1
|
||||
..style = PaintingStyle.stroke
|
||||
@ -85,4 +101,33 @@ class Background extends Component {
|
||||
canvas.drawLine(Offset.zero, const Offset(10, 0), axisPaint);
|
||||
canvas.drawCircle(Offset.zero, 1.0, originPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) => true;
|
||||
}
|
||||
|
||||
class ExpandingCircle extends CircleComponent {
|
||||
ExpandingCircle(Offset center)
|
||||
: super(
|
||||
position: Vector2(center.dx, center.dy),
|
||||
anchor: Anchor.center,
|
||||
radius: 0,
|
||||
paint: Paint()
|
||||
..color = const Color(0xffffffff)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1,
|
||||
);
|
||||
|
||||
static const maxRadius = 50;
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
radius += dt * 10;
|
||||
if (radius >= maxRadius) {
|
||||
removeFromParent();
|
||||
} else {
|
||||
final opacity = 1 - radius / maxRadius;
|
||||
paint.color = const Color(0xffffffff).withOpacity(opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ export 'src/anchor.dart';
|
||||
export 'src/collisions/has_collision_detection.dart';
|
||||
export 'src/collisions/hitboxes/screen_hitbox.dart';
|
||||
export 'src/components/component.dart';
|
||||
export 'src/components/component_point_pair.dart';
|
||||
export 'src/components/component_set.dart';
|
||||
export 'src/components/custom_painter_component.dart';
|
||||
export 'src/components/input/joystick_component.dart';
|
||||
|
||||
@ -3,11 +3,16 @@ import 'dart:collection';
|
||||
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import '../../components.dart';
|
||||
import '../../game.dart';
|
||||
import '../../input.dart';
|
||||
import '../cache/value_cache.dart';
|
||||
import '../game/mixins/game.dart';
|
||||
import '../gestures/events.dart';
|
||||
import '../text.dart';
|
||||
import 'component_point_pair.dart';
|
||||
import 'component_set.dart';
|
||||
import 'mixins/coordinate_transform.dart';
|
||||
import 'position_type.dart';
|
||||
|
||||
/// [Component]s are the basic building blocks for your game.
|
||||
///
|
||||
@ -613,10 +618,56 @@ class Component {
|
||||
return (parent is T ? parent : parent?.findParent<T>()) as T?;
|
||||
}
|
||||
|
||||
/// Called to check whether the point is to be counted as within the component
|
||||
/// It needs to be overridden to have any effect, like it is in
|
||||
/// PositionComponent.
|
||||
bool containsPoint(Vector2 point) => false;
|
||||
/// Checks whether the [point] is within this component's bounds.
|
||||
///
|
||||
/// This method should be implemented for any component that has a visual
|
||||
/// representation and non-zero size. The [point] is in the local coordinate
|
||||
/// space.
|
||||
bool containsLocalPoint(Vector2 point) => false;
|
||||
|
||||
/// Same as [containsLocalPoint], but for a "global" [point].
|
||||
///
|
||||
/// This will be deprecated in the future, due to the notion of "global" point
|
||||
/// not being well-defined.
|
||||
bool containsPoint(Vector2 point) => containsLocalPoint(point);
|
||||
|
||||
/// An iterable of descendant components intersecting the given point. The
|
||||
/// [point] is in the local coordinate space.
|
||||
///
|
||||
/// More precisely, imagine a ray originating at a certain point (x, y) on
|
||||
/// the screen, and extending perpendicularly to the screen's surface into
|
||||
/// your game's world. The purpose of this method is to find all components
|
||||
/// that intersect with this ray, in the order from those that are closest to
|
||||
/// the user to those that are farthest.
|
||||
///
|
||||
/// The return value is an [Iterable] of `(component, point)` pairs, which
|
||||
/// gives not only the components themselves, but also the points of
|
||||
/// intersection, in their respective local coordinates.
|
||||
///
|
||||
/// The default implementation relies on the [CoordinateTransform] interface
|
||||
/// to translate from the parent's coordinate system into the local one. Make
|
||||
/// sure that your component implements this interface if it alters the
|
||||
/// coordinate system when rendering.
|
||||
///
|
||||
/// If your component overrides [renderTree], then it almost certainly needs
|
||||
/// to override this method as well, so that this method can find all rendered
|
||||
/// components wherever they are.
|
||||
Iterable<ComponentPointPair> componentsAtPoint(Vector2 point) sync* {
|
||||
if (_children != null) {
|
||||
for (final child in _children!.reversed()) {
|
||||
Vector2? childPoint = point;
|
||||
if (child is CoordinateTransform) {
|
||||
childPoint = (child as CoordinateTransform).parentToLocal(point);
|
||||
}
|
||||
if (childPoint != null) {
|
||||
yield* child.componentsAtPoint(childPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (containsLocalPoint(point)) {
|
||||
yield ComponentPointPair(this, point);
|
||||
}
|
||||
}
|
||||
|
||||
/// Usually this is not something that the user would want to call since the
|
||||
/// component list isn't re-ordered when it is called.
|
||||
|
||||
27
packages/flame/lib/src/components/component_point_pair.dart
Normal file
27
packages/flame/lib/src/components/component_point_pair.dart
Normal file
@ -0,0 +1,27 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import 'component.dart';
|
||||
|
||||
/// A simple tuple of a component and a point. This is a helper class for the
|
||||
/// [Component.componentsAtPoint] method.
|
||||
@immutable
|
||||
class ComponentPointPair {
|
||||
const ComponentPointPair(this.component, this.point);
|
||||
final Component component;
|
||||
final Vector2 point;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is ComponentPointPair &&
|
||||
other.component == component &&
|
||||
other.point == point;
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(component, point);
|
||||
|
||||
@override
|
||||
String toString() => '<$component, $point>';
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
import '../component.dart';
|
||||
|
||||
/// Interface to be implemented by components that perform a coordinate change.
|
||||
///
|
||||
/// Any [Component] that does any coordinate transformation of the canvas during
|
||||
/// rendering should consider implementing this interface in order to describe
|
||||
/// how the points from the parent's coordinate system relate to the component's
|
||||
/// local coordinate system.
|
||||
///
|
||||
/// This interface assumes that the component performs a "uniform" coordinate
|
||||
/// transformation, that is, the transform applies to all children of the
|
||||
/// component equally. If that is not the case (for example, the component does
|
||||
/// different transformations for some of its children), then that component
|
||||
/// must implement [Component.componentsAtPoint] method instead.
|
||||
///
|
||||
/// The two methods of this interface convert between the parent's coordinate
|
||||
/// space and the local coordinates. The methods may also return `null`,
|
||||
/// indicating that the given cannot be mapped to any local/parent point.
|
||||
abstract class CoordinateTransform {
|
||||
Vector2? parentToLocal(Vector2 point);
|
||||
|
||||
Vector2? localToParent(Vector2 point);
|
||||
}
|
||||
@ -8,6 +8,7 @@ import '../extensions/vector2.dart';
|
||||
import '../game/notifying_vector2.dart';
|
||||
import '../game/transform2d.dart';
|
||||
import 'component.dart';
|
||||
import 'mixins/coordinate_transform.dart';
|
||||
|
||||
/// A [Component] implementation that represents an object that can be
|
||||
/// freely moved around the screen, rotated, and scaled.
|
||||
@ -59,7 +60,12 @@ import 'component.dart';
|
||||
/// do not specify the size of a PositionComponent, then it will be
|
||||
/// equal to zero and the component won't be able to respond to taps.
|
||||
class PositionComponent extends Component
|
||||
implements AnchorProvider, AngleProvider, PositionProvider, ScaleProvider {
|
||||
implements
|
||||
AnchorProvider,
|
||||
AngleProvider,
|
||||
PositionProvider,
|
||||
ScaleProvider,
|
||||
CoordinateTransform {
|
||||
PositionComponent({
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
@ -214,14 +220,24 @@ class PositionComponent extends Component
|
||||
/// component. The top and the left borders of the component are inclusive,
|
||||
/// while the bottom and the right borders are exclusive.
|
||||
@override
|
||||
bool containsPoint(Vector2 point) {
|
||||
final local = absoluteToLocal(point);
|
||||
return (local.x >= 0) &&
|
||||
(local.y >= 0) &&
|
||||
(local.x < _size.x) &&
|
||||
(local.y < _size.y);
|
||||
bool containsLocalPoint(Vector2 point) {
|
||||
return (point.x >= 0) &&
|
||||
(point.y >= 0) &&
|
||||
(point.x < _size.x) &&
|
||||
(point.y < _size.y);
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsPoint(Vector2 point) {
|
||||
return containsLocalPoint(absoluteToLocal(point));
|
||||
}
|
||||
|
||||
@override
|
||||
Vector2 parentToLocal(Vector2 point) => transform.globalToLocal(point);
|
||||
|
||||
@override
|
||||
Vector2 localToParent(Vector2 point) => transform.localToGlobal(point);
|
||||
|
||||
/// Convert local coordinates of a point [point] inside the component
|
||||
/// into the parent's coordinate space.
|
||||
Vector2 positionOf(Vector2 point) {
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import '../components/component.dart';
|
||||
import '../components/component_point_pair.dart';
|
||||
import '../components/position_component.dart';
|
||||
import '../effects/controllers/effect_controller.dart';
|
||||
import '../effects/move_effect.dart';
|
||||
@ -92,7 +93,7 @@ class CameraComponent extends Component {
|
||||
viewport.clip(canvas);
|
||||
try {
|
||||
currentCameras.add(this);
|
||||
canvas.transform(viewfinder.transformMatrix.storage);
|
||||
canvas.transform(viewfinder.transform.transformMatrix.storage);
|
||||
world.renderFromCamera(canvas);
|
||||
viewfinder.renderTree(canvas);
|
||||
} finally {
|
||||
@ -105,6 +106,24 @@ class CameraComponent extends Component {
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<ComponentPointPair> componentsAtPoint(Vector2 point) sync* {
|
||||
final viewportPoint = point - viewport.position;
|
||||
if (world.isMounted && currentCameras.length < maxCamerasDepth) {
|
||||
if (viewport.containsPoint(viewportPoint)) {
|
||||
try {
|
||||
currentCameras.add(this);
|
||||
final worldPoint = viewfinder.transform.globalToLocal(viewportPoint);
|
||||
yield* world.componentsAtPointFromCamera(worldPoint);
|
||||
yield* viewfinder.componentsAtPoint(worldPoint);
|
||||
} finally {
|
||||
currentCameras.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
yield* viewport.componentsAtPoint(viewportPoint);
|
||||
}
|
||||
|
||||
/// A camera that currently performs rendering.
|
||||
///
|
||||
/// This variable is set to `this` when we begin rendering the world through
|
||||
|
||||
@ -12,14 +12,19 @@ class CircularViewport extends Viewport {
|
||||
}
|
||||
|
||||
Path _clipPath = Path();
|
||||
double _radiusSquared = 0;
|
||||
|
||||
@override
|
||||
void clip(Canvas canvas) => canvas.clipPath(_clipPath, doAntiAlias: false);
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) => point.length2 <= _radiusSquared;
|
||||
|
||||
@override
|
||||
void onViewportResize() {
|
||||
assert(size.x == size.y, 'Viewport shape is not circular: $size');
|
||||
final x = size.x / 2;
|
||||
final y = size.y / 2;
|
||||
_clipPath = Path()..addOval(Rect.fromLTRB(-x, -y, x, y));
|
||||
_clipPath = Path()..addOval(Rect.fromLTRB(-x, -x, x, x));
|
||||
_radiusSquared = x * x;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,12 @@ class FixedAspectRatioViewport extends Viewport {
|
||||
}
|
||||
|
||||
@override
|
||||
void clip(Canvas canvas) => canvas.clipRect(_clipRect);
|
||||
void clip(Canvas canvas) => canvas.clipRect(_clipRect, doAntiAlias: false);
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) {
|
||||
return point.x.abs() <= size.x / 2 && point.y.abs() <= size.y / 2;
|
||||
}
|
||||
|
||||
@override
|
||||
void onViewportResize() {
|
||||
|
||||
@ -24,6 +24,11 @@ class FixedSizeViewport extends Viewport {
|
||||
@override
|
||||
void clip(Canvas canvas) => canvas.clipRect(_clipRect, doAntiAlias: false);
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) {
|
||||
return point.x.abs() <= size.x / 2 && point.y.abs() <= size.y / 2;
|
||||
}
|
||||
|
||||
@override
|
||||
void onViewportResize() {
|
||||
final x = size.x / 2;
|
||||
|
||||
@ -21,6 +21,9 @@ class MaxViewport extends Viewport {
|
||||
@override
|
||||
void clip(Canvas canvas) {}
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) => true;
|
||||
|
||||
@override
|
||||
void onViewportResize() {}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ class Viewfinder extends Component
|
||||
final Transform2D _transform = Transform2D();
|
||||
|
||||
@internal
|
||||
Matrix4 get transformMatrix => _transform.transformMatrix;
|
||||
Transform2D get transform => _transform;
|
||||
|
||||
/// The game coordinates of a point that is to be positioned at the center
|
||||
/// of the viewport.
|
||||
|
||||
@ -66,6 +66,14 @@ abstract class Viewport extends Component implements PositionProvider {
|
||||
/// This API must be implemented by all viewports.
|
||||
void clip(Canvas canvas);
|
||||
|
||||
/// Tests whether the given point lies within the viewport.
|
||||
///
|
||||
/// This method must be consistent with the action of [clip], in the sense
|
||||
/// that [containsLocalPoint] must return true if and only if that point on
|
||||
/// the canvas is not clipped by [clip].
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point);
|
||||
|
||||
/// Override in order to perform a custom action upon resize.
|
||||
///
|
||||
/// A typical use-case would be to adjust the viewport's clip mask to match
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import '../components/component.dart';
|
||||
import '../components/component_point_pair.dart';
|
||||
import 'camera_component.dart';
|
||||
|
||||
/// The root component for all game world elements.
|
||||
@ -20,4 +22,14 @@ class World extends Component {
|
||||
assert(CameraComponent.currentCamera != null);
|
||||
super.renderTree(canvas);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<ComponentPointPair> componentsAtPoint(Vector2 point) {
|
||||
return const Iterable.empty();
|
||||
}
|
||||
|
||||
@internal
|
||||
Iterable<ComponentPointPair> componentsAtPointFromCamera(Vector2 point) {
|
||||
return super.componentsAtPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,8 +131,8 @@ class FlameGame extends Component with Game {
|
||||
|
||||
/// Whether a point is within the boundaries of the visible part of the game.
|
||||
@override
|
||||
bool containsPoint(Vector2 p) {
|
||||
return p.x > 0 && p.y > 0 && p.x < size.x && p.y < size.y;
|
||||
bool containsLocalPoint(Vector2 p) {
|
||||
return p.x >= 0 && p.y >= 0 && p.x < size.x && p.y < size.y;
|
||||
}
|
||||
|
||||
/// Returns the current time in seconds with microseconds precision.
|
||||
|
||||
@ -85,6 +85,14 @@ class CircleComponent extends ShapeComponent {
|
||||
scaledRadius * scaledRadius;
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) {
|
||||
final radius = size.x / 2;
|
||||
final dx = point.x - radius;
|
||||
final dy = point.y - radius;
|
||||
return dx * dx + dy * dy <= radius * radius;
|
||||
}
|
||||
|
||||
/// Returns the locus of points in which the provided line segment intersect
|
||||
/// the circle.
|
||||
///
|
||||
@ -94,7 +102,7 @@ class CircleComponent extends ShapeComponent {
|
||||
LineSegment line, {
|
||||
double epsilon = double.minPositive,
|
||||
}) {
|
||||
double sq(double x) => pow(x, 2).toDouble();
|
||||
double sq(double x) => x * x;
|
||||
|
||||
final cx = absoluteCenter.x;
|
||||
final cy = absoluteCenter.y;
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui' hide Canvas;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../cache.dart';
|
||||
import '../../components.dart';
|
||||
import '../../extensions.dart';
|
||||
import '../../geometry.dart';
|
||||
import '../anchor.dart';
|
||||
import '../cache/value_cache.dart';
|
||||
import '../components/component.dart';
|
||||
import '../extensions/rect.dart';
|
||||
import '../extensions/vector2.dart';
|
||||
import 'line_segment.dart';
|
||||
import 'shape_component.dart';
|
||||
|
||||
class PolygonComponent extends ShapeComponent {
|
||||
final List<Vector2> _vertices;
|
||||
@ -221,6 +224,23 @@ class PolygonComponent extends ShapeComponent {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsLocalPoint(Vector2 point) {
|
||||
if (size.x == 0 || size.y == 0) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < _vertices.length; i++) {
|
||||
final edge = getEdge(i, vertices: vertices);
|
||||
final isOutside = (edge.to.x - edge.from.x) * (point.y - edge.from.y) -
|
||||
(point.x - edge.from.x) * (edge.to.y - edge.from.y) >
|
||||
0;
|
||||
if (isOutside) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return all vertices as [LineSegment]s that intersect [rect], if [rect]
|
||||
/// is null return all vertices as [LineSegment]s.
|
||||
List<LineSegment> possibleIntersectionVertices(Rect? rect) {
|
||||
|
||||
@ -501,6 +501,56 @@ void main() {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('componentsAtPoint', () {
|
||||
testWithFlameGame('nested components', (game) async {
|
||||
final componentA = PositionComponent()
|
||||
..size = Vector2(200, 150)
|
||||
..scale = Vector2.all(2)
|
||||
..position = Vector2(350, 50)
|
||||
..addToParent(game);
|
||||
final componentB = CircleComponent(radius: 10)
|
||||
..position = Vector2(150, 75)
|
||||
..anchor = Anchor.center
|
||||
..addToParent(componentA);
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
game.componentsAtPoint(Vector2.zero()).toList(),
|
||||
[ComponentPointPair(game, Vector2.zero())],
|
||||
);
|
||||
expect(
|
||||
game.componentsAtPoint(Vector2(400, 100)).toList(),
|
||||
[
|
||||
ComponentPointPair(componentA, Vector2(25, 25)),
|
||||
ComponentPointPair(game, Vector2(400, 100)),
|
||||
],
|
||||
);
|
||||
expect(
|
||||
game.componentsAtPoint(Vector2(650, 200)).toList(),
|
||||
[
|
||||
ComponentPointPair(componentB, Vector2(10, 10)),
|
||||
ComponentPointPair(componentA, Vector2(150, 75)),
|
||||
ComponentPointPair(game, Vector2(650, 200)),
|
||||
],
|
||||
);
|
||||
expect(
|
||||
game.componentsAtPoint(Vector2(664, 214)).toList(),
|
||||
[
|
||||
ComponentPointPair(componentB, Vector2(17, 17)),
|
||||
ComponentPointPair(componentA, Vector2(157, 82)),
|
||||
ComponentPointPair(game, Vector2(664, 214)),
|
||||
],
|
||||
);
|
||||
expect(
|
||||
game.componentsAtPoint(Vector2(664, 216)).toList(),
|
||||
[
|
||||
ComponentPointPair(componentA, Vector2(157, 83)),
|
||||
ComponentPointPair(game, Vector2(664, 216)),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user