物理演算

2023-05-03

forge2d

物理エンジン Box2D を Dart に移植したforge2dによって物理演算を行うことができます。 Flame 向けにはflame_forge2dが用意されています。

FlameGameの代わりにForge2dGameを使うことで forge2d の機能を簡単に使うことができます。

公式ドキュメント https://docs.flame-engine.org/latest/bridge_packages/flame_forge2d/forge2d.html#forge2d
APIリファレンス https://pub.dev/documentation/flame_forge2d/latest/forge2d_game/Forge2DGame-class.html

動かしてみる

左右と下に壁を作り、タップした場所にボールを生成するサンプルを作ってみます。

まずはflame_forge2dを導入します。

  flame_forge2d: ^0.16.0+4

壁クラスを用意します。BodyComponentを継承して作ります。

class Wall extends BodyComponent {
  Wall({required this.pos, required this.size})
      : super(paint: BasicPalette.gray.paint());

  final Vector2 pos;
  final Vector2 size;

  @override
  Body createBody() {
    final shape = PolygonShape()..setAsBox(size.x, size.y, pos, 0);
    final fixtureDef = FixtureDef(shape, friction: 0.3);
    final bodyDef = BodyDef(userData: this);
    return world.createBody(bodyDef)..createFixture(fixtureDef);
  }
}

ボールクラスを用意します。 回転がわかりやすいように線を引いています。

class Ball extends BodyComponent {
  Ball({required this.pos});

  final Vector2 pos;

  @override
  Body createBody() {
    final shape = CircleShape()..radius = 2;

    final fixtureDef = FixtureDef(
      shape,
      restitution: 0.8,
      density: 1.0,
      friction: 0.4,
    );

    final bodyDef = BodyDef(
      userData: this,
      position: pos,
      type: BodyType.dynamic,
    );

    return world.createBody(bodyDef)..createFixture(fixtureDef);
  }

  @override
  void renderCircle(Canvas canvas, Offset center, double radius) {
    super.renderCircle(canvas, center, radius);

    canvas.drawLine(
      center,
      center + Offset(0, radius),
      BasicPalette.black.paint(),
    );
  }
}

Forge2dGameを継承したゲームクラスで、壁やボールを生成します。

class PhysicsGame extends Forge2DGame with TapCallbacks {
  @override
  Future<void> onLoad() async {
    super.onLoad();

    final rect = camera.visibleWorldRect;

    await world.add(
      Wall(pos: rect.bottomLeft.toVector2(), size: Vector2(rect.width, 1)),
    );
    await world.add(
      Wall(pos: rect.topLeft.toVector2(), size: Vector2(1, rect.height)),
    );
    await world.add(
      Wall(pos: rect.topRight.toVector2(), size: Vector2(1, rect.height)),
    );
  }

  @override
  void onTapDown(TapDownEvent event) {
    super.onTapDown(event);

    world.add(
      Ball(pos: screenToWorld(event.localPosition)),
    );
  }
}

これで物理演算が動くようになりました。

© 2023 tnantoka