Skip to content

Commit 794ca78

Browse files
ThierryBergermockersf
authored andcommitted
add an example featuring full lines instantly through debug_lines dependency
1 parent 4e4bf5c commit 794ca78

File tree

2 files changed

+298
-0
lines changed

2 files changed

+298
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ version = "0.8.0"
1313
features = ["bevy_sprite", "bevy_render", "bevy_ui", "bevy_text", "bevy_core_pipeline", "bevy_winit", "bevy_asset", "bevy_scene"]
1414
default-features = false
1515

16+
[dev-dependencies]
17+
bevy_prototype_debug_lines = "0.8"
18+
1619
[features]
1720
default = []
1821
linuxci = ["bevy/x11"]

examples/lines.rs

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
use bevy::{prelude::*, sprite::MaterialMesh2dBundle, window::WindowResized};
2+
use bevy_pathmesh::{PathMesh, PathmeshPlugin};
3+
use bevy_prototype_debug_lines::{DebugLines, DebugLinesPlugin};
4+
5+
fn main() {
6+
App::new()
7+
.insert_resource(ClearColor(Color::BLACK))
8+
.insert_resource(WindowDescriptor {
9+
title: "Navmesh with Polyanya".to_string(),
10+
fit_canvas_to_parent: true,
11+
..default()
12+
})
13+
.add_plugins(DefaultPlugins)
14+
.add_plugin(DebugLinesPlugin::default())
15+
.add_plugin(PathmeshPlugin)
16+
.add_event::<NewPathStepEvent>()
17+
.insert_resource(PathToDisplay::default())
18+
.add_startup_system(setup)
19+
.add_system(on_mesh_change)
20+
.add_system(mesh_change)
21+
.add_system(on_click)
22+
.add_system(compute_paths)
23+
.add_system(update_path_display)
24+
.run();
25+
}
26+
27+
struct Meshes {
28+
simple: Handle<PathMesh>,
29+
arena: Handle<PathMesh>,
30+
aurora: Handle<PathMesh>,
31+
}
32+
33+
enum CurrentMesh {
34+
Simple,
35+
Arena,
36+
Aurora,
37+
}
38+
39+
struct MeshDetails {
40+
mesh: CurrentMesh,
41+
size: Vec2,
42+
}
43+
44+
const SIMPLE: MeshDetails = MeshDetails {
45+
mesh: CurrentMesh::Simple,
46+
size: Vec2::new(13.0, 8.0),
47+
};
48+
49+
const ARENA: MeshDetails = MeshDetails {
50+
mesh: CurrentMesh::Arena,
51+
size: Vec2::new(49.0, 49.0),
52+
};
53+
54+
const AURORA: MeshDetails = MeshDetails {
55+
mesh: CurrentMesh::Aurora,
56+
size: Vec2::new(1024.0, 768.0),
57+
};
58+
59+
fn setup(
60+
mut commands: Commands,
61+
mut pathmeshes: ResMut<Assets<PathMesh>>,
62+
asset_server: Res<AssetServer>,
63+
) {
64+
commands.spawn_bundle(Camera2dBundle::default());
65+
commands.insert_resource(Meshes {
66+
simple: pathmeshes.add(PathMesh::from_polyanya_mesh(polyanya::Mesh::new(
67+
vec![
68+
polyanya::Vertex::new(Vec2::new(0., 6.), vec![0, -1]),
69+
polyanya::Vertex::new(Vec2::new(2., 5.), vec![0, -1, 2]),
70+
polyanya::Vertex::new(Vec2::new(5., 7.), vec![0, 2, -1]),
71+
polyanya::Vertex::new(Vec2::new(5., 8.), vec![0, -1]),
72+
polyanya::Vertex::new(Vec2::new(0., 8.), vec![0, -1]),
73+
polyanya::Vertex::new(Vec2::new(1., 4.), vec![1, -1]),
74+
polyanya::Vertex::new(Vec2::new(2., 1.), vec![1, -1]),
75+
polyanya::Vertex::new(Vec2::new(4., 1.), vec![1, -1]),
76+
polyanya::Vertex::new(Vec2::new(4., 2.), vec![1, -1, 2]),
77+
polyanya::Vertex::new(Vec2::new(2., 4.), vec![1, 2, -1]),
78+
polyanya::Vertex::new(Vec2::new(7., 4.), vec![2, -1, 4]),
79+
polyanya::Vertex::new(Vec2::new(10., 7.), vec![2, 4, 6, -1, 3]),
80+
polyanya::Vertex::new(Vec2::new(7., 7.), vec![2, 3, -1]),
81+
polyanya::Vertex::new(Vec2::new(11., 8.), vec![3, -1]),
82+
polyanya::Vertex::new(Vec2::new(7., 8.), vec![3, -1]),
83+
polyanya::Vertex::new(Vec2::new(7., 0.), vec![5, 4, -1]),
84+
polyanya::Vertex::new(Vec2::new(11., 3.), vec![4, 5, -1]),
85+
polyanya::Vertex::new(Vec2::new(11., 5.), vec![4, -1, 6]),
86+
polyanya::Vertex::new(Vec2::new(12., 0.), vec![5, -1]),
87+
polyanya::Vertex::new(Vec2::new(12., 3.), vec![5, -1]),
88+
polyanya::Vertex::new(Vec2::new(13., 5.), vec![6, -1]),
89+
polyanya::Vertex::new(Vec2::new(13., 7.), vec![6, -1]),
90+
polyanya::Vertex::new(Vec2::new(1., 3.), vec![1, -1]),
91+
],
92+
vec![
93+
polyanya::Polygon::new(vec![0, 1, 2, 3, 4], true),
94+
polyanya::Polygon::new(vec![5, 22, 6, 7, 8, 9], true),
95+
polyanya::Polygon::new(vec![1, 9, 8, 10, 11, 12, 2], false),
96+
polyanya::Polygon::new(vec![12, 11, 13, 14], true),
97+
polyanya::Polygon::new(vec![10, 15, 16, 17, 11], false),
98+
polyanya::Polygon::new(vec![15, 18, 19, 16], true),
99+
polyanya::Polygon::new(vec![11, 17, 20, 21], true),
100+
],
101+
))),
102+
arena: asset_server.load("arena-merged.polyanya.mesh"),
103+
aurora: asset_server.load("aurora-merged.polyanya.mesh"),
104+
});
105+
commands.insert_resource(SIMPLE);
106+
}
107+
108+
#[derive(Default)]
109+
struct PathToDisplay {
110+
steps: Vec<Vec2>,
111+
}
112+
113+
fn on_mesh_change(
114+
mut path_to_display: ResMut<PathToDisplay>,
115+
mesh: Res<MeshDetails>,
116+
mut commands: Commands,
117+
mut meshes: ResMut<Assets<Mesh>>,
118+
pathmeshes: Res<Assets<PathMesh>>,
119+
mut materials: ResMut<Assets<ColorMaterial>>,
120+
path_meshes: Res<Meshes>,
121+
mut current_mesh_entity: Local<Option<Entity>>,
122+
windows: Res<Windows>,
123+
window_resized: EventReader<WindowResized>,
124+
asset_server: Res<AssetServer>,
125+
text: Query<Entity, With<Text>>,
126+
) {
127+
if !mesh.is_changed() && window_resized.is_empty() {
128+
return;
129+
}
130+
path_to_display.steps.clear();
131+
let handle = match mesh.mesh {
132+
CurrentMesh::Simple => &path_meshes.simple,
133+
CurrentMesh::Arena => &path_meshes.arena,
134+
CurrentMesh::Aurora => &path_meshes.aurora,
135+
};
136+
let pathmesh = pathmeshes.get(handle).unwrap();
137+
if let Some(entity) = *current_mesh_entity {
138+
commands.entity(entity).despawn();
139+
}
140+
let window = windows.primary();
141+
let factor = (window.width() / mesh.size.x).min(window.height() / mesh.size.y);
142+
*current_mesh_entity = Some(
143+
commands
144+
.spawn_bundle(MaterialMesh2dBundle {
145+
mesh: meshes.add(pathmesh.to_mesh()).into(),
146+
transform: Transform::from_translation(Vec3::new(
147+
-mesh.size.x / 2.0 * factor,
148+
-mesh.size.y / 2.0 * factor,
149+
0.0,
150+
))
151+
.with_scale(Vec3::splat(factor)),
152+
material: materials.add(ColorMaterial::from(Color::BLUE)),
153+
..default()
154+
})
155+
.id(),
156+
);
157+
if let Ok(entity) = text.get_single() {
158+
commands.entity(entity).despawn();
159+
}
160+
let font = asset_server.load("fonts/FiraMono-Medium.ttf");
161+
commands.spawn_bundle(TextBundle {
162+
text: Text::from_sections([
163+
TextSection::new(
164+
match mesh.mesh {
165+
CurrentMesh::Simple => "Simple\n",
166+
CurrentMesh::Arena => "Arena\n",
167+
CurrentMesh::Aurora => "Aurora\n",
168+
},
169+
TextStyle {
170+
font: font.clone_weak(),
171+
font_size: 30.0,
172+
color: Color::WHITE,
173+
},
174+
),
175+
TextSection::new(
176+
"Press spacebar to switch mesh\n",
177+
TextStyle {
178+
font: font.clone_weak(),
179+
font_size: 15.0,
180+
color: Color::WHITE,
181+
},
182+
),
183+
TextSection::new(
184+
"Click to find a path",
185+
TextStyle {
186+
font: font.clone_weak(),
187+
font_size: 15.0,
188+
color: Color::WHITE,
189+
},
190+
),
191+
]),
192+
style: Style {
193+
position_type: PositionType::Absolute,
194+
position: UiRect {
195+
top: Val::Px(5.0),
196+
left: Val::Px(5.0),
197+
..default()
198+
},
199+
..default()
200+
},
201+
..default()
202+
});
203+
}
204+
205+
fn mesh_change(mut mesh: ResMut<MeshDetails>, keyboard_input: Res<Input<KeyCode>>) {
206+
if keyboard_input.just_pressed(KeyCode::Space) {
207+
match mesh.mesh {
208+
CurrentMesh::Simple => *mesh = ARENA,
209+
CurrentMesh::Arena => *mesh = AURORA,
210+
CurrentMesh::Aurora => *mesh = SIMPLE,
211+
}
212+
}
213+
}
214+
215+
struct NewPathStepEvent(Vec2);
216+
217+
fn on_click(
218+
mut path_step_event: EventWriter<NewPathStepEvent>,
219+
mouse_button_input: Res<Input<MouseButton>>,
220+
windows: Res<Windows>,
221+
mesh: Res<MeshDetails>,
222+
meshes: Res<Meshes>,
223+
pathmeshes: Res<Assets<PathMesh>>,
224+
) {
225+
if mouse_button_input.just_pressed(MouseButton::Left) {
226+
if let Some(position) = windows.primary().cursor_position() {
227+
let screen = Vec2::new(windows.primary().width(), windows.primary().height());
228+
let factor = (screen.x / mesh.size.x).min(screen.y / mesh.size.y);
229+
230+
let in_mesh = (position - screen / 2.0) / factor + mesh.size / 2.0;
231+
if pathmeshes
232+
.get(match mesh.mesh {
233+
CurrentMesh::Simple => &meshes.simple,
234+
CurrentMesh::Arena => &meshes.arena,
235+
CurrentMesh::Aurora => &meshes.aurora,
236+
})
237+
.map(|mesh| mesh.is_in_mesh(in_mesh))
238+
.unwrap_or_default()
239+
{
240+
info!("going to {}", in_mesh);
241+
path_step_event.send(NewPathStepEvent(in_mesh));
242+
} else {
243+
info!("clicked outside of mesh");
244+
}
245+
}
246+
}
247+
}
248+
249+
fn compute_paths(
250+
mut event_new_step_path: EventReader<NewPathStepEvent>,
251+
mut path_to_display: ResMut<PathToDisplay>,
252+
mesh: Res<MeshDetails>,
253+
meshes: Res<Meshes>,
254+
pathmeshes: Res<Assets<PathMesh>>,
255+
) {
256+
for ev in event_new_step_path.iter() {
257+
if path_to_display.steps.is_empty() {
258+
path_to_display.steps.push(ev.0);
259+
return;
260+
}
261+
262+
let path_mesh = pathmeshes
263+
.get(match mesh.mesh {
264+
CurrentMesh::Simple => &meshes.simple,
265+
CurrentMesh::Arena => &meshes.arena,
266+
CurrentMesh::Aurora => &meshes.aurora,
267+
})
268+
.unwrap();
269+
if let Some(path) = path_mesh.path(*path_to_display.steps.last().unwrap(), ev.0) {
270+
for p in path.path {
271+
path_to_display.steps.push(p);
272+
}
273+
} else {
274+
info!("no path found");
275+
}
276+
}
277+
}
278+
279+
fn update_path_display(
280+
path_to_display: Res<PathToDisplay>,
281+
mut lines: ResMut<DebugLines>,
282+
mesh: Res<MeshDetails>,
283+
windows: Res<Windows>,
284+
) {
285+
let window = windows.primary();
286+
let factor = (window.width() / mesh.size.x).min(window.height() / mesh.size.y);
287+
288+
(1..path_to_display.steps.len()).for_each(|i| {
289+
lines.line(
290+
((path_to_display.steps[i - 1] - mesh.size / 2.0) * factor).extend(0f32),
291+
((path_to_display.steps[i] - mesh.size / 2.0) * factor).extend(0f32),
292+
0f32,
293+
);
294+
});
295+
}

0 commit comments

Comments
 (0)