Files
flame/examples/lib/stories/input/gesture_hitboxes_example.dart
Lukas Klingsbo b5bdf4ec17 feat!: The HasTappableComponents mixin is no longer needed (#2450)
This PR is the second in a series of refactors that aim to simplify event handling in Flame. The approach is as follows:

    Added the MultiTapDispatcher component, which contains the logic that used to be within the HasTappableComponents mixin. This component is internal; it mounts to a FlameGame directly, and ensures that it is a singleton.
    Whenever any TapCallbacks component is added to a game, it automatically adds the MultiTapDispatcher component (unless there is already one), which in turn registers a tap gesture detector with GestureDetectorBuilder and rebuilds the game widget.

The end result is that now in order to make a component tappable you only need to add the TapCallbacks mixin to that component, everything else will be handled by the framework.

Consequently, the HasTappableComponents mixin is now empty and marked as deprecated.
2023-04-02 16:52:57 +00:00

100 lines
2.6 KiB
Dart

import 'dart:math';
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame/experimental.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
enum Shapes { circle, rectangle, polygon }
class GestureHitboxesExample extends FlameGame
with TapCallbacks, HasHoverables {
static const description = '''
Tap to create a PositionComponent with a randomly shaped hitbox.
You can then hover over to shapes to see that they receive the hover events
only when the cursor is within the shape. If you tap/click within the shape
it is removed.
''';
final _rng = Random();
PositionComponent 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;
final hitbox = () {
switch (shapeType) {
case Shapes.circle:
return CircleHitbox();
case Shapes.rectangle:
return RectangleHitbox();
case Shapes.polygon:
final points = [
-Vector2.random(_rng),
Vector2.random(_rng)..x *= -1,
Vector2.random(_rng),
Vector2.random(_rng)..y *= -1,
];
return PolygonHitbox.relative(points, parentSize: shapeSize);
}
}();
return MyShapeComponent(
hitbox: hitbox,
position: position,
size: shapeSize,
angle: shapeAngle,
);
}
@override
void onTapDown(TapDownEvent info) {
super.onTapDown(info);
final tapPosition = info.localPosition;
final component = randomShape(tapPosition);
add(component);
}
}
class MyShapeComponent extends PositionComponent
with TapCallbacks, Hoverable, GestureHitboxes {
final ShapeHitbox hitbox;
late final Color baseColor;
late final Color hoverColor;
MyShapeComponent({
required this.hitbox,
super.position,
super.size,
super.angle,
}) : super(anchor: Anchor.center);
@override
Future<void> onLoad() async {
super.onLoad();
baseColor = ColorExtension.random(withAlpha: 0.8, base: 100);
hitbox.paint.color = baseColor;
hitbox.renderShape = true;
add(hitbox);
}
@override
void onTapDown(TapDownEvent _) {
removeFromParent();
}
@override
bool onHoverEnter(PointerHoverInfo info) {
hitbox.paint.color = hitbox.paint.color.darken(0.5);
return true;
}
@override
bool onHoverLeave(PointerHoverInfo info) {
hitbox.paint.color = baseColor;
return true;
}
}