#碰撞 #orca #navigation #avoidance #rvo

dodgy_3d

ORCA的实现,一个用于3D的局部避障算法

3个不稳定版本

0.5.1 2024年8月11日
0.5.0 2024年6月12日
0.4.0 2024年3月29日

#1365游戏开发

Download history 164/week @ 2024-06-10 3/week @ 2024-06-17 3/week @ 2024-07-29 50/week @ 2024-08-05 77/week @ 2024-08-12

每月 130次下载

MIT/Apache

62KB
1.5K SLoC

dodgy_3d

一个Rust包,用于计算智能体的局部避障(特别是ORCA)。

为什么是局部避障?

在视频游戏中,角色通常需要找到路径在游戏世界中导航。一旦完成,就需要遵循这条路径。问题出现在角色开始相互阻挡时。由于路径通常不会在每一帧都重新生成,因此无法考虑其他角色。局部避障即使在高密度情况下也能为角色提供廉价的避障。

哪种局部避障?

存在几种局部避障算法。此包实现了ORCA

此包基本上是将RVO2-3D移植到Rust。已经做出了几个更改:编写了测试,增加了代码注释,并使公共API更加灵活。

示例

此示例使用"原始"API。

use std::borrow::Cow;

use dodgy_3d::{Agent, AvoidanceOptions, Vec3};

let mut agents: Vec<Cow<'static, Agent>> = vec![
  Cow::Owned(Agent {
    position: Vec3::ZERO,
    velocity: Vec3::ZERO,
    radius: 1.0,
    avoidance_responsibility: 1.0,
  }),
  // Add more agents here.
];

let goal_points = vec![
  Vec3::new(50.0, 0.0, 0.0),
  // Add goal points for every agent.
];

let time_horizon = 3.0;

fn get_delta_seconds() -> f32 {
  // Use something that actually gets the time between frames.
  return 0.01;
}

for i in 0..100 {
  let delta_seconds = get_delta_seconds();
  if delta_seconds == 0.0 {
    // Skip frames where agents can't move anyway.
    continue;
  }

  let mut new_velocities = Vec::with_capacity(agents.len());

  for i in 0..agents.len() {
    let neighbours = agents[..i]
      .iter()
      .chain(agents[(i + 1)..].iter())
      .map(|agent| agent.clone())
      .collect::<Vec<Cow<'_, Agent>>>();

    let agent_max_speed = 5.0;
    let preferred_velocity = (goal_points[i] - agents[i].position)
      .normalize_or_zero() * agent_max_speed;

    let avoidance_velocity = agents[i].compute_avoiding_velocity(
      &neighbours,
      preferred_velocity,
      agent_max_speed,
      delta_seconds,
      &AvoidanceOptions { time_horizon },
    );
    new_velocities.push(avoidance_velocity);
  }

  for (i, agent) in agents.iter_mut().map(Cow::to_mut).enumerate() {
    agent.velocity = new_velocities[i];
    agent.position += agent.velocity * delta_seconds;
  }

  // Update rendering using new agent positions.
}

这是首选的API,因为找到邻居本质上就是空间查询。在大多数游戏引擎中,通常都会在某个范围内查找相关对象。使用此API,您可以通过常规的空间查询找到邻居,并且仅公开避障部分。

但是,使用Simulator结构体的另一种选择

use dodgy_3d::{
  Agent, AvoidanceOptions, AgentParameters, Simulator, SimulatorMargin, Vec3
};

let mut simulator = Simulator::new();
simulator.add_agent(Agent {
  position: Vec3::ZERO,
  velocity: Vec3::ZERO,
  radius: 1.0,
  avoidance_responsibility: 1.0,
}, AgentParameters {
  goal_point: Vec3::new(50.0, 0.0, 0.0),
  max_speed: 5.0,
  obstacle_margin: SimulatorMargin::Distance(0.1),
  time_horizon: 3.0,
  obstacle_time_horizon: 1.0,
});
// Add more agents.

fn get_delta_seconds() -> f32 {
  // Use something that actually gets the time between frames.
  return 0.01;
}

for i in 0..100 {
  let delta_seconds = get_delta_seconds();
  simulator.step(delta_seconds);

  // Update rendering using new agent positions (using simulator.get_agent).
}

再次强调,这不是首选的方法!这只是为少数用户快速启动和运行的一种更简单的方式。其他API更灵活且更受欢迎。

许可证

在以下许可证下

任选其一。

贡献

除非您明确声明,否则根据Apache-2.0许可证定义的您提交给作品中的任何有意贡献,都应按上述方式双授权,没有任何额外的条款或条件。

归属

dodgy_3d 包含从 RVO2 端口迁移的代码。请参阅 original_license.txt

依赖关系

~3.5MB
~109K SLoC