overhaul map generation
This commit is contained in:
parent
a0f3e44245
commit
1985db2d17
|
@ -31,8 +31,7 @@ fn main() {
|
|||
.add_startup_system(player::setup)
|
||||
.add_system(player::input)
|
||||
.add_system(exit_on_escape)
|
||||
.add_system(log_collisions)
|
||||
// .add_system(keyboard_input_camera)
|
||||
// .add_system(log_collisions)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
291
src/map.rs
291
src/map.rs
|
@ -1,16 +1,41 @@
|
|||
use std::collections::HashMap;
|
||||
use rand::prelude::*;
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
};
|
||||
use heron::prelude::*;
|
||||
use crate::Layer;
|
||||
use crate::{
|
||||
player::Player,
|
||||
Layer,
|
||||
};
|
||||
|
||||
pub struct LevelResources {
|
||||
soil_material: Handle<StandardMaterial>,
|
||||
grass_material: Handle<StandardMaterial>,
|
||||
bridge_material: Handle<StandardMaterial>,
|
||||
last_build: Option<Time>,
|
||||
}
|
||||
|
||||
const BUILD_RANGE: f32 = 70.0;
|
||||
const ISLAND_SPACING: f32 = 30.0;
|
||||
|
||||
const GRASS_HEIGHT: f32 = 0.3;
|
||||
const BRIDGE_WIDTH: f32 = 2.0;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Island {
|
||||
/// x grid
|
||||
u: i32,
|
||||
/// z grid
|
||||
v: i32,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Bridge {
|
||||
from: (i32, i32),
|
||||
to: (i32, i32),
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct GroundContact(pub usize);
|
||||
|
||||
|
@ -22,38 +47,26 @@ impl std::default::Default for GroundContact {
|
|||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Ground {
|
||||
/// (x0, width)
|
||||
/// (center x, width)
|
||||
x: (f32, f32),
|
||||
/// ground top
|
||||
y: f32,
|
||||
/// (z0, width)
|
||||
/// (center y, height)
|
||||
y: (f32, f32),
|
||||
/// (center z, width)
|
||||
z: (f32, f32),
|
||||
}
|
||||
|
||||
impl Ground {
|
||||
const HEIGHT: f32 = 2.5;
|
||||
|
||||
pub fn overlaps(&self, other: &Self) -> bool {
|
||||
self.x.0 + self.x.1 >= other.x.0 &&
|
||||
self.x.0 <= other.x.0 + other.x.1 &&
|
||||
self.z.0 + self.z.1 >= other.z.0 &&
|
||||
self.z.0 <= other.z.0 + other.z.1
|
||||
}
|
||||
|
||||
pub fn to_transform(&self) -> Transform {
|
||||
let half_width = self.x.1 / 2.0;
|
||||
let half_height = Self::HEIGHT / 2.0;
|
||||
let half_depth = self.z.1 / 2.0;
|
||||
Transform::from_xyz(
|
||||
self.x.0 + half_width,
|
||||
self.y + half_height,
|
||||
self.z.0 + half_depth,
|
||||
self.x.0,
|
||||
self.y.0,
|
||||
self.z.0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_box(&self) -> shape::Box {
|
||||
let half_width = self.x.1 / 2.0;
|
||||
let half_height = Self::HEIGHT / 2.0;
|
||||
let half_height = self.y.1 / 2.0;
|
||||
let half_depth = self.z.1 / 2.0;
|
||||
shape::Box {
|
||||
min_x: - half_width,
|
||||
|
@ -67,7 +80,7 @@ impl Ground {
|
|||
|
||||
pub fn half_extends(&self) -> Vec3 {
|
||||
let half_width = self.x.1 / 2.0;
|
||||
let half_height = Self::HEIGHT / 2.0;
|
||||
let half_height = self.y.1 / 2.0;
|
||||
let half_depth = self.z.1 / 2.0;
|
||||
Vec3::new(half_width, half_height, half_depth)
|
||||
}
|
||||
|
@ -80,6 +93,7 @@ pub fn setup(
|
|||
let res = LevelResources {
|
||||
soil_material: materials.add(Color::rgb(0.8, 0.7, 0.1).into()),
|
||||
grass_material: materials.add(Color::rgb(0.2, 1.0, 0.25).into()),
|
||||
bridge_material: materials.add(Color::rgb(0.5, 0.4, 0.1).into()),
|
||||
last_build: None,
|
||||
};
|
||||
commands.insert_resource(res);
|
||||
|
@ -90,76 +104,213 @@ pub fn build(
|
|||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut res: ResMut<LevelResources>,
|
||||
grounds: Query<&Ground>,
|
||||
islands: Query<(Entity, &Island)>,
|
||||
bridges: Query<(Entity, &Bridge)>,
|
||||
player_transforms: Query<&Transform, With<Player>>,
|
||||
) {
|
||||
// let now = time.seconds_since_startup();
|
||||
if res.last_build.is_none() //res.last_build.as_ref().map_or(true, |last_build| now - last_build.seconds_since_startup() >= 0.1)
|
||||
let now = time.seconds_since_startup();
|
||||
if res.last_build.as_ref().map_or(true, |last_build| now - last_build.seconds_since_startup() >= 0.1)
|
||||
{
|
||||
let mut rng = rand::thread_rng();
|
||||
for z in -9..10 {
|
||||
for x in -9..10 {
|
||||
let ground = Ground {
|
||||
x: (2.0 * x as f32, 2.0),
|
||||
y: rng.gen_range::<f32, _>(0.0..2.0),
|
||||
z: (2.0 * z as f32, 2.0),
|
||||
};
|
||||
let mut collision = false;
|
||||
for other in grounds.iter() {
|
||||
if ground.overlaps(other) {
|
||||
// println!("collision: {:?}x{:?} && {:?}x{:?}", ground.x, ground.z, other.x, other.z);
|
||||
collision = true;
|
||||
break;
|
||||
let mut min_x = None;
|
||||
let mut max_x = None;
|
||||
let mut min_z = None;
|
||||
let mut max_z = None;
|
||||
for transform in player_transforms.iter() {
|
||||
let t = &transform.translation;
|
||||
|
||||
if min_x.map_or(true, |min_x| t.x < min_x) {
|
||||
min_x = Some(t.x);
|
||||
}
|
||||
if max_x.map_or(true, |max_x| t.x > max_x) {
|
||||
max_x = Some(t.x);
|
||||
}
|
||||
if min_z.map_or(true, |min_z| t.z < min_z) {
|
||||
min_z = Some(t.z);
|
||||
}
|
||||
if max_z.map_or(true, |max_z| t.z > max_z) {
|
||||
max_z = Some(t.z);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((u_range, v_range)) = Some(()).and_then(|()| {
|
||||
Some((
|
||||
((min_x? - BUILD_RANGE) / ISLAND_SPACING) as i32
|
||||
..=
|
||||
((max_x? + BUILD_RANGE) / ISLAND_SPACING) as i32,
|
||||
((min_z? - BUILD_RANGE) / ISLAND_SPACING) as i32
|
||||
..=
|
||||
((max_z? + BUILD_RANGE) / ISLAND_SPACING) as i32
|
||||
))
|
||||
}) {
|
||||
// collect already-built island
|
||||
let mut islands_built = HashMap::new();
|
||||
for (entity, island) in islands.iter() {
|
||||
islands_built.insert((island.u, island.v), entity);
|
||||
}
|
||||
// collect already-built bridges
|
||||
let mut bridges_built = HashMap::new();
|
||||
for (entity, bridge) in bridges.iter() {
|
||||
bridges_built.insert((bridge.from, bridge.to), entity);
|
||||
}
|
||||
|
||||
// build new islands where needed
|
||||
let mut rng = rand::thread_rng();
|
||||
for v in v_range {
|
||||
for u in u_range.clone() {
|
||||
let x = u as f32 * ISLAND_SPACING;
|
||||
let z = v as f32 * ISLAND_SPACING;
|
||||
|
||||
if islands_built.remove(&(u, v)).is_none() {
|
||||
let step = rng.gen_range(0.2..1.0);
|
||||
let top = step + rng.gen_range(0.5..1.0);
|
||||
let height = rng.gen_range(4.0..128.0);
|
||||
let width = rng.gen_range(8.0..20.0);
|
||||
let depth = rng.gen_range(8.0..15.0);
|
||||
// central hill
|
||||
let center = Ground {
|
||||
x: (x, width),
|
||||
y: ((top - height) / 2.0, height + top),
|
||||
z: (z, depth),
|
||||
};
|
||||
// steps
|
||||
let north = Ground {
|
||||
x: (x, rng.gen_range(BRIDGE_WIDTH..width)),
|
||||
y: ((step - height) / 2.0, height + step),
|
||||
z: (z - (depth / 2.0) - 1.0, 2.0),
|
||||
};
|
||||
let south = Ground {
|
||||
x: (x, rng.gen_range(BRIDGE_WIDTH..width)),
|
||||
y: ((step - height) / 2.0, height + step),
|
||||
z: (z + (depth / 2.0) + 1.0, 2.0),
|
||||
};
|
||||
let east = Ground {
|
||||
x: (x - (width / 2.0) - 1.0, 2.0),
|
||||
y: ((step - height) / 2.0, height + step),
|
||||
z: (z, rng.gen_range(BRIDGE_WIDTH..depth)),
|
||||
};
|
||||
let west = Ground {
|
||||
x: (x + (width / 2.0) + 1.0, 2.0),
|
||||
y: ((step - height) / 2.0, height + step),
|
||||
z: (z, rng.gen_range(BRIDGE_WIDTH..depth)),
|
||||
};
|
||||
let grounds = vec![center, north, south, east, west];
|
||||
let island = Island { u, v };
|
||||
add_island(&mut commands, &mut meshes, &res, grounds, island);
|
||||
}
|
||||
|
||||
if bridges_built.remove(&((u, v), (u + 1, v))).is_none() {
|
||||
let ground = Ground {
|
||||
x: (x + 0.5 * ISLAND_SPACING, ISLAND_SPACING),
|
||||
y: (0.0, 0.25),
|
||||
z: (z, BRIDGE_WIDTH),
|
||||
};
|
||||
let bridge = Bridge {
|
||||
from: (u, v),
|
||||
to: (u + 1, v),
|
||||
};
|
||||
add_bridge(&mut commands, &mut meshes, &res, ground, bridge);
|
||||
}
|
||||
|
||||
if bridges_built.remove(&((u, v), (u, v + 1))).is_none() {
|
||||
let ground = Ground {
|
||||
x: (x, BRIDGE_WIDTH),
|
||||
y: (0.0, 0.25),
|
||||
z: (z + 0.5 * ISLAND_SPACING, ISLAND_SPACING),
|
||||
};
|
||||
let bridge = Bridge {
|
||||
from: (u, v),
|
||||
to: (u, v + 1),
|
||||
};
|
||||
add_bridge(&mut commands, &mut meshes, &res, ground, bridge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !collision {
|
||||
add_ground(&mut commands, &mut meshes, &res, ground);
|
||||
}
|
||||
// remove remaining built islands where not needed
|
||||
for (_, entity) in islands_built.into_iter() {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
|
||||
// remove remaining built bridges where not needed
|
||||
for (_, entity) in bridges_built.into_iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
res.last_build = Some(time.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn add_ground(
|
||||
fn add_island(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut ResMut<Assets<Mesh>>,
|
||||
res: &ResMut<LevelResources>,
|
||||
grounds: Vec<Ground>,
|
||||
island: Island,
|
||||
) {
|
||||
commands.spawn()
|
||||
.insert(GlobalTransform::default())
|
||||
.insert(Transform::default())
|
||||
.insert(island)
|
||||
.with_children(|children| {
|
||||
for ground in grounds {
|
||||
let transform = ground.to_transform();
|
||||
|
||||
let bounds = ground.to_box();
|
||||
let mut soil_box = bounds.clone();
|
||||
soil_box.max_y = soil_box.max_y - GRASS_HEIGHT;
|
||||
let mut grass_box = bounds.clone();
|
||||
grass_box.min_y = soil_box.max_y;
|
||||
|
||||
let soil_mesh = meshes.add(Mesh::from(soil_box));
|
||||
let grass_mesh = meshes.add(Mesh::from(grass_box));
|
||||
children.spawn()
|
||||
.insert(RigidBody::Static)
|
||||
.insert(CollisionShape::Cuboid {
|
||||
border_radius: None,
|
||||
half_extends: ground.half_extends(),
|
||||
})
|
||||
// .insert(ground)
|
||||
.insert(GlobalTransform::default())
|
||||
.insert(transform)
|
||||
.with_children(|children| {
|
||||
children.spawn_bundle(PbrBundle {
|
||||
mesh: soil_mesh,
|
||||
material: res.soil_material.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
children.spawn_bundle(PbrBundle {
|
||||
mesh: grass_mesh,
|
||||
material: res.grass_material.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
fn add_bridge(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut ResMut<Assets<Mesh>>,
|
||||
res: &ResMut<LevelResources>,
|
||||
ground: Ground,
|
||||
bridge: Bridge,
|
||||
) {
|
||||
let mut transform = ground.to_transform();
|
||||
|
||||
let bounds = ground.to_box();
|
||||
let mut soil_box = bounds.clone();
|
||||
soil_box.max_y = soil_box.min_y + 0.7 * (soil_box.max_y - soil_box.min_y);
|
||||
let mut grass_box = bounds.clone();
|
||||
grass_box.min_y = soil_box.max_y;
|
||||
|
||||
let soil_mesh = meshes.add(Mesh::from(soil_box));
|
||||
commands.spawn()
|
||||
.insert_bundle(PbrBundle {
|
||||
mesh: soil_mesh,
|
||||
material: res.soil_material.clone(),
|
||||
transform: transform.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let grass_mesh = meshes.add(Mesh::from(grass_box));
|
||||
commands.spawn()
|
||||
.insert_bundle(PbrBundle {
|
||||
mesh: grass_mesh,
|
||||
material: res.grass_material.clone(),
|
||||
transform: transform.clone(),
|
||||
..Default::default()
|
||||
})
|
||||
let transform = ground.to_transform();
|
||||
let bridge_mesh = meshes.add(Mesh::from(ground.to_box()));
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: bridge_mesh,
|
||||
material: res.bridge_material.clone(),
|
||||
transform,
|
||||
..Default::default()
|
||||
})
|
||||
.insert(bridge)
|
||||
.insert(RigidBody::Static)
|
||||
.insert(CollisionShape::Cuboid {
|
||||
border_radius: None,
|
||||
half_extends: ground.half_extends(),
|
||||
})
|
||||
.insert(ground);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn collide(mut events: EventReader<CollisionEvent>, mut contacted: Query<&mut GroundContact>) {
|
||||
fn is_map(layers: CollisionLayers) -> bool {
|
||||
layers.contains_group(Layer::Map)
|
||||
|
|
|
@ -48,23 +48,19 @@ pub fn setup(
|
|||
.insert(GlobalTransform::default())
|
||||
.insert(transform)
|
||||
.with_children(|children| {
|
||||
let transform = Transform::default();
|
||||
children.spawn_bundle(PbrBundle {
|
||||
mesh: mesh1.clone(),
|
||||
material: material1.clone(),
|
||||
transform,
|
||||
..Default::default()
|
||||
});
|
||||
children.spawn_bundle(PbrBundle {
|
||||
mesh: mesh2.clone(),
|
||||
material: material2.clone(),
|
||||
transform,
|
||||
..Default::default()
|
||||
});
|
||||
children.spawn_bundle(PbrBundle {
|
||||
mesh: mesh3.clone(),
|
||||
material: material3.clone(),
|
||||
transform,
|
||||
..Default::default()
|
||||
});
|
||||
let transform = Transform::from_translation(-0.71 * Vec3::Y);
|
||||
|
|
Loading…
Reference in New Issue