andriyDev / bevy_landmass

A crate for Bevy to use landmass.
Apache License 2.0
7 stars 1 forks source link

desired_velocity always zero #3

Closed maxfierke closed 1 year ago

maxfierke commented 1 year ago

Hey there, I'm playing around with bevy_landmass and landmass trying to get navmesh-based pathfinding working for a point-and-click adventure game.

I think I've got everything wired up correctly, but I'm not able to see desired_velocity get updated to anything other than [0, 0, 0].

Here's what the navmesh looks like in both code and rendered on-screen with bevy_prototype_debug_lines:

Code ```rust // This converts map coordinates into Bevy world coordinates // context: the map's background image is 480x270, positioned at center 0,0 (bevy origin), and scaled 3x for screen fn map_to_world(x: f32, y: f32, z: f32) -> glam::Vec3 { glam::Vec3::new( (-480.0 / 2.0 + x) * 3.0, (-270.0 / 2.0 + y) * 3.0, z, ) } // Called during scene load. Coords come from a mesh-making tool that uses bottom-left origin, // so map_to_world is used to convert it to Bevy world coordinate space fn spawn_navmesh(mut commands: Commands,) { let navmesh = landmass::NavigationMesh { mesh_bounds: None, vertices: vec![ map_to_world(51.0, 124.0, 0.0), // 0 map_to_world(174.0, 124.0, 0.0), // 1 map_to_world(174.0, 106.0, 0.0), // 2 map_to_world(36.0, 106.0, 0.0), // 3 map_to_world(304.0, 79.0, 0.0), // 4 map_to_world(124.0, 79.0, 0.0), // 5 map_to_world(144.0, 106.0, 0.0), // 6 map_to_world(293.0, 106.0, 0.0), // 7 map_to_world(293.0, 106.0, 0.0), // 7 + 8 map_to_world(208.0, 106.0, 0.0), // 9 map_to_world(223.0, 124.0, 0.0), // 10 map_to_world(291.0, 124.0, 0.0), // 11 map_to_world(457.0, 94.0, 0.0), // 12 map_to_world(473.0, 79.0, 0.0), // 13 map_to_world(302.0, 79.0, 0.0), // 14 map_to_world(297.0, 94.0, 0.0), // 15 map_to_world(454.0, 99.0, 0.0), // 16 map_to_world(457.0, 94.0, 0.0), // 12 + 17 map_to_world(397.0, 94.0, 0.0), // 18 map_to_world(394.0, 99.0, 0.0), // 19 map_to_world(457.0, 180.0, 0.0), // 20 map_to_world(473.0, 187.0, 0.0), // 21 map_to_world(473.0, 79.0, 0.0), // 13 + 22 map_to_world(457.0, 79.0, 0.0), // 23 ], polygons: vec![ vec![0, 1, 2, 3], vec![4, 5, 6, 7], vec![7, 9, 10, 11], vec![12, 13, 14, 15], vec![16, 12, 18, 19], vec![20, 21, 13, 23], ], }; commands.spawn( Archipelago::new( landmass::Archipelago::create_from_navigation_mesh( navmesh.validate().expect("is valid") ) ) ); } // Just printing the desired velocity from the player's agent fn print_desired_velocity( query: Query<(Entity, &AgentTarget, &AgentDesiredVelocity), With>, transforms: Query<&GlobalTransform>, ) { for (entity, target, desired_velocity) in query.iter() { let entity_pos = transforms.get(entity).unwrap(); let pos = match target { AgentTarget::None => Some(Vec3::ZERO), AgentTarget::Point(pos) => Some(*pos), AgentTarget::Entity(target_entity) => transforms.get(*target_entity).map(|e| e.translation()).ok(), }; if let Some(pos) = pos { println!( "entity={:?}, target={:?}, desired_velocity={:?}", entity, pos, desired_velocity.velocity() ); } else { println!( "Trying to move entity={:?} to an entity pos, but cannot find position of entity", entity ); } } } // System called when the cursor is clicked. Cursor coordinates are world-space here. fn cursor_entity_click( mut commands: Commands, cursor_query: Query<(Entity, &ActionState), With>, player_query: Query>, rapier_context: Res, cursor_world_pos: Res, navmeshes: Query>, mut cursor_events: EventWriter, ) { if let Ok((cursor, action_state)) = cursor_query.get_single() { if action_state.just_pressed(CursorAction::Click) { let mut found_intersection = false; for (_, entity, intersecting) in rapier_context.intersections_with(cursor) { if intersecting { found_intersection = true; println!("Cursor clicked event: {:?}", entity); cursor_events.send(CursorEvent::Clicked(entity)); } } if !found_intersection { if let Ok(player) = player_query.get_single() { let pos = cursor_world_pos.truncate(); let archipelago_id = navmeshes.single(); println!("Moving {:?} to {}", player, pos); commands.entity(player).insert(AgentBundle { agent: Agent { radius: 32.0 * 4.0, // Character sprite is scaled up 4x max_velocity: 10.0, }, archipelago_ref: ArchipelagoRef(archipelago_id), target: AgentTarget::Point(Vec3::new(pos.x, pos.y, 0.0)), velocity: AgentVelocity(Vec3::splat(10.0)), desired_velocity: Default::default(), }); } } } } } ```
ValidNavigationMesh ```rust ValidNavigationMesh { mesh_bounds: Box { min: Vec3(-612.0, -168.0, 0.0), max: Vec3(699.0, 156.0, 0.0) }, vertices: [Vec3(-567.0, -33.0, 0.0), Vec3(-198.0, -33.0, 0.0), Vec3(-198.0, -87.0, 0.0), Vec3(-612.0, -87.0, 0.0), Vec3(192.0, -168.0, 0.0), Vec3(-348.0, -168.0, 0.0), Vec3(-288.0, -87.0, 0.0), Vec3(159.0, -87.0, 0.0), Vec3(159.0, -87.0, 0.0), Vec3(-96.0, -87.0, 0.0), Vec3(-51.0, -33.0, 0.0), Vec3(153.0, -33.0, 0.0), Vec3(651.0, -123.0, 0.0), Vec3(699.0, -168.0, 0.0), Vec3(186.0, -168.0, 0.0), Vec3(171.0, -123.0, 0.0), Vec3(642.0, -108.0, 0.0), Vec3(651.0, -123.0, 0.0), Vec3(471.0, -123.0, 0.0), Vec3(462.0, -108.0, 0.0), Vec3(651.0, 135.0, 0.0), Vec3(699.0, 156.0, 0.0), Vec3(699.0, -168.0, 0.0), Vec3(651.0, -168.0, 0.0)], polygons: [ValidPolygon { vertices: [0, 1, 2, 3], bounds: Box { min: Vec3(-612.0, -87.0, 0.0), max: Vec3(-198.0, -33.0, 0.0) }, center: Vec3(-393.75, -60.0, 0.0) }, ValidPolygon { vertices: [4, 5, 6, 7], bounds: Box { min: Vec3(-348.0, -168.0, 0.0), max: Vec3(192.0, -87.0, 0.0) }, center: Vec3(-71.25, -127.5, 0.0) }, ValidPolygon { vertices: [7, 9, 10, 11], bounds: Box { min: Vec3(-96.0, -87.0, 0.0), max: Vec3(159.0, -33.0, 0.0) }, center: Vec3(41.25, -60.0, 0.0) }, ValidPolygon { vertices: [12, 13, 14, 15], bounds: Box { min: Vec3(171.0, -168.0, 0.0), max: Vec3(699.0, -123.0, 0.0) }, center: Vec3(426.75, -145.5, 0.0) }, ValidPolygon { vertices: [16, 12, 18, 19], bounds: Box { min: Vec3(462.0, -123.0, 0.0), max: Vec3(651.0, -108.0, 0.0) }, center: Vec3(556.5, -115.5, 0.0) }, ValidPolygon { vertices: [20, 21, 13, 23], bounds: Box { min: Vec3(651.0, -168.0, 0.0), max: Vec3(699.0, 156.0, 0.0) }, center: Vec3(675.0, -11.25, 0.0) }], connectivity: [[], [], [], [], [], []], boundary_edges: [MeshEdgeRef { polygon_index: 0, edge_index: 3 }, MeshEdgeRef { polygon_index: 3, edge_index: 1 }, MeshEdgeRef { polygon_index: 4, edge_index: 2 }, MeshEdgeRef { polygon_index: 0, edge_index: 2 }, MeshEdgeRef { polygon_index: 3, edge_index: 0 }, MeshEdgeRef { polygon_index: 0, edge_index: 1 }, MeshEdgeRef { polygon_index: 5, edge_index: 2 }, MeshEdgeRef { polygon_index: 5, edge_index: 3 }, MeshEdgeRef { polygon_index: 2, edge_index: 1 }, MeshEdgeRef { polygon_index: 4, edge_index: 3 }, MeshEdgeRef { polygon_index: 5, edge_index: 1 }, MeshEdgeRef { polygon_index: 5, edge_index: 0 }, MeshEdgeRef { polygon_index: 1, edge_index: 2 }, MeshEdgeRef { polygon_index: 2, edge_index: 0 }, MeshEdgeRef { polygon_index: 3, edge_index: 3 }, MeshEdgeRef { polygon_index: 2, edge_index: 3 }, MeshEdgeRef { polygon_index: 4, edge_index: 0 }, MeshEdgeRef { polygon_index: 4, edge_index: 1 }, MeshEdgeRef { polygon_index: 1, edge_index: 0 }, MeshEdgeRef { polygon_index: 1, edge_index: 1 }, MeshEdgeRef { polygon_index: 0, edge_index: 0 }, MeshEdgeRef { polygon_index: 1, edge_index: 3 }, MeshEdgeRef { polygon_index: 2, edge_index: 2 }, MeshEdgeRef { polygon_index: 3, edge_index: 2 }] } ```
Screenshot 2023-07-22 at 1 15 15 PM

When clicking a destination to move, here's what I see printed every frame (indicating it's at least updating AgentDesiredVelocity every frame):

entity=43v0, target=Vec3(-59.12106, -78.839844, 0.0), desired_velocity=Vec3(0.0, 0.0, 0.0)

As far as I can tell, both based on the visual representation and the numbers itself compared with the generated navmesh coordinates, the target is within the navmesh, so I would expect it to find a path. (small white line is from the center of the actor's feet to the target. There's some simple math involved to shift the Y to make it all based on the actor's feet, but I removed it from the example above and the behavior is the same either way.)

Any ideas on what I'm missing or doing wrong here?

andriyDev commented 1 year ago

Your coordinates are all flipped! landmass assumes that Y is up and agents walk in the XZ plane, as its mostly meant for 3D navigation although it should be totally capable to do 2D navigation. So your nav mesh needs to use Z as its other dimension, as well as your AgentTarget, AS WELL AS your agent's transform.

The last one is the hardest one to manage. What you can do as a workaround is to spawn a separate entity to be your "proxy". Then add a system to copy its transform to match the "real" entity's transform but moving the Y to be the Z. Then you can read back the desired velocity by querying for the proxy from your "real" entity. I'm not 100% sure this will fix your problem, but it should be a step in the right direction.

I think I would like to support 2D navigation, at least from bevy_landmass. I'll make an issue for it. I also still need to add better reporting mechanisms so users can actually tell what their agent is doing (or why they're not doing!).

P.S. Thank you so much for trying out bevy_landmass and landmass! I'm so excited! :D

maxfierke commented 1 year ago

Ohhh that makes sense!! I'll give the proxy idea a shot and see where I get with that. Thanks!

I'm really liking the approach of landmass so far, though! Definitely fits my mental model for this type of stuff and the code is quite readable!

maxfierke commented 1 year ago

Ah hah! The proxy idea has yielded some progress! I can now navigate within one polygon (connectivity between polygons seems not to be working yet, but I haven't looked into it yet. Probably something easy I'm missing.)

entity=44v0, target=Vec3(-254.54494, 0.0, -1.2148438), desired_velocity=Vec3(-9.9998865, 0.0, -0.047725555)

For posterity, I ended up:

Thanks for all your help 😁 !