#碰撞 #orca #导航 #躲避 #rvo

dodgy

ORCA本地避碰算法的实现

1个不稳定版本

1.3.0 2023年12月29日
1.2.2 2023年10月11日
1.2.1 2023年6月19日
1.0.0 2023年4月2日
0.3.0 2024年1月7日

#1923游戏开发

Download history 3/week @ 2024-05-12 9/week @ 2024-05-19 20/week @ 2024-05-26 23/week @ 2024-06-02 26/week @ 2024-06-09 23/week @ 2024-06-16 15/week @ 2024-06-23 1/week @ 2024-06-30 9/week @ 2024-07-07 10/week @ 2024-07-14 23/week @ 2024-07-21 51/week @ 2024-07-28 32/week @ 2024-08-04 15/week @ 2024-08-11

121 每月下载量

MIT 许可证

125KB
3K SLoC

dodgy

一个Rust库,用于计算智能体(agent)的本地避碰(特别是ORCA)。

为什么是本地避碰?

在视频游戏中,角色通常需要找到路径在游戏世界中导航。一旦完成,就需要遵循这条路径。当角色开始互相阻碍时,问题就出现了。由于路径通常不会在每个游戏帧中重新生成,因此不能考虑其他角色。本地避碰在密集情况下也能为角色提供廉价的避碰。

哪种本地避碰?

存在多种本地避碰算法。此库实现了ORCA

此库基本上是将RVO2移植到Rust。已进行了几项更改:编写了测试,代码注释更多,并使公共API更加灵活。

示例

此示例使用“原始”API。

use dodgy::{Agent, AvoidanceOptions, Obstacle};
use glam::Vec2;

let mut agents = vec![
  Agent {
    position: Vec2::ZERO,
    velocity: Vec2::ZERO,
    radius: 1.0,
    avoidance_responsibility: 1.0,
    max_velocity: 5.0,
  },
  // Add more agents here.
];

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

let obstacles = vec![
  Obstacle::Closed{
    vertices: vec![
      Vec2::new(-1000.0, -1000.0),
      Vec2::new(-1000.0, 1000.0),
      Vec2::new(1000.0, 1000.0),
      Vec2::new(1000.0, -1000.0),
    ],
  },
  // Add more obstacles here.
];

let time_horizon = 3.0;
let obstacle_time_horizon = 1.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())
      .collect::<Vec<&Agent>>();
    let nearby_obstacles = obstacles
      .iter()
      .map(|obstacle| obstacle)
      .collect::<Vec<&Obstacle>>();

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

    let avoidance_velocity = agents[i].compute_avoiding_velocity(
      &neighbours,
      &nearby_obstacles,
      preferred_velocity,
      delta_seconds,
      &AvoidanceOptions {
        obstacle_margin: 0.1,
        time_horizon,
        obstacle_time_horizon,
      });
    new_velocities.push(avoidance_velocity);
  }

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

  // Update rendering using new agent positions.
}

这是首选的API,因为查找邻居本质上只是一个空间查询。在大多数游戏引擎中,通常都会执行在某个半径内查找相关对象的操作。使用此API,可以通过常规空间查询找到邻居,并仅公开避碰部分。

但是,使用Simulator结构体的替代方案

use dodgy::{Agent, AvoidanceOptions, AgentParameters, Obstacle, Simulator};
use glam::Vec2;

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

simulator.add_obstacle(
  Obstacle::Closed{
    vertices: vec![
      Vec2::new(-1000.0, -1000.0),
      Vec2::new(-1000.0, 1000.0),
      Vec2::new(1000.0, 1000.0),
      Vec2::new(1000.0, -1000.0),
    ],
  }
);
// Add more obstacles.

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更加灵活且首选。

许可证

根据MIT许可证许可。

归属

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

依赖关系

~3MB
~89K SLoC