# landmass
A Rust crate to provide a navigation system for video game characters to walk
around levels.
## What is a navigation system?
A navigation system is essentially the collection of tools needed for robust
agent movement in video games. This generally involves 4 things:
- Path finding (e.g. A-star)
- Path simplification (e.g. SSFA)
- Steering (e.g. boids)
- Local collision avoidance
In addition, managing agents and the navigation meshes they walk on can be
cumbersome, so a navigation system ideally will handle that for you.
Generally it is difficult to find a full, free system to handle all of these for
you, and the goal is for `landmass` to work relatively easily with other
languages so it can be used anywhere.
## Overview
`landmass` has three major components: `Archipelago`s, `Island`s and `Agent`s.
An `Archipelago` is composed of several `Island`s, as well as the `Agent`s that
travel across those `Island`s. Each `Island` holds a single
[navigation mesh](https://en.wikipedia.org/wiki/Navigation_mesh). Each game
character (controlled by AI) should correspond to one `Agent`. To start using
`landmass`:
1. Create an `Archipelago`.
2. Create an `Island`.
3. Assign a `ValidNavigationMesh` to the `Island`.
2. Add `Agent`s to the `Archipelago`.
Each frame of the game:
1. Set the position and velocity of each game character to its corresponding
`Agent`.
2. Call `update` on the `Archipelago`.
3. Use the desired move from each `Agent` to inform the corresponding game
character where it should move.
Note: `landmass` intentionally does not update the `Agent`s position itself.
Generally, characters are moved using some other method (like a physics
simulation) rather than just moving the character, so moving the `Agent` would
be confusing.
## Example
```rust
use glam::Vec3;
use landmass::*;
use std::sync::Arc;
let mut archipelago = Archipelago::new();
let nav_mesh = NavigationMesh {
mesh_bounds: None,
vertices: vec![
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(15.0, 0.0, 0.0),
Vec3::new(15.0, 0.0, 15.0),
Vec3::new(0.0, 0.0, 15.0),
],
polygons: vec![vec![0, 1, 2, 3]],
};
let valid_nav_mesh = Arc::new(
nav_mesh.validate().expect("Validation succeeds")
);
let island_id = archipelago.add_island();
archipelago
.get_island_mut(island_id)
.set_nav_mesh(
Transform { translation: Vec3::ZERO, rotation: 0.0 },
valid_nav_mesh,
);
let agent_1 = archipelago.add_agent({
let mut agent = Agent::create(
/* position= */ Vec3::new(1.0, 0.0, 1.0),
/* velocity= */ Vec3::ZERO,
/* radius= */ 1.0,
/* max_velocity= */ 1.0,
);
agent.current_target = Some(Vec3::new(11.0, 0.0, 1.1));
agent.target_reached_condition = TargetReachedCondition::Distance(0.01);
agent
});
let agent_2 = archipelago.add_agent({
let mut agent = Agent::create(
/* position= */ Vec3::new(11.0, 0.0, 1.1),
/* velocity= */ Vec3::ZERO,
/* radius= */ 1.0,
/* max_velocity= */ 1.0,
);
agent.current_target = Some(Vec3::new(1.0, 0.0, 1.0));
agent.target_reached_condition = TargetReachedCondition::Distance(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);
agent.velocity = agent.get_desired_velocity();
agent.position += agent.velocity * delta_time;
}
}
assert!(archipelago
.get_agent(agent_1)
.position
.abs_diff_eq(Vec3::new(11.0, 0.0, 1.1), 0.1));
assert!(archipelago
.get_agent(agent_2)
.position
.abs_diff_eq(Vec3::new(1.0, 0.0, 1.0), 0.1));
```
## License
License under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.