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
更多关于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
接口要求你实现 getNeighbors
和 getCostTo
方法。
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.');
}
}