4个版本
0.5.2 | 2024年8月11日 |
---|---|
0.5.1 | 2024年7月4日 |
0.5.0 | 2024年6月12日 |
0.4.0 | 2024年3月29日 |
#769 在 游戏开发
305 每月下载量
在 3 个包中使用 (通过 landmass)
125KB
3K SLoC
dodgy_2d
一个Rust包,用于计算智能体(特别是ORCA)的局部避碰。
为什么是局部避碰?
在视频游戏中,角色通常需要找到路径来在游戏世界中导航。一旦完成,就需要遵循路径。问题出现在角色开始互相阻碍时。由于路径通常不会在每一帧重新生成,因此无法考虑其他角色。局部避碰在高度密集的情况下为角色提供廉价的避碰。
哪种局部避碰?
存在多种局部避碰算法。此包实现了ORCA。
此包基本上是将RVO2移植到Rust。已经做出了一些更改:编写了测试,代码注释更多,并且公开API更灵活。
示例
此示例使用“原始”API。
use std::borrow::Cow;
use dodgy_2d::{Agent, AvoidanceOptions, Obstacle, Vec2};
let mut agents: Vec<Cow<'static, Agent>> = vec![
Cow::Owned(Agent {
position: Vec2::ZERO,
velocity: Vec2::ZERO,
radius: 1.0,
avoidance_responsibility: 1.0,
}),
// Add more agents here.
];
let goal_points = vec![
Vec2::new(50.0, 0.0),
// Add goal points for every agent.
];
let obstacles: Vec<Cow<'static, Obstacle>> = vec![
Cow::Owned(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())
.map(|agent| agent.clone())
.collect::<Vec<Cow<'_, Agent>>>();
let nearby_obstacles = obstacles
.iter()
.map(|obstacle| obstacle.clone())
.collect::<Vec<Cow<'_, Obstacle>>>();
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,
&nearby_obstacles,
preferred_velocity,
agent_max_speed,
delta_seconds,
&AvoidanceOptions {
obstacle_margin: 0.1,
time_horizon,
obstacle_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_2d::{
Agent, AvoidanceOptions, AgentParameters, Obstacle, Simulator, Vec2
};
let mut simulator = Simulator::new();
simulator.add_agent(Agent {
position: Vec2::ZERO,
velocity: Vec2::ZERO,
radius: 1.0,
avoidance_responsibility: 1.0,
}, AgentParameters {
goal_point: Vec2::new(50.0, 0.0),
max_speed: 5.0,
obstacle_margin: dodgy_2d::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更灵活且更受欢迎。
许可证
以下许可证之一
- Apache License,版本2.0 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
由您选择。
贡献
除非您明确声明,否则任何有意提交以包含在您的工作中的贡献,根据Apache-2.0许可证定义,应按上述方式双授权,而无需任何附加条款或条件。
署名
dodgy_2d 包含从RVO2移植的代码。请参阅original_license.txt。
依赖关系
~3.5MB
~109K SLoC