mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-14 11:51:21 +08:00
Add hitbox to PositionComponent (#618)
* Move out collision detection methods * Add possibility to define a hull for PositionComponents * Add example of how to use hull with tapable * Update contains point comment * Fix contains point * Hull should be based on center position * Remove collision detection parts * Added tests * Use percentage of size instead of absolute size * Separate hull from PositionComponent * Clarify hull example * Fix formatting * Override correct method * Use mixin for hitbox * Update changelog * Rename HasHitbox to Hitbox * Clarified names * Center to edge is considered as 1.0 * Fix test * Add spaces within braces * Removed extra spaces in the braces * Add hitbox docs * Fix link * Moved point rotation to Vector2 extension * Render hitbox within extension * Fix rebase * Fix rebase * Fix formatting
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
- Move files to comply with the dart package layout convention
|
- Move files to comply with the dart package layout convention
|
||||||
- Fix gesture detection bug of children of `PositionComponent`
|
- Fix gesture detection bug of children of `PositionComponent`
|
||||||
- The `game` argument on `GameWidget` is now required
|
- The `game` argument on `GameWidget` is now required
|
||||||
|
- Add hitbox mixin for PositionComponent to make more accurate gestures
|
||||||
|
|
||||||
## 1.0.0-rc5
|
## 1.0.0-rc5
|
||||||
- Option for overlays to be already visible on the GameWidget
|
- Option for overlays to be already visible on the GameWidget
|
||||||
|
|||||||
58
doc/examples/gestures/lib/main_tapables_hitbox.dart
Normal file
58
doc/examples/gestures/lib/main_tapables_hitbox.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(50),
|
||||||
|
color: const Color(0xFFA9A9A9),
|
||||||
|
child: GameWidget(
|
||||||
|
game: MyGame(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TapablePolygon extends PositionComponent with Tapable, Hitbox {
|
||||||
|
TapablePolygon({Vector2 position}) {
|
||||||
|
size = Vector2.all(100);
|
||||||
|
// The hitbox is defined as percentages of the full size of the component
|
||||||
|
shape = [
|
||||||
|
Vector2(-1.0, 0.0),
|
||||||
|
Vector2(-0.8, 0.6),
|
||||||
|
Vector2(0.0, 1.0),
|
||||||
|
Vector2(0.6, 0.9),
|
||||||
|
Vector2(1.0, 0.0),
|
||||||
|
Vector2(0.6, -0.8),
|
||||||
|
Vector2(0, -1.0),
|
||||||
|
Vector2(-0.8, -0.8),
|
||||||
|
];
|
||||||
|
this.position = position ?? Vector2.all(150);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onTapUp(TapUpDetails details) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onTapDown(TapDownDetails details) {
|
||||||
|
angle += 1.0;
|
||||||
|
size.add(Vector2.all(10));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onTapCancel() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyGame extends BaseGame with HasTapableComponents {
|
||||||
|
MyGame() {
|
||||||
|
debugMode = true;
|
||||||
|
add(TapablePolygon()..anchor = Anchor.center);
|
||||||
|
add(TapablePolygon()..y = 350);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
doc/input.md
10
doc/input.md
@@ -207,6 +207,16 @@ class MyGame extends BaseGame with HasDraggableComponents {
|
|||||||
|
|
||||||
Warning: `HasDraggableComponents` uses an advanced gesture detector under the hood and as explained further up on this page, shouldn't be used alongside basic detectors.
|
Warning: `HasDraggableComponents` uses an advanced gesture detector under the hood and as explained further up on this page, shouldn't be used alongside basic detectors.
|
||||||
|
|
||||||
|
## Hitbox
|
||||||
|
The `Hitbox` mixin is used to make detection of gestures on top of your `PositionComponent`s more
|
||||||
|
accurate. Say that you have a fairly round rock as a `SpriteComponent` for example, then you don't
|
||||||
|
want to register input that is in the corner of the image where the rock is not displayed. Then you
|
||||||
|
can use the `Hitbox` mixin to define a more accurate polygon for which the input should be within
|
||||||
|
for the event to be counted on your component.
|
||||||
|
|
||||||
|
An example of you to use it can be seen
|
||||||
|
[here](https://github.com/flame-engine/flame/blob/master/doc/examples/gestures/lib/main_tapables_hitbox.dart).
|
||||||
|
|
||||||
## Keyboard
|
## Keyboard
|
||||||
|
|
||||||
Flame provides a simple way to access Flutter's features regarding accessing Keyboard input events.
|
Flame provides a simple way to access Flutter's features regarding accessing Keyboard input events.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export 'joystick.dart';
|
|||||||
|
|
||||||
export 'src/components/mixins/draggable.dart';
|
export 'src/components/mixins/draggable.dart';
|
||||||
export 'src/components/mixins/has_game_ref.dart';
|
export 'src/components/mixins/has_game_ref.dart';
|
||||||
|
export 'src/components/mixins/hitbox.dart';
|
||||||
export 'src/components/mixins/single_child_particle.dart';
|
export 'src/components/mixins/single_child_particle.dart';
|
||||||
export 'src/components/mixins/tapable.dart';
|
export 'src/components/mixins/tapable.dart';
|
||||||
|
|
||||||
|
|||||||
18
lib/src/collision_detection.dart
Normal file
18
lib/src/collision_detection.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import '../extensions.dart';
|
||||||
|
|
||||||
|
/// Checks whether the [polygon] represented by the list of [Vector2] contains
|
||||||
|
/// the [point].
|
||||||
|
bool containsPoint(Vector2 point, List<Vector2> polygon) {
|
||||||
|
for (int i = 0; i < polygon.length; i++) {
|
||||||
|
final previousNode = polygon[i];
|
||||||
|
final node = polygon[(i + 1) % polygon.length];
|
||||||
|
final isOutside = (node.x - previousNode.x) * (point.y - previousNode.y) -
|
||||||
|
(point.x - previousNode.x) * (node.y - previousNode.y) >
|
||||||
|
0;
|
||||||
|
if (isOutside) {
|
||||||
|
// Point is outside of convex polygon
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -109,7 +109,7 @@ abstract class BaseComponent extends Component {
|
|||||||
/// Called to check whether the point is to be counted as within the component
|
/// 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 the
|
/// It needs to be overridden to have any effect, like it is in the
|
||||||
/// [PositionComponent]
|
/// [PositionComponent]
|
||||||
bool checkOverlap(Vector2 point) => false;
|
bool containsPoint(Vector2 point) => false;
|
||||||
|
|
||||||
/// Add an effect to the component
|
/// Add an effect to the component
|
||||||
void addEffect(ComponentEffect effect) {
|
void addEffect(ComponentEffect effect) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ mixin Draggable on BaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool handleReceiveDrag(DragEvent event) {
|
bool handleReceiveDrag(DragEvent event) {
|
||||||
if (checkOverlap(event.initialPosition.toVector2())) {
|
if (containsPoint(event.initialPosition.toVector2())) {
|
||||||
return onReceiveDrag(event);
|
return onReceiveDrag(event);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
84
lib/src/components/mixins/hitbox.dart
Normal file
84
lib/src/components/mixins/hitbox.dart
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import '../../../components.dart';
|
||||||
|
import '../../collision_detection.dart' as collision_detection;
|
||||||
|
|
||||||
|
mixin Hitbox on PositionComponent {
|
||||||
|
List<Vector2> _shape;
|
||||||
|
|
||||||
|
/// The list of vertices used for collision detection and to define whether
|
||||||
|
/// a point is inside of the component or not, so that the tap detection etc
|
||||||
|
/// can be more accurately performed.
|
||||||
|
/// The hitbox is defined from the center of the component and with
|
||||||
|
/// percentages of the size of the component.
|
||||||
|
/// Example: [[1.0, 0.0], [0.0, 1.0], [-1.0, 0.0], [0.0, -1.0]]
|
||||||
|
/// This will form a square with a 45 degree angle (pi/4 rad) within the
|
||||||
|
/// bounding size box.
|
||||||
|
set shape(List<Vector2> vertices) => _shape = vertices;
|
||||||
|
List<Vector2> get shape => _shape ?? [];
|
||||||
|
|
||||||
|
/// Whether the hitbox shape has defined vertices and is not an empty list
|
||||||
|
bool hasShape() => _shape?.isNotEmpty ?? false;
|
||||||
|
|
||||||
|
Iterable<Vector2> _scaledShape;
|
||||||
|
Vector2 _lastScaledSize;
|
||||||
|
|
||||||
|
/// Gives back the shape vectors multiplied by the size of the component
|
||||||
|
Iterable<Vector2> get scaledShape {
|
||||||
|
if (_lastScaledSize != size || _scaledShape == null) {
|
||||||
|
_lastScaledSize = size;
|
||||||
|
_scaledShape = _shape?.map(
|
||||||
|
(p) => p.clone()..multiply(size / 2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _scaledShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderContour(Canvas canvas) {
|
||||||
|
final hitboxPath = Path()
|
||||||
|
..addPolygon(
|
||||||
|
scaledShape.map((point) => (point + size / 2).toOffset()).toList(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
canvas.drawPath(hitboxPath, debugPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These variables are used to see whether the bounding vertices cache is
|
||||||
|
// valid or not
|
||||||
|
Vector2 _lastCachePosition;
|
||||||
|
Vector2 _lastCacheSize;
|
||||||
|
double _lastCacheAngle;
|
||||||
|
bool _hadShape = false;
|
||||||
|
List<Vector2> _cachedHitbox;
|
||||||
|
|
||||||
|
bool _isHitboxCacheValid() {
|
||||||
|
return _lastCacheAngle == angle &&
|
||||||
|
_lastCacheSize == size &&
|
||||||
|
_lastCachePosition == position &&
|
||||||
|
_hadShape == hasShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gives back the bounding vertices represented as a list of points which
|
||||||
|
/// are the "corners" of the hitbox rotated with [angle].
|
||||||
|
List<Vector2> get hitbox {
|
||||||
|
// Use cached bounding vertices if state of the component hasn't changed
|
||||||
|
if (!_isHitboxCacheValid()) {
|
||||||
|
_cachedHitbox = scaledShape
|
||||||
|
.map((point) => rotatePoint(center + point))
|
||||||
|
.toList(growable: false) ??
|
||||||
|
[];
|
||||||
|
_lastCachePosition = position.clone();
|
||||||
|
_lastCacheSize = size.clone();
|
||||||
|
_lastCacheAngle = angle;
|
||||||
|
_hadShape = hasShape();
|
||||||
|
}
|
||||||
|
return _cachedHitbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether the hitbox represented by the list of [Vector2] contains
|
||||||
|
/// the [point].
|
||||||
|
@override
|
||||||
|
bool containsPoint(Vector2 point) {
|
||||||
|
return collision_detection.containsPoint(point, hitbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ mixin Tapable on BaseComponent {
|
|||||||
bool _checkPointerId(int pointerId) => _currentPointerId == pointerId;
|
bool _checkPointerId(int pointerId) => _currentPointerId == pointerId;
|
||||||
|
|
||||||
bool handleTapDown(int pointerId, TapDownDetails details) {
|
bool handleTapDown(int pointerId, TapDownDetails details) {
|
||||||
if (checkOverlap(details.localPosition.toVector2())) {
|
if (containsPoint(details.localPosition.toVector2())) {
|
||||||
_currentPointerId = pointerId;
|
_currentPointerId = pointerId;
|
||||||
return onTapDown(details);
|
return onTapDown(details);
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ mixin Tapable on BaseComponent {
|
|||||||
|
|
||||||
bool handleTapUp(int pointerId, TapUpDetails details) {
|
bool handleTapUp(int pointerId, TapUpDetails details) {
|
||||||
if (_checkPointerId(pointerId) &&
|
if (_checkPointerId(pointerId) &&
|
||||||
checkOverlap(details.localPosition.toVector2())) {
|
containsPoint(details.localPosition.toVector2())) {
|
||||||
_currentPointerId = null;
|
_currentPointerId = null;
|
||||||
return onTapUp(details);
|
return onTapUp(details);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'dart:ui' hide Offset;
|
import 'dart:ui' hide Offset;
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
|
import '../collision_detection.dart' as collision_detection;
|
||||||
import '../anchor.dart';
|
import '../anchor.dart';
|
||||||
import '../extensions/offset.dart';
|
import '../extensions/offset.dart';
|
||||||
import '../extensions/vector2.dart';
|
import '../extensions/vector2.dart';
|
||||||
import '../../game.dart';
|
|
||||||
import 'base_component.dart';
|
import 'base_component.dart';
|
||||||
import 'component.dart';
|
import 'component.dart';
|
||||||
|
import 'mixins/hitbox.dart';
|
||||||
|
|
||||||
/// A [Component] implementation that represents a component that has a
|
/// A [Component] implementation that represents a component that has a
|
||||||
/// specific, possibly dynamic position on the screen.
|
/// specific, possibly dynamic position on the screen.
|
||||||
@@ -71,6 +71,11 @@ abstract class PositionComponent extends BaseComponent {
|
|||||||
this.position = position + (anchor.toVector2..multiply(size));
|
this.position = position + (anchor.toVector2..multiply(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the position of the center of the component
|
||||||
|
Vector2 get center {
|
||||||
|
return anchor == Anchor.center ? position : topLeftPosition + (size / 2);
|
||||||
|
}
|
||||||
|
|
||||||
/// Angle (with respect to the x-axis) this component should be rendered with.
|
/// Angle (with respect to the x-axis) this component should be rendered with.
|
||||||
/// It is rotated around its anchor.
|
/// It is rotated around its anchor.
|
||||||
double angle = 0.0;
|
double angle = 0.0;
|
||||||
@@ -98,45 +103,22 @@ abstract class PositionComponent extends BaseComponent {
|
|||||||
topLeftPosition = rect.topLeft.toVector2();
|
topLeftPosition = rect.topLeft.toVector2();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
/// Rotate [point] around component's angle and position (anchor)
|
||||||
bool checkOverlap(Vector2 absolutePoint) {
|
Vector2 rotatePoint(Vector2 point) {
|
||||||
final point = absolutePoint - absoluteCanvasPosition;
|
return point.clone()..rotate(angle, center: position);
|
||||||
final corners = _rotatedCorners();
|
|
||||||
for (int i = 0; i < corners.length; i++) {
|
|
||||||
final previousCorner = corners[i];
|
|
||||||
final corner = corners[(i + 1) % corners.length];
|
|
||||||
final isOutside =
|
|
||||||
(corner.x - previousCorner.x) * (point.y - previousCorner.y) -
|
|
||||||
(point.x - previousCorner.x) * (corner.y - previousCorner.y) >
|
|
||||||
0;
|
|
||||||
if (isOutside) {
|
|
||||||
// Point is outside of convex polygon (only used for rectangles so far)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Vector2> _rotatedCorners() {
|
@override
|
||||||
// Rotates the corner around [position]
|
bool containsPoint(Vector2 point) {
|
||||||
Vector2 rotateCorner(Vector2 corner) {
|
final corners = [
|
||||||
return Vector2(
|
rotatePoint(absoluteTopLeftPosition), // Top-left
|
||||||
math.cos(angle) * (corner.x - position.x) -
|
rotatePoint(
|
||||||
math.sin(angle) * (corner.y - position.y) +
|
absoluteTopLeftPosition + Vector2(0.0, size.y)), // Bottom-left
|
||||||
position.x,
|
rotatePoint(absoluteTopLeftPosition + size), // Bottom-right
|
||||||
math.sin(angle) * (corner.x - position.x) +
|
rotatePoint(absoluteTopLeftPosition + Vector2(size.x, 0.0)), // Top-right
|
||||||
math.cos(angle) * (corner.y - position.y) +
|
|
||||||
position.y,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter-clockwise direction
|
|
||||||
return [
|
|
||||||
rotateCorner(topLeftPosition), // Top-left
|
|
||||||
rotateCorner(topLeftPosition + Vector2(0.0, size.y)), // Bottom-left
|
|
||||||
rotateCorner(topLeftPosition + size), // Bottom-right
|
|
||||||
rotateCorner(topLeftPosition + Vector2(size.x, 0.0)), // Top-right
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return collision_detection.containsPoint(point, corners);
|
||||||
}
|
}
|
||||||
|
|
||||||
double angleTo(PositionComponent c) => position.angleTo(c.position);
|
double angleTo(PositionComponent c) => position.angleTo(c.position);
|
||||||
@@ -145,6 +127,9 @@ abstract class PositionComponent extends BaseComponent {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void renderDebugMode(Canvas canvas) {
|
void renderDebugMode(Canvas canvas) {
|
||||||
|
if (this is Hitbox) {
|
||||||
|
(this as Hitbox).renderContour(canvas);
|
||||||
|
}
|
||||||
canvas.drawRect(size.toRect(), debugPaint);
|
canvas.drawRect(size.toRect(), debugPaint);
|
||||||
debugTextConfig.render(
|
debugTextConfig.render(
|
||||||
canvas,
|
canvas,
|
||||||
|
|||||||
@@ -28,11 +28,19 @@ extension Vector2Extension on Vector2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Rotates the [Vector2] with [angle] in radians
|
/// Rotates the [Vector2] with [angle] in radians
|
||||||
void rotate(double angle) {
|
/// rotates around [center] if it is defined
|
||||||
setValues(
|
void rotate(double angle, {Vector2 center}) {
|
||||||
x * cos(angle) - y * sin(angle),
|
if (center == null) {
|
||||||
x * sin(angle) + y * cos(angle),
|
setValues(
|
||||||
);
|
x * cos(angle) - y * sin(angle),
|
||||||
|
x * sin(angle) + y * cos(angle),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setValues(
|
||||||
|
cos(angle) * (x - center.x) - sin(angle) * (y - center.y) + center.x,
|
||||||
|
sin(angle) * (x - center.x) + cos(angle) * (y - center.y) + center.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the [length] of the vector to the length provided, without changing direction.
|
/// Changes the [length] of the vector to the length provided, without changing direction.
|
||||||
@@ -45,6 +53,9 @@ extension Vector2Extension on Vector2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modulo/Remainder
|
||||||
|
Vector2 operator %(Vector2 mod) => Vector2(x % mod.x, y % mod.y);
|
||||||
|
|
||||||
/// Create a Vector2 with ints as input
|
/// Create a Vector2 with ints as input
|
||||||
static Vector2 fromInts(int x, int y) => Vector2(x.toDouble(), y.toDouble());
|
static Vector2 fromInts(int x, int y) => Vector2(x.toDouble(), y.toDouble());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class MyComponent extends PositionComponent with Tapable, HasGameRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool checkOverlap(Vector2 v) => true;
|
bool containsPoint(Vector2 v) => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onRemove() {
|
void onRemove() {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:test/test.dart';
|
|||||||
|
|
||||||
class MyComponent extends PositionComponent {}
|
class MyComponent extends PositionComponent {}
|
||||||
|
|
||||||
|
class MyHitboxComponent extends PositionComponent with Hitbox {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('PositionComponent overlap test', () {
|
group('PositionComponent overlap test', () {
|
||||||
test('overlap', () {
|
test('overlap', () {
|
||||||
@@ -15,7 +17,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(2.0, 2.0);
|
final point = Vector2(2.0, 2.0);
|
||||||
expect(component.checkOverlap(point), true);
|
expect(component.containsPoint(point), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('overlap on edge', () {
|
test('overlap on edge', () {
|
||||||
@@ -26,7 +28,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(1.0, 1.0);
|
final point = Vector2(1.0, 1.0);
|
||||||
expect(component.checkOverlap(point), true);
|
expect(component.containsPoint(point), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('not overlapping with x', () {
|
test('not overlapping with x', () {
|
||||||
@@ -37,7 +39,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(4.0, 1.0);
|
final point = Vector2(4.0, 1.0);
|
||||||
expect(component.checkOverlap(point), false);
|
expect(component.containsPoint(point), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('not overlapping with y', () {
|
test('not overlapping with y', () {
|
||||||
@@ -48,7 +50,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(1.0, 4.0);
|
final point = Vector2(1.0, 4.0);
|
||||||
expect(component.checkOverlap(point), false);
|
expect(component.containsPoint(point), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('overlapping with angle', () {
|
test('overlapping with angle', () {
|
||||||
@@ -59,7 +61,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(3.1, 2.0);
|
final point = Vector2(3.1, 2.0);
|
||||||
expect(component.checkOverlap(point), true);
|
expect(component.containsPoint(point), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('not overlapping with angle', () {
|
test('not overlapping with angle', () {
|
||||||
@@ -70,7 +72,7 @@ void main() {
|
|||||||
component.anchor = Anchor.center;
|
component.anchor = Anchor.center;
|
||||||
|
|
||||||
final point = Vector2(1.0, 0.1);
|
final point = Vector2(1.0, 0.1);
|
||||||
expect(component.checkOverlap(point), false);
|
expect(component.containsPoint(point), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('overlapping with angle and topLeft anchor', () {
|
test('overlapping with angle and topLeft anchor', () {
|
||||||
@@ -81,7 +83,41 @@ void main() {
|
|||||||
component.anchor = Anchor.topLeft;
|
component.anchor = Anchor.topLeft;
|
||||||
|
|
||||||
final point = Vector2(1.0, 3.1);
|
final point = Vector2(1.0, 3.1);
|
||||||
expect(component.checkOverlap(point), true);
|
expect(component.containsPoint(point), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
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.anchor = Anchor.topLeft;
|
||||||
|
component.size = size;
|
||||||
|
component.shape = [
|
||||||
|
Vector2(1, 0),
|
||||||
|
Vector2(0, -1),
|
||||||
|
Vector2(-1, 0),
|
||||||
|
Vector2(0, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
final point = component.position + component.size / 4;
|
||||||
|
expect(component.containsPoint(point), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
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.anchor = Anchor.topLeft;
|
||||||
|
component.size = size;
|
||||||
|
component.shape = [
|
||||||
|
Vector2(1, 0),
|
||||||
|
Vector2(0, -1),
|
||||||
|
Vector2(-1, 0),
|
||||||
|
Vector2(0, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
final point = Vector2(1.1, 1.1);
|
||||||
|
expect(component.containsPoint(point), false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user