use std::{f32::consts::FRAC_PI_2, time::Duration}; use avian3d::{math::Vector, prelude::*}; use bevy::{ color::palettes, math::{vec2, vec3}, prelude::*, time::common_conditions::on_timer, }; use polyanya::Triangulation; use rand::Rng; use vleue_navigator::prelude::*; const MESH_UNIT: u32 = 100; #[derive(Component)] struct Obstacle(Timer); fn main() { let mut app = App::new(); app.insert_resource(ClearColor(palettes::css::BLACK.into())) .add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "Navmesh with Polyanya".to_string(), fit_canvas_to_parent: true, ..default() }), ..default() }), PhysicsPlugins::default().with_length_unit(2.0), VleueNavigatorPlugin, NavmeshUpdaterPlugin::::default(), )) .insert_resource(Gravity(Vector::NEG_Y * 9.81 * 10.0)) .add_systems(Startup, setup) .add_systems(Update, despawn_obstacles) .add_systems(PostUpdate, display_path) .add_systems( Update, spawn_obstacles.run_if(on_timer(Duration::from_secs_f32(0.5))), ); let mut config_store = app .world_mut() .get_resource_mut::() .unwrap(); for (_, config, _) in config_store.iter_mut() { config.depth_bias = -1.0; } app.run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(Camera3dBundle { transform: Transform::from_xyz(0.0, MESH_UNIT as f32 * 2.2, MESH_UNIT as f32).looking_at( vec3(MESH_UNIT as f32 * 0.85, 0.0, MESH_UNIT as f32), Vec3::Y, ), ..Default::default() }); commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { illuminance: 3000.0, shadows_enabled: true, ..default() }, transform: Transform::default().looking_at(Vec3::new(-1.0, -2.5, -1.5), Vec3::Y), ..default() }); // side A commands .spawn(SpatialBundle::from_transform(Transform::from_rotation( Quat::from_rotation_x(FRAC_PI_2), ))) .with_children(|p| { p.spawn(( NavMeshBundle { settings: NavMeshSettings { fixed: Triangulation::from_outer_edges(&[ vec2(0.0, 0.0), vec2(MESH_UNIT as f32, 0.0), vec2(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0), vec2(0.0, MESH_UNIT as f32 * 2.0), ]), simplify: 0.001, merge_steps: 3, upward_shift: 1.0, layer: Some(0), stitches: vec![( (0, 1), [ vec2(MESH_UNIT as f32, 0.0), vec2(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0), ], )], ..default() }, update_mode: NavMeshUpdateMode::Direct, ..NavMeshBundle::with_unique_id(0) }, NavMeshDebug(palettes::tailwind::YELLOW_600.into()), )); p.spawn(( PbrBundle { mesh: meshes.add(Plane3d::new( -Vec3::Z, Vec2::new(MESH_UNIT as f32 / 2.0, MESH_UNIT as f32), )), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::SLATE_900, ))), transform: Transform::from_xyz(MESH_UNIT as f32 / 2.0, MESH_UNIT as f32, 0.0), ..default() }, RigidBody::Static, Collider::cuboid(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0, 0.01), Restitution::ZERO, )); }); // side B commands .spawn(SpatialBundle::from_transform( Transform::from_translation(vec3(MESH_UNIT as f32, 0.0, 0.0)) .with_rotation(Quat::from_rotation_x(FRAC_PI_2)), )) .with_children(|p| { p.spawn(( NavMeshBundle { settings: NavMeshSettings { fixed: Triangulation::from_outer_edges(&[ vec2(0.0, 0.0), vec2(MESH_UNIT as f32, 0.0), vec2(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0), vec2(0.0, MESH_UNIT as f32 * 2.0), ]), simplify: 0.001, merge_steps: 3, upward_shift: 1.0, layer: Some(1), stitches: vec![( (0, 1), [ vec2(MESH_UNIT as f32, 0.0), vec2(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0), ], )], ..default() }, update_mode: NavMeshUpdateMode::Direct, ..NavMeshBundle::with_unique_id(0) }, NavMeshDebug(palettes::tailwind::LIME_600.into()), )); p.spawn(( PbrBundle { mesh: meshes.add(Plane3d::new( -Vec3::Z, Vec2::new(MESH_UNIT as f32 / 2.0, MESH_UNIT as f32), )), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::SLATE_900, ))), transform: Transform::from_xyz(MESH_UNIT as f32 / 2.0, MESH_UNIT as f32, 0.0), ..default() }, RigidBody::Static, Collider::cuboid(MESH_UNIT as f32, MESH_UNIT as f32 * 2.0, 0.01), Restitution::ZERO, )); }); commands.spawn(( PbrBundle { mesh: meshes.add(Capsule3d::new(2.0, 2.0).mesh()), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::RED_600, ))), transform: Transform::from_translation(vec3( MESH_UNIT as f32 / 10.0, 0.0, MESH_UNIT as f32 / 10.0, )), ..default() }, RigidBody::Static, Collider::capsule(2.0, 2.0), )); commands.spawn(( PbrBundle { mesh: meshes.add(Capsule3d::new(2.0, 2.0).mesh()), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::RED_600, ))), transform: Transform::from_translation(vec3( MESH_UNIT as f32 * 2.0 - MESH_UNIT as f32 / 10.0, 0.0, MESH_UNIT as f32 * 2.0 - MESH_UNIT as f32 / 10.0, )), ..default() }, RigidBody::Static, Collider::capsule(2.0, 2.0), )); commands.spawn(( PbrBundle { mesh: meshes.add(Capsule3d::new(2.0, 2.0).mesh()), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::RED_600, ))), transform: Transform::from_translation(vec3( MESH_UNIT as f32 / 10.0, 0.0, MESH_UNIT as f32 * 2.0 - MESH_UNIT as f32 / 10.0, )), ..default() }, RigidBody::Static, Collider::capsule(2.0, 2.0), )); commands.spawn(( PbrBundle { mesh: meshes.add(Capsule3d::new(2.0, 2.0).mesh()), material: materials.add(StandardMaterial::from(Color::Srgba( palettes::tailwind::RED_600, ))), transform: Transform::from_translation(vec3( MESH_UNIT as f32 * 2.0 - MESH_UNIT as f32 / 10.0, 0.0, MESH_UNIT as f32 / 10.0, )), ..default() }, RigidBody::Static, Collider::capsule(2.0, 2.0), )); } fn spawn_obstacles( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, navmeshes: Res>, ) { let cube_size = rand::thread_rng().gen_range(5.0..10.0); loop { let x = rand::thread_rng().gen_range(-(MESH_UNIT as f32 * 3.0)..(MESH_UNIT as f32 * 3.0)); let z = rand::thread_rng().gen_range(-(MESH_UNIT as f32 * 3.0)..(MESH_UNIT as f32 * 3.0)); if navmeshes.iter().any(|(_, nm)| nm.is_in_mesh(vec2(x, z))) { commands.spawn(( PbrBundle { mesh: meshes.add(Cuboid::new(cube_size, cube_size, cube_size)), material: materials.add(Color::srgb(0.2, 0.7, 0.9)), transform: Transform::from_xyz(x, 50.0, z).looking_to( Vec3::new( rand::thread_rng().gen_range(-1.0..1.0), rand::thread_rng().gen_range(-1.0..1.0), rand::thread_rng().gen_range(-1.0..1.0), ) .normalize(), Vec3::Y, ), ..default() }, RigidBody::Dynamic, Collider::cuboid(cube_size, cube_size, cube_size), Restitution::ZERO, Obstacle(Timer::from_seconds(30.0, TimerMode::Once)), )); return; } } } fn despawn_obstacles( mut commands: Commands, time: Res