diff --git a/packages/flame/lib/src/collisions/hitboxes/polygon_hitbox.dart b/packages/flame/lib/src/collisions/hitboxes/polygon_hitbox.dart index 878458de07e..3bb172fdb71 100644 --- a/packages/flame/lib/src/collisions/hitboxes/polygon_hitbox.dart +++ b/packages/flame/lib/src/collisions/hitboxes/polygon_hitbox.dart @@ -40,6 +40,21 @@ class PolygonHitbox extends PolygonComponent this.collisionType = collisionType; } + /// With this constructor you create a regular (equiangular and equilateral) + /// polygon hitbox from number of sides and radius. + PolygonHitbox.regular({ + required super.sides, + required super.radius, + super.position, + super.angle, + super.anchor, + bool isSolid = false, + CollisionType collisionType = CollisionType.active, + }) : super.regular() { + this.isSolid = isSolid; + this.collisionType = collisionType; + } + @override @protected void computeAabb(Aabb2 aabb) { diff --git a/packages/flame/lib/src/geometry/polygon_component.dart b/packages/flame/lib/src/geometry/polygon_component.dart index 7c528584e71..044283fb062 100644 --- a/packages/flame/lib/src/geometry/polygon_component.dart +++ b/packages/flame/lib/src/geometry/polygon_component.dart @@ -22,9 +22,9 @@ class PolygonComponent extends ShapeComponent { final _cachedGlobalVertices = ValueCache>(); - /// With this constructor you create your [PolygonComponent] from positions in + /// With this constructor you create your [PolygonComponent] from positions /// anywhere in the 2d-space. It will automatically calculate the [size] of - /// the Polygon (the bounding box) if no size it given. + /// the Polygon (the bounding box) if no size is given. PolygonComponent( this._vertices, { super.position, @@ -93,6 +93,42 @@ class PolygonComponent extends ShapeComponent { children: children, ); + /// With this constructor you create a regular (equiangular and equilateral) + /// polygon from number of sides and radius anywhere in the 2d-space. It will + /// automatically calculate the [size] of the Polygon (the bounding box) if no + /// size is given. + PolygonComponent.regular({ + required int sides, + required double radius, + Vector2? position, + Vector2? size, + Vector2? scale, + double? angle, + Anchor? anchor, + Iterable? children, + int? priority, + Paint? paint, + List? paintLayers, + ComponentKey? key, + bool? shrinkToBounds, + }) : this( + List.generate(sides, (i) { + final angle = 2 * pi * i / sides; + return Vector2(radius * cos(angle), radius * sin(angle)); + }, growable: false), + position: position, + size: size, + scale: scale, + angle: angle, + anchor: anchor, + children: children, + priority: priority, + paint: paint, + paintLayers: paintLayers, + key: key, + shrinkToBounds: shrinkToBounds, + ); + @internal static List normalsToVertices( List normals, diff --git a/packages/flame/test/collisions/polygon_hitbox_regular_constructor_test.dart b/packages/flame/test/collisions/polygon_hitbox_regular_constructor_test.dart new file mode 100644 index 00000000000..391f99665db --- /dev/null +++ b/packages/flame/test/collisions/polygon_hitbox_regular_constructor_test.dart @@ -0,0 +1,48 @@ +import 'package:flame/collisions.dart'; +import 'package:flame/components.dart'; +import 'package:test/test.dart'; + +void main() { + group('PolygonHitbox.regular', () { + test('creates the expected number of vertices', () { + final hitbox = PolygonHitbox.regular(sides: 7, radius: 10); + + expect(hitbox.vertices, hasLength(7)); + }); + + test('places all vertices at the given radius from the center', () { + const radius = 12.5; + final hitbox = PolygonHitbox.regular(sides: 8, radius: radius); + + for (final vertex in hitbox.vertices) { + expect(vertex.length, closeTo(radius, 1e-10)); + } + }); + + test('uses center anchor by default', () { + final hitbox = PolygonHitbox.regular(sides: 5, radius: 10); + + expect(hitbox.anchor, Anchor.center); + }); + + test('supports custom position and anchor', () { + final hitbox = PolygonHitbox.regular( + sides: 6, + radius: 3, + position: Vector2(10, 20), + anchor: Anchor.topLeft, + ); + + expect(hitbox.position.x, closeTo(10, 1e-10)); + expect(hitbox.position.y, closeTo(20, 1e-10)); + expect(hitbox.anchor, Anchor.topLeft); + }); + + test('throws assertion error when sides are less than 3', () { + expect( + () => PolygonHitbox.regular(sides: 2, radius: 5), + throwsA(isA()), + ); + }); + }); +} diff --git a/packages/flame/test/geometry/polygon_component_regular_constructor_test.dart b/packages/flame/test/geometry/polygon_component_regular_constructor_test.dart new file mode 100644 index 00000000000..be4157bf56e --- /dev/null +++ b/packages/flame/test/geometry/polygon_component_regular_constructor_test.dart @@ -0,0 +1,47 @@ +import 'package:flame/components.dart'; +import 'package:test/test.dart'; + +void main() { + group('PolygonComponent.regular', () { + test('creates the expected number of vertices', () { + final component = PolygonComponent.regular(sides: 7, radius: 10); + + expect(component.vertices, hasLength(7)); + }); + + test('places all vertices at the given radius from the center', () { + const radius = 12.5; + final component = PolygonComponent.regular(sides: 8, radius: radius); + + for (final vertex in component.vertices) { + expect(vertex.length, closeTo(radius, 1e-10)); + } + }); + + test('uses center anchor by default', () { + final component = PolygonComponent.regular(sides: 5, radius: 10); + + expect(component.anchor, Anchor.center); + }); + + test('supports custom position and anchor', () { + final component = PolygonComponent.regular( + sides: 6, + radius: 3, + position: Vector2(10, 20), + anchor: Anchor.topLeft, + ); + + expect(component.position.x, closeTo(10, 1e-10)); + expect(component.position.y, closeTo(20, 1e-10)); + expect(component.anchor, Anchor.topLeft); + }); + + test('throws assertion error when sides are less than 3', () { + expect( + () => PolygonComponent.regular(sides: 2, radius: 5), + throwsA(isA()), + ); + }); + }); +}