Flutter路径查找与优化插件astar_dart的使用

Flutter路径查找与优化插件astar_dart的使用

astar_dart

astar_dart 是一个用于在网格上进行路径查找的插件。它实现了A*算法,可以用于游戏开发、地图导航等场景。

安装

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  astar_dart: ^版本号

然后运行 flutter pub get 来安装依赖。

示例代码

以下是一个完整的示例代码,展示了如何使用 astar_dart 插件来实现路径查找功能。

// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:math';

import 'package:astar_dart/astar_dart.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '欢迎来到Astar示例',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const GridExample(),
    );
  }
}

class GridExample extends StatefulWidget {
  const GridExample({super.key});

  [@override](/user/override)
  State<GridExample> createState() => _GridExampleState();
}

class _GridExampleState extends State<GridExample> {
  late ValueNotifier<bool> updater;
  IconData lastTapped = Icons.notifications;
  late final Array2d<Floor> array2d;

  [@override](/user/override)
  void initState() {
    super.initState();
    updater = ValueNotifier(false);
    array2d = Array2d<Floor>(10, 10, valueBuilder: (x, y) {
      if ((x >= 1 && x < 4 && y == 1) ||
          (x == 1 && y == 2) ||
          (x >= 1 && x < 4 && y == 3)) {
        return Floor(
          x: x,
          y: y,
          ground: GroundType.water,
          target: Target.none,
        );
      } else if (x >= 4 && x < 8 && y >= 5 && y < 9) {
        return Floor(
          x: x,
          y: y,
          ground: GroundType.forest,
          target: Target.none,
        );
      } else if (x >= 2 && x < 4 && y >= 4 && y < 6) {
        return Floor(
          x: x,
          y: y,
          ground: GroundType.barrier,
          target: Target.none,
        );
      } else if (x == 0 && y == 0) {
        return Floor(
          x: x,
          y: y,
          ground: GroundType.field,
          target: Target.player,
        );
      } else {
        return Floor(
          x: x,
          y: y,
          ground: GroundType.field,
          target: Target.none,
        );
      }
    });
  }

  Future<void> _updateFloor(Floor floor) async {
    debugPrint('updateFloor target x:${floor.x}, y: ${floor.y}');
    final astar = AStarManhattan(
      rows: 10,
      columns: 10,
      gridBuilder: (x, y) {
        final floor = array2d[x][y];
        return ANode(
            x: x,
            y: y,
            neighbors: [],
            weight: switch (floor.ground) {
              GroundType.field => 1,
              GroundType.water => 7,
              GroundType.forest => 10,
              GroundType.barrier => 1,
            },
            barrier: switch (floor.ground) {
              GroundType.field ||
              GroundType.water ||
              GroundType.forest =>
                Barrier.pass,
              GroundType.barrier => Barrier.block,
            });
      },
    );
    astar.addNeighbors();

    final path = await astar
        .findPath(start: (x: 0, y: 0), end: (x: floor.x, y: floor.y));
    array2d.forEach((floor, x, y) => floor
      ..isPath = false
      ..update());
    for (var p in path) {
      array2d[p.x][p.y]
        ..isPath = true
        ..update();
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: TextStyle(color: Colors.black, fontWeight: FontWeight.w700),
      child: Center(
        child: SingleChildScrollView(
          scrollDirection: Axis.vertical,
          child: Padding(
            padding: const EdgeInsets.symmetric(vertical: 100.0),
            child: ConstrainedBox(
              constraints:
                  BoxConstraints.tightFor(height: 10 * 100, width: 10 * 100),
              child: Flow(
                delegate: MyFlowDelegate(updater: updater),
                children: [
                  for (var x = 0; x < 10; x++)
                    for (var y = 0; y < 10; y++)
                      FloorItemWidget(
                        floor: array2d[x][y],
                        onTap: (floor) async {
                          await _updateFloor(floor);
                        },
                      )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class MyFlowDelegate extends FlowDelegate {
  MyFlowDelegate({required this.updater})
      : super(
          repaint: updater,
        );

  final ValueNotifier<bool> updater;

  [@override](/user/override)
  bool shouldRepaint(MyFlowDelegate oldDelegate) {
    return updater != oldDelegate.updater;
  }

  [@override](/user/override)
  void paintChildren(FlowPaintingContext context) {
    final startPoint = Size(0, 0);
    for (int i = 0; i < context.childCount; ++i) {
      final y = i % 10;
      final x = i ~/ 10;

      context.paintChild(
        i,
        transform: Matrix4.translationValues(-startPoint.width + (x * 100.0),
            -startPoint.height + (y * 100.0), 0),
      );
    }
  }
}

class FloorItemWidget extends StatelessWidget {
  const FloorItemWidget({
    super.key,
    required this.floor,
    required this.onTap,
  });
  final Floor floor;
  final Function(Floor floor) onTap;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: floor,
      builder: (context, _) {
        return Material(
          type: MaterialType.transparency,
          child: ConstrainedBox(
            constraints: BoxConstraints(
                maxHeight: 100, minHeight: 100, maxWidth: 100, minWidth: 100),
            child: Ink(
              decoration: BoxDecoration(
                  color: switch (floor.ground) {
                    _ when floor.isPath => Colors.grey,
                    GroundType.field => Colors.green[200],
                    GroundType.water => Colors.cyanAccent,
                    GroundType.forest => Colors.green[700],
                    GroundType.barrier => Colors.red[700],
                  },
                  border: Border.all(color: Colors.black, width: 3.0)),
              child: InkWell(
                onTap: () {
                  onTap(floor);
                },
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text('x: ${floor.x}, y: ${floor.y}'),
                        SizedBox(width: 32),
                        Icon(
                          switch (floor.ground) {
                            GroundType.field => Icons.grass,
                            GroundType.water => Icons.water,
                            GroundType.forest => Icons.forest,
                            GroundType.barrier => Icons.block,
                          },
                          color: Colors.black,
                          size: 20.0,
                        ),
                        SizedBox(width: 32),
                        Text(
                          "weight: ${switch (floor.ground) {
                            GroundType.field => '1',
                            GroundType.water => '7',
                            GroundType.forest => '10',
                            GroundType.barrier => 'x',
                          }}",
                        )
                      ],
                    ),
                    SizedBox(height: 32),
                    Text(
                      switch (floor.target) {
                        Target.player => 'PLAYER',
                        Target.enemy => 'ENEMY',
                        Target.none => '',
                      },
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}

// --------------------- 创建世界 --------------------------
enum GroundType {
  field,
  water,
  forest,
  barrier,
}

enum Target {
  player,
  enemy,
  none,
}

class Floor with ChangeNotifier {
  Floor({
    required int x,
    required int y,
    required Target target,
    required GroundType ground,
    bool isPath = false,
  }) {
    _isPath = isPath;
    _ground = ground;
    _target = target;
    _x = x;
    _y = y;
  }
  late int _x;
  set x(int value) {
    _x = value;
  }

  int get x => _x;

  late int _y;
  set y(int value) {
    _y = value;
  }

  int get y => _y;

  late bool _isPath;
  set isPath(bool value) {
    _isPath = value;
  }

  late GroundType _ground;
  set ground(GroundType value) {
    _ground = value;
  }

  GroundType get ground => _ground;

  late Target _target;
  set target(Target value) {
    _target = value;
  }

  void update() {
    notifyListeners();
  }

  Target get target => _target;

  bool get isPath => _isPath;

  factory Floor.wrong() => Floor(
        x: -1,
        y: -1,
        ground: GroundType.field,
        target: Target.none,
        isPath: false,
      );
}

更多关于Flutter路径查找与优化插件astar_dart的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter路径查找与优化插件astar_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


astar_dart 是一个用于在 Flutter 中实现 A* (A-star) 算法的插件。A* 算法是一种广泛使用的路径查找算法,用于在图中找到从起点到终点的最优路径。astar_dart 插件可以帮助你在 Flutter 应用中轻松实现路径查找功能。

安装 astar_dart 插件

首先,你需要在 pubspec.yaml 文件中添加 astar_dart 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  astar_dart: ^1.0.0

然后运行 flutter pub get 来获取依赖。

使用 astar_dart 插件

以下是一个简单的示例,展示如何在 Flutter 中使用 astar_dart 插件进行路径查找。

1. 导入库

首先,导入 astar_dart 库:

import 'package:astar_dart/astar_dart.dart';

2. 定义节点类

你需要定义一个节点类,该类需要实现 AStarNode 接口。AStarNode 接口要求你实现 getNeighborsgetCostTo 方法。

class MyNode extends AStarNode<MyNode> {
  final int x;
  final int y;

  MyNode(this.x, this.y);

  @override
  double getCostTo(MyNode neighbor) {
    // 计算当前节点到邻居节点的成本
    return 1.0; // 假设每个节点之间的成本为 1
  }

  @override
  Iterable<MyNode> getNeighbors() {
    // 返回当前节点的所有邻居节点
    return [
      MyNode(x + 1, y),
      MyNode(x - 1, y),
      MyNode(x, y + 1),
      MyNode(x, y - 1),
    ];
  }

  @override
  bool operator ==(Object other) {
    return other is MyNode && x == other.x && y == other.y;
  }

  @override
  int get hashCode => x.hashCode ^ y.hashCode;
}

3. 创建 A* 实例并查找路径

现在你可以创建一个 AStar 实例,并使用它来查找路径。

void main() {
  final startNode = MyNode(0, 0);
  final goalNode = MyNode(4, 4);

  final aStar = AStar<MyNode>();

  final path = aStar.findPath(startNode, goalNode);

  if (path != null) {
    print('Path found:');
    for (var node in path) {
      print('(${node.x}, ${node.y})');
    }
  } else {
    print('No path found.');
  }
}
回到顶部