Skip to content

Commit d9c00a0

Browse files
authored
Merge pull request #2 from Earthmark/earthmark-dev
Updates to add some basic maze generation, and to check the asset loaders.
2 parents 1970943 + 68df0a5 commit d9c00a0

5 files changed

Lines changed: 271 additions & 67 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ version = "0.1.0"
66

77
[dependencies]
88
bevy = "0.6"
9+
futures-lite = "1"
910
rand = "0.8"
1011

1112
# Enable only a small amount of optimization in debug mode

src/async_promoter.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use bevy::{prelude::*, tasks::Task};
2+
use futures_lite::future;
3+
4+
pub fn promote_task_component<Comp: Component>(
5+
mut commands: Commands,
6+
mut task_waiter: Query<(Entity, &mut Task<Comp>)>,
7+
) {
8+
for (entity, mut task) in task_waiter.iter_mut() {
9+
if let Some(comp) = future::block_on(future::poll_once(&mut *task)) {
10+
// Remote the task variant and replace it with the concrete variant.
11+
commands.entity(entity).remove::<Task<Comp>>().insert(comp);
12+
}
13+
}
14+
}

src/level.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
use std::time::Instant;
2+
3+
use crate::maze;
4+
use bevy::{prelude::*, tasks::AsyncComputeTaskPool};
5+
use rand::prelude::*;
6+
7+
struct LevelAdaptor<const DIMS: usize> {
8+
position: [usize; DIMS],
9+
maze: maze::Maze<DIMS>,
10+
}
11+
12+
impl<const DIMS: usize> LevelAdaptor<DIMS> {
13+
fn should_make_wall(&self, position: &[usize; DIMS], direction: usize) -> bool {
14+
if let Some(walkable) = self.maze.can_move(position, direction) {
15+
!walkable
16+
} else {
17+
false
18+
}
19+
}
20+
}
21+
22+
impl<const DIMS: usize> MazeLevel for LevelAdaptor<DIMS> {
23+
fn dims(&self) -> usize {
24+
DIMS
25+
}
26+
27+
fn length_of_dim(&self, dim: usize) -> Option<usize> {
28+
self.maze.lengths().get(dim).copied()
29+
}
30+
31+
fn generate_walls(&self, dim_x: usize, dim_y: usize) {
32+
// If we're mapping the same dimension, bail out.
33+
// If we're not accessing the correct dimensions, bail out.
34+
if dim_x == dim_y || dim_x >= DIMS || dim_y >= DIMS {
35+
return;
36+
}
37+
for cursor_x in 0..self.maze.lengths()[dim_x] {
38+
for cursor_y in 0..self.maze.lengths()[dim_y] {
39+
let mut cursor = self.position;
40+
cursor[dim_x] = cursor_x;
41+
cursor[dim_y] = cursor_y;
42+
43+
if self.should_make_wall(&cursor, dim_x) {}
44+
if self.should_make_wall(&cursor, dim_y) {}
45+
}
46+
}
47+
}
48+
}
49+
50+
trait MazeLevel: Sync + Send {
51+
fn dims(&self) -> usize;
52+
fn length_of_dim(&self, dim: usize) -> Option<usize>;
53+
fn generate_walls(&self, dim_x: usize, dim_y: usize);
54+
}
55+
56+
#[derive(Component, Clone, Debug)]
57+
pub struct LevelLoader {
58+
pub rng_source: RngSource,
59+
pub dimensions: DimensionLength,
60+
}
61+
62+
#[derive(Clone, Debug)]
63+
pub enum RngSource {
64+
Seeded(u64),
65+
}
66+
67+
// Remove this once construction methods for dimensions are found.
68+
#[allow(dead_code)]
69+
#[derive(Clone, Debug)]
70+
pub enum DimensionLength {
71+
Two([usize; 2]),
72+
Three([usize; 3]),
73+
Four([usize; 4]),
74+
Five([usize; 5]),
75+
Six([usize; 6]),
76+
}
77+
78+
impl Default for LevelLoader {
79+
fn default() -> Self {
80+
Self {
81+
rng_source: RngSource::Seeded(123456789),
82+
dimensions: DimensionLength::Two([2, 2]),
83+
}
84+
}
85+
}
86+
87+
#[derive(Default, Bundle)]
88+
pub struct LevelLoaderBundle {
89+
pub level_loader: LevelLoader,
90+
pub transform: Transform,
91+
pub global_transform: GlobalTransform,
92+
}
93+
94+
impl LevelLoader {
95+
pub fn load(&self) -> Level {
96+
let mut rng = match self.rng_source {
97+
RngSource::Seeded(seed) => StdRng::seed_from_u64(seed),
98+
};
99+
match self.dimensions {
100+
DimensionLength::Two(lengths) => Level::new(&lengths, &mut rng),
101+
DimensionLength::Three(lengths) => Level::new(&lengths, &mut rng),
102+
DimensionLength::Four(lengths) => Level::new(&lengths, &mut rng),
103+
DimensionLength::Five(lengths) => Level::new(&lengths, &mut rng),
104+
DimensionLength::Six(lengths) => Level::new(&lengths, &mut rng),
105+
}
106+
}
107+
}
108+
109+
#[derive(Component)]
110+
pub struct Level {
111+
maze: Box<dyn MazeLevel>,
112+
dim_x: usize,
113+
dim_y: usize,
114+
joint: Handle<Mesh>,
115+
wall: Handle<Mesh>,
116+
material: Handle<StandardMaterial>,
117+
}
118+
119+
impl Level {
120+
fn new<const DIMS: usize>(lengths: &[usize; DIMS], rng: &mut impl rand::Rng) -> Self {
121+
Level {
122+
maze: Box::new(LevelAdaptor {
123+
maze: maze::Maze::new(lengths, rng),
124+
position: [0; DIMS],
125+
}),
126+
dim_x: 0,
127+
dim_y: 1,
128+
joint: Default::default(),
129+
wall: Default::default(),
130+
material: Default::default(),
131+
}
132+
}
133+
fn generate_walls(&self, dim_x: usize, dim_y: usize) {
134+
self.maze.generate_walls(dim_x, dim_y)
135+
}
136+
137+
// assume dim_x and dim_y are both together.
138+
fn length_x(&self) -> usize {
139+
self.maze.length_of_dim(self.dim_x).unwrap()
140+
}
141+
142+
fn length_y(&self) -> usize {
143+
self.maze.length_of_dim(self.dim_y).unwrap()
144+
}
145+
}
146+
147+
pub struct LevelPlugin;
148+
149+
impl Plugin for LevelPlugin {
150+
fn build(&self, app: &mut App) {
151+
app.add_system(spawn_level_system)
152+
.add_system(level_generation_system)
153+
.add_system(crate::async_promoter::promote_task_component::<Level>);
154+
}
155+
}
156+
157+
fn spawn_level_system(
158+
mut commands: Commands,
159+
query: Query<(Entity, &LevelLoader), Added<LevelLoader>>,
160+
thread_pool: Res<AsyncComputeTaskPool>,
161+
mut meshes: ResMut<Assets<Mesh>>,
162+
mut materials: ResMut<Assets<StandardMaterial>>,
163+
) {
164+
for (entity, level_loader) in query.iter() {
165+
let local_loader = level_loader.clone();
166+
let joint = meshes.add(Mesh::from(shape::Cube { size: 0.3 }));
167+
let wall = meshes.add(Mesh::from(shape::Cube { size: 0.1 }));
168+
let material = materials.add(Color::rgb(0.8, 0.7, 0.6).into());
169+
commands
170+
.entity(entity)
171+
.insert(thread_pool.spawn(async move {
172+
info!("Generating maze from {:?}", local_loader);
173+
let now = Instant::now();
174+
let mut level = local_loader.load();
175+
info!("Maze generated in {:?}", now.elapsed());
176+
level.joint = joint;
177+
level.wall = wall;
178+
level.material = material;
179+
level
180+
}));
181+
}
182+
}
183+
184+
fn level_generation_system(mut commands: Commands, query: Query<(Entity, &Level), Changed<Level>>) {
185+
for (entity, level) in query.iter() {
186+
info!("Spawning level");
187+
commands.entity(entity).with_children(move |builder| {
188+
builder.spawn().with_children(move |builder| {
189+
for x in 0..level.length_x() {
190+
for y in 0..level.length_y() {
191+
info!("Spawning pillar at [{},{}]", x, y);
192+
builder.spawn_bundle(PbrBundle {
193+
mesh: level.joint.clone(),
194+
material: level.material.clone(),
195+
transform: Transform::from_xyz(x as f32, 0.0, y as f32),
196+
..Default::default()
197+
});
198+
}
199+
}
200+
201+
level.generate_walls(level.dim_x, level.dim_y);
202+
});
203+
});
204+
}
205+
}

src/main.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
#![windows_subsystem = "windows"]
1+
//#![windows_subsystem = "windows"]
22

3+
mod async_promoter;
4+
mod level;
35
mod maze;
46

57
use bevy::prelude::*;
68

79
fn main() {
810
App::new()
911
.add_plugins(DefaultPlugins)
12+
.add_plugin(level::LevelPlugin)
1013
.add_startup_system(setup)
1114
.run();
1215
}
@@ -17,4 +20,26 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1720
texture: asset_server.load("textures/icon.png"),
1821
..Default::default()
1922
});
23+
commands.spawn_bundle(PointLightBundle {
24+
point_light: PointLight {
25+
intensity: 1500.0,
26+
shadows_enabled: true,
27+
..Default::default()
28+
},
29+
transform: Transform::from_xyz(4.0, 8.0, 4.0),
30+
..Default::default()
31+
});
32+
33+
commands.spawn_bundle(PerspectiveCameraBundle {
34+
transform: Transform::from_xyz(10.2, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
35+
..Default::default()
36+
});
37+
38+
commands.spawn_bundle(level::LevelLoaderBundle {
39+
level_loader: level::LevelLoader {
40+
dimensions: level::DimensionLength::Four([6, 6, 6, 6]),
41+
..Default::default()
42+
},
43+
..Default::default()
44+
});
2045
}

0 commit comments

Comments
 (0)