fix: randomised target positions

This commit is contained in:
Robert Fry 2025-01-07 14:17:30 +00:00
parent 490fef5393
commit eb67c2bf23
Signed by: robertfry
GPG Key ID: E89FFC8597BFE26C

View File

@ -1,4 +1,6 @@
use std::collections::HashMap;
use bevy::prelude::*; use bevy::prelude::*;
use avian3d::prelude::*; use avian3d::prelude::*;
@ -20,35 +22,90 @@ impl Plugin for TargetWallPlugin
{ {
fn build(&self, app: &mut App) fn build(&self, app: &mut App)
{ {
app.insert_resource(TargetResources {
target_separation: 0.1,
target_handle_counter: 0,
targets: HashMap::new(),
});
app.add_systems(Startup, setup_target_wall); app.add_systems(Startup, setup_target_wall);
app.add_systems(PostProcessCollisions, on_targets_shot.run_if(on_event::<CollisionEnded>)); app.add_systems(PostProcessCollisions, on_targets_shot.run_if(on_event::<CollisionEnded>));
} }
} }
#[derive(Component)] #[derive(Component, Copy, Clone, PartialEq, Eq, Hash)]
struct Target; struct TargetHandle(i32); // todo: would be nice to use the Entity handle here
impl Target #[derive(Resource)]
struct TargetResources
{ {
fn random_transform() -> Transform target_separation: f32,
{ target_handle_counter: i32,
let radius = rand::random::<f32>() * 0.4 + 0.3; targets: HashMap<TargetHandle,(Vec2,Circle)>,
let x = rand::random::<f32>() * (WALL_SIZE.x - radius) - WALL_SIZE.x * 0.5; }
let y = rand::random::<f32>() * (WALL_SIZE.y - radius);
Transform::IDENTITY impl TargetResources
.with_translation(Vec3::new(x, y, WALL_FACE_Z + TARGET_HEIGHT * 0.5)) {
.with_rotation(Quat::from_rotation_x(std::f32::consts::PI * 0.5)) fn new_handle(&mut self) -> TargetHandle
.with_scale(Vec3::new(radius, 1.0, radius)) {
self.target_handle_counter += 1;
TargetHandle(self.target_handle_counter)
}
fn random_transform(&mut self, handle: &TargetHandle) -> Transform
{
self.targets.remove(&handle);
let (transform, cache_entry) = loop
{
let radius = rand::random::<f32>() * 0.4 + 0.3;
let x = rand::random::<f32>() * (WALL_SIZE.x - radius) - WALL_SIZE.x * 0.5;
let y = rand::random::<f32>() * (WALL_SIZE.y - radius) - WALL_SIZE.y * 0.5;
let position = Vec2::new(x, y);
let circle = Circle::new(radius);
let has_overlap = self.targets.values().any(|&(position_other, circle_other)|
{
let distance_squared = Vec2::distance_squared(position, position_other);
let max_distance = circle.radius + circle_other.radius + self.target_separation;
distance_squared < max_distance * max_distance
});
if has_overlap {
continue;
}
let transform = Transform::IDENTITY
.with_translation(Vec3::new(x, y, TARGET_HEIGHT * 0.5))
.with_rotation(Quat::from_rotation_x(std::f32::consts::PI * 0.5))
.with_scale(Vec3::new(radius, 1.0, radius))
;
break (transform, (position, circle));
};
self.targets.insert(*handle, cache_entry);
return transform;
} }
} }
#[derive(Component)]
pub struct TargetWall;
#[derive(Component)]
pub struct Target;
fn setup_target_wall( fn setup_target_wall(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut target_resources: ResMut<TargetResources>,
) { ) {
commands.spawn(( // spawn the wall
//
let mut wall_commands = commands.spawn((
TargetWall,
Mesh3d(meshes.add(Cuboid::from_corners( Mesh3d(meshes.add(Cuboid::from_corners(
Vec3::ZERO, WALL_SIZE Vec3::ZERO, WALL_SIZE
))), ))),
@ -56,7 +113,7 @@ fn setup_target_wall(
base_color: WALL_COLOR, base_color: WALL_COLOR,
..default() ..default()
})), })),
Transform::from_xyz(0.0, WALL_SIZE.y * 0.5, WALL_FACE_Z - WALL_SIZE.z * 0.5), Transform::from_xyz(0.0, WALL_SIZE.y * 0.5, WALL_FACE_Z),
Collider::cuboid(WALL_SIZE.x, WALL_SIZE.y, WALL_SIZE.z * 0.5), Collider::cuboid(WALL_SIZE.x, WALL_SIZE.y, WALL_SIZE.z * 0.5),
RigidBody::Static, RigidBody::Static,
)); ));
@ -73,35 +130,49 @@ fn setup_target_wall(
TARGET_RADIUS, TARGET_HEIGHT TARGET_RADIUS, TARGET_HEIGHT
); );
let targets: Box<[_;TARGET_COUNT]> = Box::new(std::array::from_fn(|_| ( // spawn the targets as children of the wall
Target, //
Mesh3d(target_mesh.clone()), (0..TARGET_COUNT).for_each(|_|
MeshMaterial3d(target_material.clone()), {
Target::random_transform(), let handle = target_resources.new_handle();
target_collider.clone(), let transform = target_resources.random_transform(&handle);
RigidBody::Static,
))); wall_commands.with_child((
commands.spawn_batch(targets.into_iter()); Target,
handle,
Mesh3d(target_mesh.clone()),
MeshMaterial3d(target_material.clone()),
transform,
target_collider.clone(),
RigidBody::Static,
));
});
} }
fn on_targets_shot( fn on_targets_shot(
mut collision_events: EventReader<CollisionEnded>, mut collision_events: EventReader<CollisionEnded>,
mut bullet_query: Query<Entity, With<Bullet>>, mut query_set: ParamSet<(
mut target_query: Query<&mut Transform, With<Target>>, Query<Entity, With<Bullet>>,
Query<(&TargetHandle, &mut Transform), With<Target>>,
)>,
mut target_resources: ResMut<TargetResources>,
) { ) {
for collision in collision_events.read() for collision in collision_events.read()
{ {
// resolve the query data from the collision, if the collision is // resolve the query data from the collision, if the collision is
// between a bullet and a target // between a bullet and a target
// //
let Ok(_) = collision.query_unique(&mut bullet_query) else { let bullet_query = &mut query_set.p0();
continue; let Ok(_) = collision.query_unique(bullet_query) else {
continue; // the collision doesn't involve a bullet
}; };
let Ok(mut target_transform) = collision.query_unique(&mut target_query) else { //
continue; let target_query = &mut query_set.p1();
let Ok((target_handle, mut target_transform)) = collision.query_unique(target_query) else {
continue; // the collision doesn't involve a target
}; };
// move the target // move the target
*target_transform = Target::random_transform(); *target_transform = target_resources.random_transform(target_handle);
} }
} }