mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 20:13:50 +08:00
ShapeComponent and Hitbox to take transform of parents full ancestor tree into consideration (#1076)
* `ShapeComponent` changes size, position and angle of underlying Shape * Added description to ShapeComponent * Fix test * Update packages/flame/lib/src/components/shape_component.dart Co-authored-by: Erick <erickzanardoo@gmail.com> * Add absoluteScale and absoluteAngle to PositionComponent * Refactor ShapeComponent * Should be scaled by total scale, not scaled size * Premature optimization for creation for objects in Polygon * Use path for default Polygon constructor * Do not sync component and hitbox shape * Fix analyze issue * Add example for flipping with collision detection * Don't use absoluteScale * Fix examples * Fix examples * Doesn't need super.render * Fix Circle dartdoc * Update changelog * Update names of vertices caches in Polygon * Update text docs * Revert "Update text docs" This reverts commit 73a68a465d76eb0eb50bb3753e57b2f4e3b5a7f4. * Fix examples * ShapeComponents docs * Move example games to the top * Fix dartdoc comment about polygon vertex relation * Fix order of polygon vertices in dartdoc * Fix anchor for PolygonComponent.fromPoints * Add test with ancestors * Update doc/components.md Co-authored-by: Pasha Stetsenko <stpasha@google.com> * Update doc/components.md Co-authored-by: Erick <erickzanardoo@gmail.com> * Rename example classes * Fix linting issues in examples * Don't use px * Use isTrue and isFalse * Update doc/components.md Co-authored-by: Erick <erickzanardoo@gmail.com> * Fixed comments on PR Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Pasha Stetsenko <stpasha@google.com>
This commit is contained in:
@ -7,14 +7,27 @@ import 'package:flame/geometry.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flutter/material.dart' hide Image, Draggable;
|
||||
|
||||
const circlesInfo = '''
|
||||
This example will create a circle every time you tap on the screen. It will have
|
||||
the initial velocity towards the center of the screen and if it touches another
|
||||
circle both of them will change color.
|
||||
''';
|
||||
class CirclesExample extends FlameGame with HasCollidables, TapDetector {
|
||||
static const description = '''
|
||||
This example will create a circle every time you tap on the screen. It will
|
||||
have the initial velocity towards the center of the screen and if it touches
|
||||
another circle both of them will change color.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
super.onLoad();
|
||||
add(ScreenCollidable());
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapDown(TapDownInfo info) {
|
||||
add(MyCollidable(info.eventPosition.game));
|
||||
}
|
||||
}
|
||||
|
||||
class MyCollidable extends PositionComponent
|
||||
with HasGameRef<Circles>, HasHitboxes, Collidable {
|
||||
with HasGameRef<CirclesExample>, HasHitboxes, Collidable {
|
||||
late Vector2 velocity;
|
||||
final _collisionColor = Colors.amber;
|
||||
final _defaultColor = Colors.cyan;
|
||||
@ -63,16 +76,3 @@ class MyCollidable extends PositionComponent
|
||||
_isCollision = true;
|
||||
}
|
||||
}
|
||||
|
||||
class Circles extends FlameGame with HasCollidables, TapDetector {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
super.onLoad();
|
||||
add(ScreenCollidable());
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapDown(TapDownInfo info) {
|
||||
add(MyCollidable(info.eventPosition.game));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
|
||||
class CollidableAnimationExample extends FlameGame with HasCollidables {
|
||||
static const description = '''
|
||||
In this example you can see four animated birds which are flying straight
|
||||
along the same route until they hit either another bird or the wall, which
|
||||
makes them turn. The birds have PolygonHitboxes which are marked with the
|
||||
green lines and dots.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
add(ScreenCollidable());
|
||||
// Top left component
|
||||
add(
|
||||
AnimatedComponent(Vector2.all(200), Vector2.all(100))..flipVertically(),
|
||||
);
|
||||
// Bottom right component
|
||||
add(
|
||||
AnimatedComponent(
|
||||
Vector2(-100, -100),
|
||||
size.clone()..sub(Vector2.all(200)),
|
||||
),
|
||||
);
|
||||
// Bottom left component
|
||||
add(
|
||||
AnimatedComponent(
|
||||
Vector2(100, -100),
|
||||
Vector2(100, size.y - 100),
|
||||
angle: pi / 4,
|
||||
),
|
||||
);
|
||||
// Top right component
|
||||
add(
|
||||
AnimatedComponent(
|
||||
Vector2(-300, 300),
|
||||
Vector2(size.x - 100, 100),
|
||||
angle: pi / 4,
|
||||
)..flipVertically(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AnimatedComponent extends SpriteAnimationComponent
|
||||
with HasHitboxes, Collidable, HasGameRef {
|
||||
final Vector2 velocity;
|
||||
final List<Collidable> activeCollisions = [];
|
||||
|
||||
AnimatedComponent(this.velocity, Vector2 position, {double angle = -pi / 4})
|
||||
: super(
|
||||
position: position,
|
||||
size: Vector2(150, 100),
|
||||
angle: angle,
|
||||
anchor: Anchor.center,
|
||||
);
|
||||
|
||||
late HitboxPolygon hitbox;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
animation = await gameRef.loadSpriteAnimation(
|
||||
'bomb_ptero.png',
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: 4,
|
||||
stepTime: 0.2,
|
||||
textureSize: Vector2.all(48),
|
||||
),
|
||||
);
|
||||
hitbox = HitboxPolygon([
|
||||
Vector2(0.0, -1.0),
|
||||
Vector2(-1.0, -0.1),
|
||||
Vector2(-0.2, 0.4),
|
||||
Vector2(0.2, 0.4),
|
||||
Vector2(1.0, -0.1),
|
||||
]);
|
||||
addHitbox(hitbox);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
position += velocity * dt;
|
||||
}
|
||||
|
||||
final Paint hitboxPaint = BasicPalette.green.paint()
|
||||
..style = PaintingStyle.stroke;
|
||||
final Paint dotPaint = BasicPalette.red.paint()..style = PaintingStyle.stroke;
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
// This is just to clearly see the vertices in the hitboxes
|
||||
hitbox.render(canvas, hitboxPaint);
|
||||
hitbox
|
||||
.localVertices()
|
||||
.forEach((p) => canvas.drawCircle(p.toOffset(), 4, dotPaint));
|
||||
}
|
||||
|
||||
@override
|
||||
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
|
||||
if (!activeCollisions.contains(other)) {
|
||||
velocity.negate();
|
||||
flipVertically();
|
||||
activeCollisions.add(other);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onCollisionEnd(Collidable other) {
|
||||
activeCollisions.remove(other);
|
||||
}
|
||||
}
|
||||
@ -3,27 +3,34 @@ import 'package:flame/game.dart';
|
||||
|
||||
import '../../commons/commons.dart';
|
||||
import 'circles.dart';
|
||||
import 'collidable_animation.dart';
|
||||
import 'multiple_shapes.dart';
|
||||
import 'only_shapes.dart';
|
||||
import 'simple_shapes.dart';
|
||||
|
||||
void addCollisionDetectionStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Collision Detection')
|
||||
..add(
|
||||
'Collidable AnimationComponent',
|
||||
(_) => GameWidget(game: CollidableAnimationExample()),
|
||||
codeLink: baseLink('collision_detection/collidable_animation.dart'),
|
||||
info: CollidableAnimationExample.description,
|
||||
)
|
||||
..add(
|
||||
'Circles',
|
||||
(_) => GameWidget(game: Circles()),
|
||||
(_) => GameWidget(game: CirclesExample()),
|
||||
codeLink: baseLink('collision_detection/circles.dart'),
|
||||
info: circlesInfo,
|
||||
info: CirclesExample.description,
|
||||
)
|
||||
..add(
|
||||
'Multiple shapes',
|
||||
(_) => GameWidget(game: MultipleShapes()),
|
||||
(_) => GameWidget(game: MultipleShapesExample()),
|
||||
codeLink: baseLink('collision_detection/multiple_shapes.dart'),
|
||||
info: multipleShapesInfo,
|
||||
info: MultipleShapesExample.description,
|
||||
)
|
||||
..add(
|
||||
'Simple Shapes',
|
||||
(_) => GameWidget(game: OnlyShapes()),
|
||||
codeLink: baseLink('collision_detection/only_shapes.dart'),
|
||||
info: onlyShapesInfo,
|
||||
(_) => GameWidget(game: SimpleShapesExample()),
|
||||
codeLink: baseLink('collision_detection/simple_shapes.dart'),
|
||||
info: SimpleShapesExample.description,
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,21 +9,95 @@ import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart' hide Image, Draggable;
|
||||
|
||||
const multipleShapesInfo = '''
|
||||
An example with many hitboxes that move around on the screen and during
|
||||
collisions they change color depending on what it is that they have collided
|
||||
with.
|
||||
|
||||
The snowman, the component built with three circles on top of each other, works
|
||||
a little bit differently than the other components to show that you can have
|
||||
multiple hitboxes within one component.
|
||||
|
||||
On this example, you can "throw" the components by dragging them quickly in any
|
||||
direction.
|
||||
''';
|
||||
|
||||
enum Shapes { circle, rectangle, polygon }
|
||||
|
||||
class MultipleShapesExample extends FlameGame
|
||||
with HasCollidables, HasDraggableComponents, FPSCounter {
|
||||
static const description = '''
|
||||
An example with many hitboxes that move around on the screen and during
|
||||
collisions they change color depending on what it is that they have collided
|
||||
with.
|
||||
|
||||
The snowman, the component built with three circles on top of each other,
|
||||
works a little bit differently than the other components to show that you
|
||||
can have multiple hitboxes within one component.
|
||||
|
||||
On this example, you can "throw" the components by dragging them quickly in
|
||||
any direction.
|
||||
''';
|
||||
|
||||
final TextPaint fpsTextPaint = TextPaint(
|
||||
config: TextPaintConfig(
|
||||
color: BasicPalette.white.color,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final screenCollidable = ScreenCollidable();
|
||||
final snowman = CollidableSnowman(
|
||||
Vector2.all(150),
|
||||
Vector2(100, 200),
|
||||
Vector2(-100, 100),
|
||||
screenCollidable,
|
||||
);
|
||||
MyCollidable lastToAdd = snowman;
|
||||
add(screenCollidable);
|
||||
add(snowman);
|
||||
var totalAdded = 1;
|
||||
while (totalAdded < 20) {
|
||||
lastToAdd = nextRandomCollidable(lastToAdd, screenCollidable);
|
||||
final lastBottomRight =
|
||||
lastToAdd.toAbsoluteRect().bottomRight.toVector2();
|
||||
if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) {
|
||||
add(lastToAdd);
|
||||
totalAdded++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _rng = Random();
|
||||
final _distance = Vector2(100, 0);
|
||||
|
||||
MyCollidable nextRandomCollidable(
|
||||
MyCollidable lastCollidable,
|
||||
ScreenCollidable screenCollidable,
|
||||
) {
|
||||
final collidableSize = Vector2.all(50) + Vector2.random(_rng) * 100;
|
||||
final isXOverflow = lastCollidable.position.x +
|
||||
lastCollidable.size.x / 2 +
|
||||
_distance.x +
|
||||
collidableSize.x >
|
||||
size.x;
|
||||
var position = _distance + Vector2(0, lastCollidable.position.y + 200);
|
||||
if (!isXOverflow) {
|
||||
position = (lastCollidable.position + _distance)
|
||||
..x += collidableSize.x / 2;
|
||||
}
|
||||
final velocity = (Vector2.random(_rng) - Vector2.random(_rng)) * 400;
|
||||
return randomCollidable(
|
||||
position,
|
||||
collidableSize,
|
||||
velocity,
|
||||
screenCollidable,
|
||||
rng: _rng,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
fpsTextPaint.render(
|
||||
canvas,
|
||||
'${fps(120).toStringAsFixed(2)}fps',
|
||||
Vector2(0, size.y - 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MyCollidable extends PositionComponent
|
||||
with Draggable, HasHitboxes, Collidable {
|
||||
double rotationSpeed = 0.0;
|
||||
@ -41,11 +115,7 @@ abstract class MyCollidable extends PositionComponent
|
||||
Vector2 size,
|
||||
this.velocity,
|
||||
this.screenCollidable,
|
||||
) {
|
||||
this.position = position;
|
||||
this.size = size;
|
||||
anchor = Anchor.center;
|
||||
}
|
||||
) : super(position: position, size: size, anchor: Anchor.center);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
@ -137,7 +207,7 @@ class CollidablePolygon extends MyCollidable {
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
final shape = HitboxPolygon([
|
||||
final hitbox = HitboxPolygon([
|
||||
Vector2(-1.0, 0.0),
|
||||
Vector2(-0.8, 0.6),
|
||||
Vector2(0.0, 1.0),
|
||||
@ -147,7 +217,7 @@ class CollidablePolygon extends MyCollidable {
|
||||
Vector2(0, -1.0),
|
||||
Vector2(-0.8, -0.8),
|
||||
]);
|
||||
addHitbox(shape);
|
||||
addHitbox(hitbox);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,8 +239,7 @@ class CollidableCircle extends MyCollidable {
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
final shape = HitboxCircle();
|
||||
addHitbox(shape);
|
||||
addHitbox(HitboxCircle());
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +248,7 @@ class SnowmanPart extends HitboxCircle {
|
||||
final hitPaint = Paint();
|
||||
|
||||
SnowmanPart(double definition, Vector2 relativeOffset, Color hitColor)
|
||||
: super(definition: definition) {
|
||||
: super(normalizedRadius: definition) {
|
||||
this.relativeOffset.setFrom(relativeOffset);
|
||||
hitPaint..color = startColor;
|
||||
onCollision = (Set<Vector2> intersectionPoints, HitboxShape other) {
|
||||
@ -245,77 +314,3 @@ MyCollidable randomCollidable(
|
||||
..rotationSpeed = rotationSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleShapes extends FlameGame
|
||||
with HasCollidables, HasDraggableComponents, FPSCounter {
|
||||
final TextPaint fpsTextPaint = TextPaint(
|
||||
config: TextPaintConfig(
|
||||
color: BasicPalette.white.color,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final screenCollidable = ScreenCollidable();
|
||||
final snowman = CollidableSnowman(
|
||||
Vector2.all(150),
|
||||
Vector2(100, 200),
|
||||
Vector2(-100, 100),
|
||||
screenCollidable,
|
||||
);
|
||||
MyCollidable lastToAdd = snowman;
|
||||
add(screenCollidable);
|
||||
add(snowman);
|
||||
var totalAdded = 1;
|
||||
while (totalAdded < 20) {
|
||||
lastToAdd = nextRandomCollidable(lastToAdd, screenCollidable);
|
||||
final lastBottomRight =
|
||||
lastToAdd.toAbsoluteRect().bottomRight.toVector2();
|
||||
if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) {
|
||||
add(lastToAdd);
|
||||
totalAdded++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _rng = Random();
|
||||
final _distance = Vector2(100, 0);
|
||||
|
||||
MyCollidable nextRandomCollidable(
|
||||
MyCollidable lastCollidable,
|
||||
ScreenCollidable screenCollidable,
|
||||
) {
|
||||
final collidableSize = Vector2.all(50) + Vector2.random(_rng) * 100;
|
||||
final isXOverflow = lastCollidable.position.x +
|
||||
lastCollidable.size.x / 2 +
|
||||
_distance.x +
|
||||
collidableSize.x >
|
||||
size.x;
|
||||
var position = _distance + Vector2(0, lastCollidable.position.y + 200);
|
||||
if (!isXOverflow) {
|
||||
position = (lastCollidable.position + _distance)
|
||||
..x += collidableSize.x / 2;
|
||||
}
|
||||
final velocity = (Vector2.random(_rng) - Vector2.random(_rng)) * 400;
|
||||
return randomCollidable(
|
||||
position,
|
||||
collidableSize,
|
||||
velocity,
|
||||
screenCollidable,
|
||||
rng: _rng,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
fpsTextPaint.render(
|
||||
canvas,
|
||||
'${fps(120).toStringAsFixed(2)}fps',
|
||||
Vector2(0, size.y - 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart' hide Image, Draggable;
|
||||
|
||||
const onlyShapesInfo = '''
|
||||
An example which adds random shapes on the screen when you tap it, if you tap on
|
||||
an already existing shape it will remove that shape and replace it with a new
|
||||
one.
|
||||
''';
|
||||
|
||||
enum Shapes { circle, rectangle, polygon }
|
||||
|
||||
class OnlyShapes extends FlameGame with HasTappableComponents {
|
||||
final shapePaint = BasicPalette.red.paint()..style = PaintingStyle.stroke;
|
||||
final _rng = Random();
|
||||
|
||||
Shape randomShape(Vector2 position) {
|
||||
final shapeType = Shapes.values[_rng.nextInt(Shapes.values.length)];
|
||||
const size = 50.0;
|
||||
switch (shapeType) {
|
||||
case Shapes.circle:
|
||||
return Circle(radius: size / 2, position: position);
|
||||
case Shapes.rectangle:
|
||||
return Rectangle(
|
||||
position: position,
|
||||
size: Vector2.all(size),
|
||||
angle: _rng.nextDouble() * 6,
|
||||
);
|
||||
case Shapes.polygon:
|
||||
final points = [
|
||||
Vector2.random(_rng),
|
||||
Vector2.random(_rng)..y *= -1,
|
||||
-Vector2.random(_rng),
|
||||
Vector2.random(_rng)..x *= -1,
|
||||
];
|
||||
return Polygon.fromDefinition(
|
||||
points,
|
||||
position: position,
|
||||
size: Vector2.all(size),
|
||||
angle: _rng.nextDouble() * 6,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapDown(int pointerId, TapDownInfo info) {
|
||||
super.onTapDown(pointerId, info);
|
||||
final tapDownPoint = info.eventPosition.game;
|
||||
final component = MyShapeComponent(randomShape(tapDownPoint), shapePaint);
|
||||
add(component);
|
||||
}
|
||||
}
|
||||
|
||||
class MyShapeComponent extends ShapeComponent with Tappable {
|
||||
MyShapeComponent(Shape shape, Paint shapePaint)
|
||||
: super(shape, paint: shapePaint);
|
||||
|
||||
@override
|
||||
bool onTapDown(TapDownInfo info) {
|
||||
removeFromParent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
105
examples/lib/stories/collision_detection/simple_shapes.dart
Normal file
105
examples/lib/stories/collision_detection/simple_shapes.dart
Normal file
@ -0,0 +1,105 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
|
||||
enum Shapes { circle, rectangle, polygon }
|
||||
|
||||
class SimpleShapesExample extends FlameGame with HasTappableComponents {
|
||||
static const description = '''
|
||||
An example which adds random shapes on the screen when you tap it, if you
|
||||
tap on an already existing shape it will remove that shape and replace it
|
||||
with a new one.
|
||||
''';
|
||||
final _rng = Random();
|
||||
|
||||
MyShapeComponent randomShape(Vector2 position) {
|
||||
final shapeType = Shapes.values[_rng.nextInt(Shapes.values.length)];
|
||||
final shapeSize =
|
||||
Vector2.all(100) + Vector2.all(50.0).scaled(_rng.nextDouble());
|
||||
final shapeAngle = _rng.nextDouble() * 6;
|
||||
switch (shapeType) {
|
||||
case Shapes.circle:
|
||||
return MyShapeComponent(
|
||||
HitboxCircle(),
|
||||
position: position,
|
||||
size: shapeSize,
|
||||
angle: shapeAngle,
|
||||
);
|
||||
case Shapes.rectangle:
|
||||
return MyShapeComponent(
|
||||
HitboxRectangle(),
|
||||
position: position,
|
||||
size: shapeSize,
|
||||
angle: shapeAngle,
|
||||
);
|
||||
case Shapes.polygon:
|
||||
final points = [
|
||||
Vector2.random(_rng),
|
||||
Vector2.random(_rng)..y *= -1,
|
||||
-Vector2.random(_rng),
|
||||
Vector2.random(_rng)..x *= -1,
|
||||
];
|
||||
return MyShapeComponent(
|
||||
HitboxPolygon(points),
|
||||
position: position,
|
||||
size: shapeSize,
|
||||
angle: shapeAngle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapDown(int pointerId, TapDownInfo info) {
|
||||
super.onTapDown(pointerId, info);
|
||||
final tapPosition = info.eventPosition.game;
|
||||
final component = randomShape(tapPosition);
|
||||
add(component);
|
||||
component.add(
|
||||
MoveEffect(
|
||||
path: [size / 2],
|
||||
speed: 30,
|
||||
isAlternating: true,
|
||||
isInfinite: true,
|
||||
),
|
||||
);
|
||||
component.add(
|
||||
RotateEffect(
|
||||
angle: 3,
|
||||
speed: 0.4,
|
||||
isAlternating: true,
|
||||
isInfinite: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyShapeComponent extends ShapeComponent with Tappable {
|
||||
@override
|
||||
final Paint paint = BasicPalette.red.paint()..style = PaintingStyle.stroke;
|
||||
|
||||
MyShapeComponent(
|
||||
HitboxShape shape, {
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
double? angle,
|
||||
}) : super(
|
||||
shape,
|
||||
position: position,
|
||||
size: size,
|
||||
angle: angle,
|
||||
anchor: Anchor.center,
|
||||
);
|
||||
|
||||
@override
|
||||
bool onTapDown(TapDownInfo _) {
|
||||
removeFromParent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user