#navigation #path-finding #system #avoidance #artificial-intelligence

landmass

为视频游戏角色在关卡中行走提供的导航系统

10 个版本 (5 个破坏性更新)

0.6.0 2024年7月23日
0.5.0 2024年7月6日
0.4.0 2024年2月18日
0.3.3 2024年1月13日
0.1.1 2023年6月22日

#79 in 游戏开发

Download history 8/week @ 2024-06-03 2/week @ 2024-06-10 13/week @ 2024-06-17 2/week @ 2024-06-24 177/week @ 2024-07-01 23/week @ 2024-07-08 213/week @ 2024-07-22 7/week @ 2024-07-29

每月222次下载
2 个工具箱中使用(通过 bevy_landmass

MIT/Apache

335KB
9K SLoC

landmass

一个Rust工具箱,提供视频游戏角色在关卡中行走的导航系统。

什么是导航系统?

导航系统基本上是一系列用于视频游戏中强大代理移动的工具。这通常包括4个方面

  • 路径查找(例如 A*)
  • 路径简化(例如 SSFA)
  • 转向(例如鸟群算法)
  • 局部碰撞避免

此外,管理代理和他们行走的导航网格可能很麻烦,因此导航系统最好为您处理这些。

通常很难找到一个完整的、免费的系统来处理所有这些,目标是让 landmass 能够与其他语言相对容易地工作,以便在各个地方使用。

概述

landmass 有四个主要组件:ArchipelagoIslandAgentCharacter。一个 Archipelago 由几个 Island 组成,以及在这些 Island 上旅行的 AgentCharacter。每个 Island 包含一个单独的 导航网格。每个游戏角色(由AI控制)应对应一个 Agent。玩家角色或其他非AI控制的角色应对应一个 Character。要开始使用 landmass

  1. 创建一个 Archipelago
  2. 创建一个 Island
  3. AgentCharacter 添加到 Archipelago

游戏的每一帧

  1. 将每个游戏角色的位置和速度设置为相应的 AgentCharacter
  2. Archipelago 上调用 update
  3. 使用每个 Agent 的期望移动来通知相应的游戏角色它应该移动到哪里。

注意:landmass 有意不更新 Agent 的位置。通常,角色是通过其他方法(如物理模拟)移动,而不是仅仅移动角色,因此移动 Agent 会让人困惑。

示例

use glam::Vec3;
use landmass::*;
use std::{sync::Arc, collections::HashMap};

let mut archipelago = Archipelago::<XYZ>::new();

let nav_mesh = NavigationMesh {
  vertices: vec![
    Vec3::new(0.0, 0.0, 0.0),
    Vec3::new(15.0, 0.0, 0.0),
    Vec3::new(15.0, 15.0, 0.0),
    Vec3::new(0.0, 15.0, 0.0),
  ],
  polygons: vec![vec![0, 1, 2, 3]],
  polygon_type_indices: vec![0],
};

let valid_nav_mesh = Arc::new(
  nav_mesh.validate().expect("Validation succeeds")
);

let island_id = archipelago
  .add_island(Island::new(
    Transform { translation: Vec3::ZERO, rotation: 0.0 },
    valid_nav_mesh,
    HashMap::new(),
  ));

let agent_1 = archipelago.add_agent({
  let mut agent = Agent::create(
    /* position= */ Vec3::new(1.0, 1.0, 0.0),
    /* velocity= */ Vec3::ZERO,
    /* radius= */ 1.0,
    /* desired_speed= */ 1.0,
    /* max_speed= */ 2.0,
  );
  agent.current_target = Some(Vec3::new(11.0, 1.1, 0.0));
  agent.target_reached_condition = TargetReachedCondition::Distance(Some(0.01));
  agent
});
let agent_2 = archipelago.add_agent({
  let mut agent = Agent::create(
    /* position= */ Vec3::new(11.0, 1.1, 0.0),
    /* velocity= */ Vec3::ZERO,
    /* radius= */ 1.0,
    /* desired_speed= */ 1.0,
    /* max_speed= */ 2.0,
  );
  agent.current_target = Some(Vec3::new(1.0, 1.0, 0.0));
  agent.target_reached_condition = TargetReachedCondition::Distance(Some(0.01));
  agent
});

for i in 0..200 {
  let delta_time = 1.0 / 10.0;
  archipelago.update(delta_time);

  for agent_id in archipelago.get_agent_ids().collect::<Vec<_>>() {
    let agent = archipelago.get_agent_mut(agent_id).unwrap();
    agent.velocity = *agent.get_desired_velocity();
    agent.position += agent.velocity * delta_time;
  }
}

assert!(archipelago
  .get_agent(agent_1)
  .unwrap()
  .position
  .abs_diff_eq(Vec3::new(11.0, 1.1, 0.0), 0.1));
assert!(archipelago
  .get_agent(agent_2)
  .unwrap()
  .position
  .abs_diff_eq(Vec3::new(1.0, 1.0, 0.0), 0.1));

许可证

以下任一许可证下

供您选择。

贡献

除非您明确声明,否则任何您有意提交以包含在作品中的贡献(根据 Apache-2.0 许可证定义),均应按上述方式双许可,无需任何附加条款或条件。

依赖项

约 11MB
约 228K SLoC