Hello!
For this blog update, I mostly want to talk about spawning in objects. So previously, my natural forest objects would randomly spawn on my scene within set bounds… however, they would randomly spawn wherever at will, which means that they could potentially (and DID) spawn on TOP OF EACH OTHER!
Well… For a simulated environment… that’s much less than ideal. If there’s a tree in a tree that sounds pretty neat, but isn’t too realistic (especially since there’s no merging). So to fix this, I had to add some sort of system that checked if an object already spawned in a spot before spawning another. This sounds super simple… but for some reason I had the hardest time implementing this (mostly due to the whole “world.spawn” thing confusing me, and I had to be careful with mutable/immutable variables and when I could use them).
So…. In order to prevent overlap this, I decided to add a mini circle “collider” to each object, which I could then use to check for overlap. Well, since each object is a different size, and a mushroom wouldn’t need as big a circle as say, a tree, I added different radius sizes to each NaturalObject. This is within my forest.rs:
pub const ASSETS: [NaturalObject; 13] = [
NaturalObject {
name: "coniferousa",
scale: 1.0,
radius: 15.0,
},
NaturalObject {
name: "mushroom",
scale: 1.5,
radius: 5.0,
},
Here’s an example of some of my objects. I can give a mushroom, a small object, a small radius of 5, while a big tree could have a much larger radius. Then, when creating an object within the scene (nature.rs), I can add this radius to an actual circle:
#[derive(Default)]
pub struct Circle {
center: Vec2,
radius: f32,
}
Then, in order to check for overlap, I can use only spawn an object if overlap != true. So when spawning, I check only spawn if the following function is true:
fn check_overlap(
new_position: Vec3,
new_radius: f32,
existing_positions: &[Vec3],
existing_radii: &[f32],
) -> bool {
for (i, position) in existing_positions.iter().enumerate() {
let distance = position.distance(new_position);
if distance < new_radius + existing_radii[i] {
return true;
}
}
false
}
Please excuse the formatting. But basically, I just check if the potential spawn point is overlapping with an already created circle. This is created and called within my create_object function:
fn create_object(
scene_handle: Handle<Scene>,
object: &NaturalObject,
existing_positions: &[Vec3],
existing_radii: &[f32],
) -> Option<SceneBundle> {
let mut circle = AddNature::create_circle(object.radius);
let mut attempts = 0;
loop {
// randomize position until non-overlapping position is found
let position = Vec3::new(
fastrand::f32() * 450.0 - 225.0,
fastrand::f32() * 450.0 - 225.0,
0.0,
);
circle.center = position.truncate();
if !AddNature::check_overlap(position, object.radius, existing_positions, existing_radii) {
return Some(SceneBundle {
scene: scene_handle,
transform: Transform::from_translation(position)
.with_scale(Vec3::splat(object.scale))
.with_rotation(Quat::from_rotation_x(std::f32::consts::FRAC_PI_2)),
..default()
});
}
attempts += 1;
if attempts > 10 {
return None;
}
}
}
This basically says to only create an object if its circle does not overlap with an already existing circle.