docs

TOP(About this memo)) > Flutter > 一覧(Flame) > 衝突

衝突検出

import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flame/palette.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(
    SafeArea(child: GameWidget(game: Example())),
  );
}

class Example extends FlameGame with HasCollisionDetection {
  Example() {
    debugMode = true;
  }

  static final _borderPaint = Paint()
    ..style = PaintingStyle.stroke
    ..strokeWidth = 2
    ..color = BasicPalette.red.color;

  static final TextPaint textRenderer = TextPaint(
    style: const TextStyle(color: Colors.white70, fontSize: 12),
  );

  @override
  Future<void> onLoad() async {
    add(ScreenHitbox());
    add(Ember(position: Vector2(size.x / 2, size.y / 2)));
    add(Rock(Vector2(size.x / 2, size.y / 2)));

    final text = TextComponent(
      textRenderer: textRenderer,
      position: Vector2(0, 0),
      anchor: Anchor.topLeft,
    );
    text.text = "viewport: $size";
    add(text);
  }

  @override
  void render(Canvas canvas) {
    canvas.drawRect(size.toRect(), _borderPaint);
    super.render(canvas);
  }
}

class Ember extends SpriteAnimationComponent
    with HasGameReference, CollisionCallbacks {
  Ember({super.position, super.key})
      : super(
          priority: 2,
          size: Vector2.all(50),
          anchor: Anchor.center,
        );
  late final maxPosition =
      Vector2(game.size.x - size.x / 2, game.size.y - size.y / 2);
  late final minPosition = Vector2(size.x / 2, size.y / 2);
  final Vector2 velocity = Vector2(10, 10);

  @override
  Future<void> onLoad() async {
    animation = await game.loadSpriteAnimation(
      'animations/ember.png',
      SpriteAnimationData.sequenced(
        amount: 3,
        textureSize: Vector2.all(16),
        stepTime: 0.15,
      ),
    );
    add(CircleHitbox());
  }

  @override
  void onCollisionStart(
    Set<Vector2> intersectionPoints,
    PositionComponent other,
  ) {
    super.onCollisionStart(intersectionPoints, other);
    if (other is Rock) {
      other.add(
        ScaleEffect.to(
          Vector2.all(0.5),
          EffectController(duration: 0.2, alternate: true),
        ),
      );
    }

    if (other is ScreenHitbox) {
      final collisionPoint = intersectionPoints.first;
      // Left Side Collision
      if (collisionPoint.x == 0) {
        velocity.x = -velocity.x;
        velocity.y = velocity.y;
      }
      // Right Side Collision
      if (collisionPoint.x == game.size.x) {
        velocity.x = -velocity.x;
        velocity.y = velocity.y;
      }
      // Top Side Collision
      if (collisionPoint.y == 0) {
        velocity.x = velocity.x;
        velocity.y = -velocity.y;
      }
      // Bottom Side Collision
      if (collisionPoint.y == game.size.y) {
        velocity.x = velocity.x;
        velocity.y = -velocity.y;
      }

      angle = -velocity.angleToSigned(Vector2(1, 0));
    }
  }

  @override
  void update(double dt) {
    super.update(dt);
    final deltaPosition = velocity * (10 * dt);
    position.add(deltaPosition);
    position.clamp(minPosition, maxPosition);
  }
}

class Rock extends SpriteComponent with HasGameRef, TapCallbacks {
  Rock(Vector2 position)
      : super(
          position: position,
          size: Vector2.all(50),
          priority: 1,
          anchor: Anchor.center,
        );

  @override
  Future<void> onLoad() async {
    sprite = await game.loadSprite('nine-box.png');
    //add(RectangleHitbox());
    add(PolygonHitbox.relative(
      [
        Vector2(0, -5.0),
        Vector2(-6, 0),
        Vector2(0, 5),
        Vector2(1, 3),
        Vector2(1, -3),
      ],
      parentSize: size,
    ));
  }
}