use std::{ sync::{Arc, RwLock}, time::Duration, }; use bevy::{ color::palettes, math::Vec3Swizzles, prelude::*, sprite::MaterialMesh2dBundle, tasks::AsyncComputeTaskPool, window::{PrimaryWindow, WindowResized}, }; use vleue_navigator::{NavMesh, VleueNavigatorPlugin}; fn main() { App::new() .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() }), VleueNavigatorPlugin, )) .add_systems(Startup, setup) .add_systems( Update, ( on_mesh_change, mesh_change, on_click, compute_paths, poll_path_tasks, move_navigator, display_path, ), ) .run(); } #[derive(Resource)] struct Meshes { simple: Handle, arena: Handle, aurora: Handle, } enum CurrentMesh { Simple, Arena, Aurora, } #[derive(Resource)] struct MeshDetails { mesh: CurrentMesh, size: Vec2, } const SIMPLE: MeshDetails = MeshDetails { mesh: CurrentMesh::Simple, size: Vec2::new(13.0, 8.0), }; const ARENA: MeshDetails = MeshDetails { mesh: CurrentMesh::Arena, size: Vec2::new(49.0, 49.0), }; const AURORA: MeshDetails = MeshDetails { mesh: CurrentMesh::Aurora, size: Vec2::new(1024.0, 768.0), }; fn setup( mut commands: Commands, mut navmeshes: ResMut>, asset_server: Res, ) { commands.spawn(Camera2dBundle::default()); commands.insert_resource(Meshes { simple: navmeshes.add(NavMesh::from_polyanya_mesh( polyanya::Mesh::new( vec![ polyanya::Vertex::new(Vec2::new(0., 6.), vec![0, u32::MAX]), polyanya::Vertex::new(Vec2::new(2., 5.), vec![0, u32::MAX, 2]), polyanya::Vertex::new(Vec2::new(5., 7.), vec![0, 2, u32::MAX]), polyanya::Vertex::new(Vec2::new(5., 8.), vec![0, u32::MAX]), polyanya::Vertex::new(Vec2::new(0., 8.), vec![0, u32::MAX]), polyanya::Vertex::new(Vec2::new(1., 4.), vec![1, u32::MAX]), polyanya::Vertex::new(Vec2::new(2., 1.), vec![1, u32::MAX]), polyanya::Vertex::new(Vec2::new(4., 1.), vec![1, u32::MAX]), polyanya::Vertex::new(Vec2::new(4., 2.), vec![1, u32::MAX, 2]), polyanya::Vertex::new(Vec2::new(2., 4.), vec![1, 2, u32::MAX]), polyanya::Vertex::new(Vec2::new(7., 4.), vec![2, u32::MAX, 4]), polyanya::Vertex::new(Vec2::new(10., 7.), vec![2, 4, 6, u32::MAX, 3]), polyanya::Vertex::new(Vec2::new(7., 7.), vec![2, 3, u32::MAX]), polyanya::Vertex::new(Vec2::new(11., 8.), vec![3, u32::MAX]), polyanya::Vertex::new(Vec2::new(7., 8.), vec![3, u32::MAX]), polyanya::Vertex::new(Vec2::new(7., 0.), vec![5, 4, u32::MAX]), polyanya::Vertex::new(Vec2::new(11., 3.), vec![4, 5, u32::MAX]), polyanya::Vertex::new(Vec2::new(11., 5.), vec![4, u32::MAX, 6]), polyanya::Vertex::new(Vec2::new(12., 0.), vec![5, u32::MAX]), polyanya::Vertex::new(Vec2::new(12., 3.), vec![5, u32::MAX]), polyanya::Vertex::new(Vec2::new(13., 5.), vec![6, u32::MAX]), polyanya::Vertex::new(Vec2::new(13., 7.), vec![6, u32::MAX]), polyanya::Vertex::new(Vec2::new(1., 3.), vec![1, u32::MAX]), ], vec![ polyanya::Polygon::new(vec![0, 1, 2, 3, 4], true), polyanya::Polygon::new(vec![5, 22, 6, 7, 8, 9], true), polyanya::Polygon::new(vec![1, 9, 8, 10, 11, 12, 2], false), polyanya::Polygon::new(vec![12, 11, 13, 14], true), polyanya::Polygon::new(vec![10, 15, 16, 17, 11], false), polyanya::Polygon::new(vec![15, 18, 19, 16], true), polyanya::Polygon::new(vec![11, 17, 20, 21], true), ], ) .unwrap(), )), arena: asset_server.load("arena-merged.polyanya.mesh"), aurora: asset_server.load("aurora-merged.polyanya.mesh"), }); commands.insert_resource(AURORA); } fn on_mesh_change( mesh: Res, mut commands: Commands, mut meshes: ResMut>, navmeshes: Res>, mut materials: ResMut>, known_meshes: Res, mut current_mesh_entity: Local>, primary_window: Query<&Window, With>, navigator: Query>, window_resized: EventReader, text: Query>, mut wait_for_mesh: Local, ) { if mesh.is_changed() || !window_resized.is_empty() || *wait_for_mesh { let handle = match mesh.mesh { CurrentMesh::Simple => &known_meshes.simple, CurrentMesh::Arena => &known_meshes.arena, CurrentMesh::Aurora => &known_meshes.aurora, }; if let Some(navmesh) = navmeshes.get(handle) { *wait_for_mesh = false; if let Some(entity) = *current_mesh_entity { commands.entity(entity).despawn(); } if let Ok(entity) = navigator.get_single() { commands.entity(entity).despawn(); } let window = primary_window.single(); let factor = (window.width() / mesh.size.x).min(window.height() / mesh.size.y); *current_mesh_entity = Some( commands .spawn(MaterialMesh2dBundle { mesh: meshes.add(navmesh.to_mesh()).into(), transform: Transform::from_translation(Vec3::new( -mesh.size.x / 2.0 * factor, -mesh.size.y / 2.0 * factor, 0.0, )) .with_scale(Vec3::splat(factor)), material: materials .add(ColorMaterial::from(Color::Srgba(palettes::css::BLUE))), ..default() }) .id(), ); if let Ok(entity) = text.get_single() { commands.entity(entity).despawn(); } commands.spawn(TextBundle { text: Text::from_sections([ TextSection::new( match mesh.mesh { CurrentMesh::Simple => "Simple\n", CurrentMesh::Arena => "Arena\n", CurrentMesh::Aurora => "Aurora\n", }, TextStyle { font_size: 30.0, color: palettes::css::WHITE.into(), ..default() }, ), TextSection::new( "Press spacebar or long touch to switch mesh\n", TextStyle { font_size: 15.0, color: palettes::css::WHITE.into(), ..default() }, ), TextSection::new( "Click to find a path", TextStyle { font_size: 15.0, color: palettes::css::WHITE.into(), ..default() }, ), ]), style: Style { position_type: PositionType::Absolute, margin: UiRect { top: Val::Px(5.0), left: Val::Px(5.0), ..default() }, ..default() }, ..default() }); } else { *wait_for_mesh = true; } } } fn mesh_change( mut mesh: ResMut, keyboard_input: Res>, mouse_input: Res>, time: Res