パスファインディング

2023-08-28

PathFinding.dart

PathFinding.dartパッケージを使うとマップないの 2 点間の道順を計算することができます。 これは Flame の機能ではありませんが、ゲームでよく使われると思うので紹介します。 (タップした場所まで障害物を避けつつ移動する場合など)

Gridでマップを準備して、AStarFinderJumpPointFinderでパスを計算します。

公式ドキュメント https://pub.dev/packages/pathfinding
APIリファレンス https://pub.dev/documentation/pathfinding/latest/

動かす

まずpubspec.yamlpathfindingパッケージを追加します。

  pathfinding: ^3.0.1

あとはコードを書いていきます。

横 10 マス、縦 10 マスのマップ(_cells)を作ります。初期値は全部道である 0 で埋めています。

  static const _rows = 20;
  static const _cols = 10;
  final _cells = List<List<int>>.generate(
    _rows,
    (_) => List<int>.generate(_cols, (_) => 0),
  );
  final _path = <Vector2>[];

ランダムで壁を作るために 1 にします。 また壁以外の点からランダムでスタート、ゴールを選びます。

    for (var i = 0; i < _rows; i++) {
      for (var j = 0; j < _cols; j++) {
        if (_random.nextInt(10) == 0) {
          _cells[i][j] = 1;
        }
      }
    }

    Vector2? start;
    Vector2? goal;
    while (start == null || goal == null) {
      final i = _random.nextInt(_rows);
      final j = _random.nextInt(_cols);
      if (_cells[i][j] == 0) {
        if (start == null) {
          start = Vector2(j.toDouble(), i.toDouble());
        } else {
          goal = Vector2(j.toDouble(), i.toDouble());
        }
      }
    }

次が pathfinding の本番です。 用意したマップ、スタート、ゴールからパスを見つけます。

    final grid = Grid(_cols, _rows, _cells);
    final finder = AStarFinder();
    final found = finder.findPath(
      start.x.toInt(),
      start.y.toInt(),
      goal.x.toInt(),
      goal.y.toInt(),
      grid,
    );
    for (final point in found) {
      _path.add(Vector2(point[0], point[1]));
    }

最後に render で画面に表示します。 道は白、壁はグレー、スタートは緑、ゴールは赤、パスを青で描画します。

  void render(Canvas canvas) {
    super.render(canvas);

    final cellSize = Vector2.all(20);
    for (var i = 0; i < _rows; i++) {
      for (var j = 0; j < _cols; j++) {
        canvas.drawRect(
          Rect.fromLTWH(
            (j + 1) * cellSize.x,
            (i + 1) * cellSize.y,
            cellSize.x,
            cellSize.y,
          ),
          Paint()..color = _cells[i][j] == 0 ? Colors.white : Colors.grey,
        );
      }
    }

    for (var i = 0; i < _path.length; i++) {
      final point = _path[i];
      final color = i == 0
          ? Colors.green
          : i == _path.length - 1
              ? Colors.red
              : Colors.blue;

      canvas.drawRect(
        Rect.fromLTWH(
          (point.x + 1) * cellSize.x,
          (point.y + 1) * cellSize.y,
          cellSize.x,
          cellSize.y,
        ),
        Paint()..color = color,
      );
    }
  }

このようにしてパスファインディングを実装できます。

© 2023 tnantoka