Skip to main content

hassium_navigation/
system.rs

1use crate::{
2    component::{NavAgent, NavAgentTarget, SimpleNavDriverTag},
3    resource::{NavMesh, NavMeshesRes, NavVec3},
4    Scalar,
5};
6use core::{
7    app::AppLifeCycle,
8    ecs::{Entities, Entity, Join, Read, ReadExpect, ReadStorage, System, WriteStorage},
9};
10use std::collections::HashMap;
11
12/// nav agents maintainment system. It's used to find path on nav mesh for agents.
13#[derive(Default)]
14pub struct NavAgentMaintainSystem(HashMap<Entity, Vec<NavVec3>>);
15
16impl NavAgentMaintainSystem {
17    pub fn with_cache_capacity(capacity: usize) -> Self {
18        Self(HashMap::with_capacity(capacity))
19    }
20}
21
22impl<'s> System<'s> for NavAgentMaintainSystem {
23    type SystemData = (
24        Entities<'s>,
25        Read<'s, NavMeshesRes>,
26        WriteStorage<'s, NavAgent>,
27    );
28
29    fn run(&mut self, (entities, meshes_res, mut agents): Self::SystemData) {
30        self.0.clear();
31        for (entity, agent) in (&entities, &agents).join() {
32            if agent.dirty_path {
33                if let Some(destination) = &agent.destination {
34                    if let Some(mesh) = meshes_res.0.get(&destination.mesh) {
35                        match destination.target {
36                            NavAgentTarget::Point(point) => {
37                                if let Some(path) = mesh.find_path(
38                                    agent.position,
39                                    point,
40                                    destination.query,
41                                    destination.mode,
42                                ) {
43                                    self.0.insert(entity, path);
44                                }
45                            }
46                            NavAgentTarget::Entity(entity) => {
47                                if let Some(other) = agents.get(entity) {
48                                    if let Some(path) = mesh.find_path(
49                                        agent.position,
50                                        other.position,
51                                        destination.query,
52                                        destination.mode,
53                                    ) {
54                                        self.0.insert(entity, path);
55                                    }
56                                }
57                            }
58                        }
59                    }
60                }
61            }
62        }
63        for (entity, path) in self.0.drain() {
64            if let Some(agent) = agents.get_mut(entity) {
65                agent.set_path(path);
66            }
67        }
68    }
69}
70
71/// Simple nav driver system. It's used to apply simple movement of agents with `SimpleNavDriverTag`
72/// component tag on their paths.
73pub struct SimpleNavDriverSystem;
74
75impl SimpleNavDriverSystem {
76    /// Internal system run function.
77    pub fn run_impl<'s>(
78        delta_time: Scalar,
79        (mut agents, drivers): (
80            WriteStorage<'s, NavAgent>,
81            ReadStorage<'s, SimpleNavDriverTag>,
82        ),
83    ) {
84        if delta_time <= 0.0 {
85            return;
86        }
87        for (agent, _) in (&mut agents, &drivers).join() {
88            if let Some(path) = agent.path() {
89                if let Some((target, _)) = NavMesh::path_target_point(
90                    path,
91                    agent.position,
92                    agent.speed.max(agent.min_target_distance.max(0.0)) * delta_time,
93                ) {
94                    let diff = target - agent.position;
95                    let dir = diff.normalize();
96                    agent.position = agent.position
97                        + dir * (agent.speed.max(0.0) * delta_time).min(diff.magnitude());
98                    agent.direction = diff.normalize();
99                }
100            }
101        }
102    }
103}
104
105impl<'s> System<'s> for SimpleNavDriverSystem {
106    type SystemData = (
107        ReadExpect<'s, AppLifeCycle>,
108        WriteStorage<'s, NavAgent>,
109        ReadStorage<'s, SimpleNavDriverTag>,
110    );
111
112    fn run(&mut self, (lifecycle, agents, drivers): Self::SystemData) {
113        let delta_time = lifecycle.delta_time_seconds();
114        Self::run_impl(delta_time, (agents, drivers));
115    }
116}