fyrox_impl/scene/navmesh.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
//! Navigational mesh (navmesh for short) is a surface which can be used for path finding. See [`NavigationalMesh`] docs
//! for more info and usage examples.
use crate::{
core::{
color::Color,
math::aabb::AxisAlignedBoundingBox,
parking_lot::RwLock,
pool::Handle,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
scene::{
base::{Base, BaseBuilder},
debug::{Line, SceneDrawingContext},
graph::Graph,
node::{Node, NodeTrait},
},
utils::navmesh::Navmesh,
};
use fyrox_core::parking_lot::{RwLockReadGuard, RwLockWriteGuard};
use fyrox_graph::BaseSceneGraph;
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
#[derive(Clone, Default, Reflect, Debug)]
pub(crate) struct Container(Arc<RwLock<Navmesh>>);
impl PartialEq for Container {
fn eq(&self, other: &Self) -> bool {
*self.0.read() == *other.0.read()
}
}
impl Visit for Container {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.write().visit(name, visitor)
}
}
/// Navigational mesh (navmesh for short) is a surface which can be used for path finding. Unlike [A* Pathfinder](crate::utils::astar),
/// it can build arbitrary paths on a surface of large polygons, making a path from point A to point B linear (standard pathfinder builds
/// path only from vertex to vertex). Navmeshes should be used when you have an arbitrary "walkable" surface, for example, a game level
/// with rooms, hallways, multiple floors and so on. A* pathfinder should be used for strategies or any other types of games with uniform
/// pathfinding grid.
///
/// ## How to create
///
/// You should prefer using the navmesh editor to create navigational meshes, however if it is not possible, you can create it manually.
/// Use [`NavigationalMeshBuilder`] to create new instance and add it to your scene graph. Keep in mind, that this node is just a
/// convenient wrapper around [`Navmesh`], so you should also read its docs to get better understanding how it works.
///
/// ```rust
/// # use fyrox_impl::{
/// # core::{algebra::Vector3, math::TriangleDefinition, pool::Handle},
/// # scene::{base::BaseBuilder, graph::Graph, navmesh::NavigationalMeshBuilder, node::Node},
/// # utils::navmesh::Navmesh,
/// # };
/// fn create_navmesh(graph: &mut Graph) -> Handle<Node> {
/// // A simple navmesh with four vertices and two triangles.
/// let navmesh = Navmesh::new(
/// vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])],
/// vec![
/// Vector3::new(-1.0, 0.0, 1.0),
/// Vector3::new(1.0, 0.0, 1.0),
/// Vector3::new(1.0, 0.0, -1.0),
/// Vector3::new(-1.0, 0.0, -1.0),
/// ],
/// );
/// NavigationalMeshBuilder::new(BaseBuilder::new())
/// .with_navmesh(navmesh)
/// .build(graph)
/// }
/// ```
///
/// ## Agents
///
/// Navigational mesh agent helps you to build paths along the surface of a navigational mesh and follow it. Agents can be
/// used to drive the motion of your game characters. Every agent knows about its target and automatically rebuilds the path
/// if the target has moved. Navmesh agents are able to move along the path, providing you with their current position, so you
/// can use it to perform an actual motion of your game characters. Agents work together with navigational meshes, you need
/// to update their state every frame, so they can recalculate path if needed. A simple example could something like this:
///
/// ```rust
/// # use fyrox_impl::utils::navmesh::NavmeshAgent;
/// # struct Foo {
/// // Add this to your script
/// agent: NavmeshAgent
/// # }
/// ```
///
/// After that, you need to update the agent every frame to make sure it will follow the target:
///
/// ```rust
/// # use fyrox_impl::{
/// # core::algebra::Vector3, scene::navmesh::NavigationalMesh, utils::navmesh::NavmeshAgent,
/// # };
/// fn update_agent(
/// agent: &mut NavmeshAgent,
/// target: Vector3<f32>,
/// dt: f32,
/// navmesh: &NavigationalMesh,
/// ) {
/// // Set the target to follow and the speed.
/// agent.set_target(target);
/// agent.set_speed(1.0);
///
/// // Update the agent.
/// agent.update(dt, &navmesh.navmesh_ref()).unwrap();
///
/// // Print its position - you can use this position as target point of your game character.
/// println!("{}", agent.position());
/// }
/// ```
///
/// This method should be called in `on_update` of your script. It accepts four parameters: a reference to the agent, a
/// target which it will follow, a time step (`context.dt`), and a reference to navigational mesh node. You can fetch
/// navigational mesh from the scene graph by its name:
///
/// ```rust
/// # use fyrox_impl::scene::{navmesh::NavigationalMesh, Scene};
/// # use fyrox_graph::SceneGraph;
/// fn find_navmesh<'a>(scene: &'a mut Scene, name: &str) -> &'a mut NavigationalMesh {
/// let handle = scene.graph.find_by_name_from_root(name).unwrap().0;
/// scene.graph[handle].as_navigational_mesh_mut()
/// }
/// ```
#[derive(Debug, Clone, Visit, Reflect, Default)]
pub struct NavigationalMesh {
base: Base,
#[reflect(read_only)]
navmesh: InheritableVariable<Container>,
}
impl TypeUuidProvider for NavigationalMesh {
fn type_uuid() -> Uuid {
uuid!("d0ce963c-b50a-4707-bd21-af6dc0d1c668")
}
}
impl Deref for NavigationalMesh {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for NavigationalMesh {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl NodeTrait for NavigationalMesh {
crate::impl_query_component!();
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.local_bounding_box()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
let navmesh = self.navmesh.0.read();
for vertex in navmesh.vertices().iter() {
ctx.draw_sphere(*vertex, 6, 6, 0.1, Color::GREEN);
}
for triangle in navmesh.triangles().iter() {
for edge in &triangle.edges() {
ctx.add_line(Line {
begin: navmesh.vertices()[edge.a as usize],
end: navmesh.vertices()[edge.b as usize],
color: Color::GREEN,
});
}
}
}
}
impl NavigationalMesh {
/// Returns a reference to the inner navigational mesh.
pub fn navmesh_ref(&self) -> RwLockReadGuard<Navmesh> {
self.navmesh.0.read()
}
/// Returns a reference to the inner navigational mesh.
pub fn navmesh_mut(&mut self) -> RwLockWriteGuard<Navmesh> {
self.navmesh.0.write()
}
/// Returns a shared reference to the inner navigational mesh. It could be used to perform
/// off-thread path calculations.
pub fn navmesh(&self) -> Arc<RwLock<Navmesh>> {
self.navmesh.0.clone()
}
}
/// Creates navigational meshes and adds them to a scene graph.
pub struct NavigationalMeshBuilder {
base_builder: BaseBuilder,
navmesh: Navmesh,
}
impl NavigationalMeshBuilder {
/// Creates new navigational mesh builder.
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
navmesh: Default::default(),
}
}
/// Sets the actual navigational mesh.
pub fn with_navmesh(mut self, navmesh: Navmesh) -> Self {
self.navmesh = navmesh;
self
}
fn build_navigational_mesh(self) -> NavigationalMesh {
NavigationalMesh {
base: self.base_builder.build_base(),
navmesh: InheritableVariable::new_modified(Container(Arc::new(RwLock::new(
self.navmesh,
)))),
}
}
/// Creates new navigational mesh instance.
pub fn build_node(self) -> Node {
Node::new(self.build_navigational_mesh())
}
/// Creates new navigational mesh instance and adds it to the graph.
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}