194 lines
5.6 KiB
Rust
194 lines
5.6 KiB
Rust
|
use rand::prelude::*;
|
||
|
use bevy::{
|
||
|
prelude::*,
|
||
|
};
|
||
|
use heron::prelude::*;
|
||
|
use crate::Layer;
|
||
|
|
||
|
pub struct LevelResources {
|
||
|
soil_material: Handle<StandardMaterial>,
|
||
|
grass_material: Handle<StandardMaterial>,
|
||
|
last_build: Option<Time>,
|
||
|
}
|
||
|
|
||
|
#[derive(Component)]
|
||
|
pub struct GroundContact(pub usize);
|
||
|
|
||
|
impl std::default::Default for GroundContact {
|
||
|
fn default() -> Self {
|
||
|
GroundContact(0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Component)]
|
||
|
pub struct Ground {
|
||
|
/// (x0, width)
|
||
|
x: (f32, f32),
|
||
|
/// ground top
|
||
|
y: f32,
|
||
|
/// (z0, 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,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub fn to_box(&self) -> shape::Box {
|
||
|
let half_width = self.x.1 / 2.0;
|
||
|
let half_height = Self::HEIGHT / 2.0;
|
||
|
let half_depth = self.z.1 / 2.0;
|
||
|
shape::Box {
|
||
|
min_x: - half_width,
|
||
|
min_y: - half_height,
|
||
|
min_z: - half_depth,
|
||
|
max_x: half_width,
|
||
|
max_y: half_height,
|
||
|
max_z: half_depth,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn half_extends(&self) -> Vec3 {
|
||
|
let half_width = self.x.1 / 2.0;
|
||
|
let half_height = Self::HEIGHT / 2.0;
|
||
|
let half_depth = self.z.1 / 2.0;
|
||
|
Vec3::new(half_width, half_height, half_depth)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn setup(
|
||
|
mut commands: Commands,
|
||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||
|
) {
|
||
|
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()),
|
||
|
last_build: None,
|
||
|
};
|
||
|
commands.insert_resource(res);
|
||
|
}
|
||
|
|
||
|
pub fn build(
|
||
|
time: Res<Time>,
|
||
|
mut commands: Commands,
|
||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||
|
mut res: ResMut<LevelResources>,
|
||
|
grounds: Query<&Ground>,
|
||
|
) {
|
||
|
// 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 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !collision {
|
||
|
add_ground(&mut commands, &mut meshes, &res, ground);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
res.last_build = Some(time.clone());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn add_ground(
|
||
|
commands: &mut Commands,
|
||
|
meshes: &mut ResMut<Assets<Mesh>>,
|
||
|
res: &ResMut<LevelResources>,
|
||
|
ground: Ground,
|
||
|
) {
|
||
|
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()
|
||
|
})
|
||
|
.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)
|
||
|
}
|
||
|
|
||
|
events
|
||
|
.iter()
|
||
|
// We care about when the entities "start" to collide
|
||
|
.filter_map(|event| {
|
||
|
let (entity_1, _entity_2) = event.rigid_body_entities();
|
||
|
let (layers_1, layers_2) = event.collision_layers();
|
||
|
if ! is_map(layers_1) && is_map(layers_2) {
|
||
|
Some((event, entity_1))
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}).for_each(|(event, entity)| {
|
||
|
match event {
|
||
|
CollisionEvent::Started(_, _) => {
|
||
|
if let Ok(mut contact) = contacted.get_mut(entity) {
|
||
|
contact.0 = contact.0.saturating_add(1);
|
||
|
}
|
||
|
}
|
||
|
CollisionEvent::Stopped(_, _) => {
|
||
|
if let Ok(mut contact) = contacted.get_mut(entity) {
|
||
|
contact.0 = contact.0.saturating_sub(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|