use std::{ sync::{Arc, RwLock}, time::Duration, }; use bevy::{ math::Vec3Swizzles, prelude::*, sprite::MaterialMesh2dBundle, tasks::AsyncComputeTaskPool, window::{PrimaryWindow, WindowResized}, }; use bevy_pathmesh::{PathMesh, PathMeshPlugin}; fn main() { App::new() .insert_resource(ClearColor(Color::BLACK)) .add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "Navmesh with Polyanya".to_string(), ..default() }), ..default() }), PathMeshPlugin, )) .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 pathmeshes: ResMut>, asset_server: Res, ) { commands.spawn(Camera2dBundle::default()); commands.insert_resource(Meshes { simple: pathmeshes.add(PathMesh::from_polyanya_mesh(polyanya::Mesh::new( vec![ polyanya::Vertex::new(Vec2::new(0., 6.), vec![0, -1]), polyanya::Vertex::new(Vec2::new(2., 5.), vec![0, -1, 2]), polyanya::Vertex::new(Vec2::new(5., 7.), vec![0, 2, -1]), polyanya::Vertex::new(Vec2::new(5., 8.), vec![0, -1]), polyanya::Vertex::new(Vec2::new(0., 8.), vec![0, -1]), polyanya::Vertex::new(Vec2::new(1., 4.), vec![1, -1]), polyanya::Vertex::new(Vec2::new(2., 1.), vec![1, -1]), polyanya::Vertex::new(Vec2::new(4., 1.), vec![1, -1]), polyanya::Vertex::new(Vec2::new(4., 2.), vec![1, -1, 2]), polyanya::Vertex::new(Vec2::new(2., 4.), vec![1, 2, -1]), polyanya::Vertex::new(Vec2::new(7., 4.), vec![2, -1, 4]), polyanya::Vertex::new(Vec2::new(10., 7.), vec![2, 4, 6, -1, 3]), polyanya::Vertex::new(Vec2::new(7., 7.), vec![2, 3, -1]), polyanya::Vertex::new(Vec2::new(11., 8.), vec![3, -1]), polyanya::Vertex::new(Vec2::new(7., 8.), vec![3, -1]), polyanya::Vertex::new(Vec2::new(7., 0.), vec![5, 4, -1]), polyanya::Vertex::new(Vec2::new(11., 3.), vec![4, 5, -1]), polyanya::Vertex::new(Vec2::new(11., 5.), vec![4, -1, 6]), polyanya::Vertex::new(Vec2::new(12., 0.), vec![5, -1]), polyanya::Vertex::new(Vec2::new(12., 3.), vec![5, -1]), polyanya::Vertex::new(Vec2::new(13., 5.), vec![6, -1]), polyanya::Vertex::new(Vec2::new(13., 7.), vec![6, -1]), polyanya::Vertex::new(Vec2::new(1., 3.), vec![1, -1]), ], 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), ], ))), 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>, pathmeshes: Res>, mut materials: ResMut>, path_meshes: Res, mut current_mesh_entity: Local>, primary_window: Query<&Window, With>, navigator: Query>, window_resized: EventReader, asset_server: Res, 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 => &path_meshes.simple, CurrentMesh::Arena => &path_meshes.arena, CurrentMesh::Aurora => &path_meshes.aurora, }; if let Some(pathmesh) = pathmeshes.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(pathmesh.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::BLUE)), ..default() }) .id(), ); if let Ok(entity) = text.get_single() { commands.entity(entity).despawn(); } let font = asset_server.load("fonts/FiraMono-Medium.ttf"); 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: font.clone_weak(), font_size: 30.0, color: Color::WHITE, }, ), TextSection::new( "Press spacebar or long touch to switch mesh\n", TextStyle { font: font.clone_weak(), font_size: 15.0, color: Color::WHITE, }, ), TextSection::new( "Click to find a path", TextStyle { font: font.clone_weak(), font_size: 15.0, color: Color::WHITE, }, ), ]), 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