1 unstable release
0.1.0 | Aug 13, 2019 |
---|
#2475 in Game dev
9KB
Amethyst Navigation
Nav-mesh based 2D and 3D navigation toolset for Amethyst game engine
Table of contents
About
At one point in your game development you may want to allow your character to find a path in the world to move along it. This Amethyst plugin gives you opportunity to use nav mesh technology to build a nav mesh (special mesh that describes walkable areas), make an agent and set its destination to follow. Your destination can be either a point in world space or another agent entity.
Manifest
Cargo.toml:
[dependencies]
amethyst-navigation = "0.1"
Setup
With amethyst-navigation
in prior to work, you have to install two systems:
NavAgentMaintainSystem
to allow agents to find paths to their destinations.SimpleNavDriverSystem
to allow agents to perform simple movement along paths.
use amethyst_navigation::prelude::*;
let game_data = GameDataBuilder::default()
// ...
// INSTALL ENGINE BUNDLES HERE
// ...
// nav agent maintainment system allows agents find paths to their destinations.
.with(NavAgentMaintainSystem::default(), "nav-agent-maintain", &[])
// simple nav driver system allows agents with `SimpleNavDriverTag` to perform simple
// movement along path.
.with(SimpleNavDriverSystem, "simple-nav-driver", &[]);
Build Nav Mesh
Nav mesh is special kind of mesh that allows agents to find shortest paths to traverse from one point to another, that points always lay on given navmesh.
use amethyst_navigation::prelude::*;
// create nav mesh vertices and triangles.
// in future this will be loaded from asset.
let vertices: Vec<NavVec3> = vec![
(50.0, 50.0).into(), // 0
(500.0, 50.0).into(), // 1
(500.0, 100.0).into(), // 2
(100.0, 100.0).into(), // 3
(100.0, 300.0).into(), // 4
(700.0, 300.0).into(), // 5
(700.0, 50.0).into(), // 6
(750.0, 50.0).into(), // 7
(750.0, 550.0).into(), // 8
(50.0, 550.0).into(), // 9
];
let triangles: Vec<NavTriangle> = vec![
(1, 2, 3).into(), // 0
(0, 1, 3).into(), // 1
(0, 3, 4).into(), // 2
(0, 4, 9).into(), // 3
(4, 8, 9).into(), // 4
(4, 5, 8).into(), // 5
(5, 7, 8).into(), // 6
(5, 6, 7).into(), // 7
];
// build a nav mesh and register it so agents can traverse it.
let mesh = NavMesh::new(vertices, triangles).unwrap();
world.write_resource::<NavMeshesRes>().register(mesh);
Create an Agent
Agent is a type of entity that finds and follows path between two points on
navmesh. Each agent entity must have NavAgent
component and one driver
component. Right now there is only one driver - SimpleNavDriverTag
marks agent
to perform simple movement along path. More drivers with different behaviours
will be provided in future, but users can easly make their own drivers.
use amethyst_navigation::prelude::*;
let mut agent = NavAgent::new((x as f64, y as f64).into());
agent.speed = speed;
world
.create_entity()
// this entity is an agent.
.with(agent)
// this entity will do a simple movement along path.
.with(SimpleNavDriverTag)
.build();
Set Agent destination
use amethyst_navigation::prelude::*;
// get mesh identifier from registry.
let mesh = meshes.meshes_iter().nth(0).unwrap().id();
// set player agent destination.
agent.set_destination(
// we can also select another agent to follow.
NavAgentTarget::Point((x as f64, y as f64).into()),
// use best quality point on nav mesh query.
NavQuery::Accuracy,
// use best quality of path finding.
NavPathMode::Accuracy,
mesh,
);
Custom Agent Driver
SimpleNavDriverSystem
has the easiest movement code you can imagine. You can
make your own driver system and change agent behaviour, for example you can add
obstacle avoidance.
NOTE: Remember to create driver component tag to mark agents to use that driver.
use amethyst_navigation::prelude::*;
pub struct SimpleNavDriverSystem;
impl<'s> System<'s> for SimpleNavDriverSystem {
type SystemData = (
Read<'s, Time>,
WriteStorage<'s, NavAgent>,
ReadStorage<'s, SimpleNavDriverTag>,
);
fn run(&mut self, (time, agents, drivers): Self::SystemData) {
let delta_time = time.delta_seconds() as f64;
if delta_time <= 0.0 {
return;
}
for (agent, _) in (&mut agents, &drivers).join() {
if let Some(path) = agent.path() {
if let Some((target, _)) = NavMesh::path_target_point(
path,
agent.position,
agent.speed.max(agent.min_target_distance.max(0.0)) * delta_time,
) {
let diff = target - agent.position;
let dir = diff.normalize();
agent.position = agent.position
+ dir * (agent.speed.max(0.0) * delta_time).min(diff.magnitude());
agent.direction = diff.normalize();
}
}
}
}
}
Dependencies
~15–21MB
~322K SLoC