8个版本
0.8.7 | 2022年10月4日 |
---|---|
0.8.4 | 2021年2月15日 |
0.8.1 | 2020年4月29日 |
0.7.0 | 2020年2月22日 |
0.1.0 | 2020年2月21日 |
#592 in 游戏开发
150KB
3K SLoC
bracket-pathfinding
此crate是整体bracket-lib
系统的一部分,并且(与bracket-algorithm-traits
一起)提供路径查找功能。支持A*(A*)和迪杰斯特拉。它还提供了视野(FOV)功能。
特性行为实现
至少,您需要为您地图定义get_available_exits
和get_pathing_distance
(从BaseMap
特性行为)。对于视野,需要is_opaque
。这些来自bracket-algorithm-traits
crate,但作为prelude
的一部分公开,以方便使用。这些还可以通过实现Algorithm2D
(来自同一crate)来受益。这些用于为库提供您的地图格式的接口:库非常努力地不去对您如何存储地图数据提出意见。
大多数Algorithm2D
可以通过只提供维度来推导,如果您对bracket-lib
提供的简单对齐满意。
impl Algorithm2D for Map {
fn dimensions(&self) -> Point {
Point::new(MAP_WIDTH, MAP_HEIGHT)
}
}
对于视野,您需要指明一个瓦片是否会阻挡视线。例如
impl BaseMap for Map {
fn is_opaque(&self, idx: usize) -> bool {
self.tiles[idx as usize] == '#' // Change this to your map definition!
}
}
迪杰斯特拉和A*需要知道从一个瓦片出发的有效出口,以及移动到该瓦片的“成本”(大多数情况下您可以使用1.0
)。例如
impl BaseMap for Map {
fn is_opaque(&self, idx: usize) -> bool {
self.tiles[idx as usize] == '#'
}
fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> {
let mut exits = SmallVec::new();
let location = self.index_to_point2d(idx);
if let Some(idx) = self.valid_exit(location, Point::new(-1, 0)) {
exits.push((idx, 1.0))
}
if let Some(idx) = self.valid_exit(location, Point::new(1, 0)) {
exits.push((idx, 1.0))
}
if let Some(idx) = self.valid_exit(location, Point::new(0, -1)) {
exits.push((idx, 1.0))
}
if let Some(idx) = self.valid_exit(location, Point::new(0, 1)) {
exits.push((idx, 1.0))
}
exits
}
}
A*还需要知道给定瓦片的目标距离。如果您不提供有用的数字,这将成为一个非常低效的搜索。您可以通过使用毕达哥拉斯、曼哈顿或其他算法来指定距离启发式函数来改变搜索的行为。例如
impl BaseMap for Map {
fn get_pathing_distance(&self, idx1: usize, idx2: usize) -> f32 {
DistanceAlg::Pythagoras.distance2d(
self.index_to_point2d(idx1),
self.index_to_point2d(idx2)
)
}
}
A* (A*) 路径查找
Bracket-lib包括一个高性能的A*系统。它使用二叉堆来优化打开/关闭存储。默认情况下,它会在65,536次迭代后取消搜索。
实际上执行A*搜索非常简单
let path = a_star_search(
map.point2d_to_index(START_POINT),
map.point2d_to_index(END_POINT),
&map
);
if path.success {
// Do something with it! path.steps has the whole path.
}
示例astar
演示了这一点。
迪杰斯特拉映射
Bracket-lib 还包含迪杰斯特拉图,可以包含你想要的任何数量的搜索目标。参见 迪杰斯特拉图的无穷力量 了解你可以用它做什么。
要生成迪杰斯特拉图,你需要一个目标瓦片索引向量。然后你可以创建这张地图
let mut search_targets : Vec<usize> = Vec::new();
search_targets.push(map.point2d_to_index(START_POINT));
search_targets.push(map.point2d_to_index(END_POINT));
let flow_map = DijkstraMap::new(MAP_WIDTH, MAP_HEIGHT, &search_targets, &map, 1024.0);
一旦你有了地图,你可以在 flow_map.map
访问单个距离 - 或者你可以使用各种辅助函数,如 find_highest_exist
和 find_lowest_exit
来帮助路径查找。
示例 dijkstra
展示了这一点。
视野(目前仅限于2D)
如果你为你的 BaseMap
特性定义了 is_opaque
,获取所有可见瓦片的集合就很容易了
let fov = field_of_view_set(START_POINT, 6, &map);
你可以在示例 fov
中看到这一点。
功能标志
如果你启用了 threaded
功能,一些迪杰斯特拉函数将使用多线程算法。
示例
有三个示例(忽略 common.rs
- 它是共享代码)
astar
(运行cargo run --example astar
),演示在随机地图上执行 A-Star 路径搜索。dijkstra
(运行cargo run --example dijkstra
),演示对两个目标进行迪杰斯特拉映射。fov
(运行cargo run --example fov
),演示视野生成。
这些使用 crossterm
将渲染到你的终端。
依赖项
~2.5MB
~52K SLoC