Commit: aebd48b
Parent: 7afa3d4

Refactor bunny code using seprate systems

Mårten Åsberg committed on 2026-04-17 at 10:38
src/bunnies/bunny.rs +56 -70
diff --git a/src/bunnies/bunny.rs b/src/bunnies/bunny.rs
index 1cb90e8..d3e0f00 100644
@@ -10,6 +10,7 @@ use bevy::{
system::{Commands, In, Query, Res, ResMut, Single},
},
gltf::GltfAssetLabel,
log::warn,
math::{Dir3, Vec3, primitives::Cuboid},
mesh::{Mesh, Mesh3d, MeshBuilder, Meshable},
scene::SceneRoot,
@@ -88,64 +89,36 @@ fn setup(
}
}
type IdleBunniesQuery<'w, 's, 't> = Query<
'w,
's,
(Entity, &'t mut Transform),
(With<Bunny>, Without<JumpState>, Without<Breeding>),
>;
type IdleBunnyFilter = (With<Bunny>, Without<JumpState>, Without<Breeding>);
fn calculate(
mut commands: Commands,
time: Res<Time>,
locator: Res<Locator>,
dog: Single<&Transform, (With<Dog>, Without<Bunny>)>,
mut bunnies: IdleBunniesQuery,
mut obstacles: Obstacles,
mut rng: ResMut<Rng>,
) {
bunnies.iter_mut().for_each(|(bunny, mut bunny_transform)| {
calculate_next_move(
&mut commands,
&time,
&locator,
&dog,
&mut bunny_transform,
bunny,
&mut obstacles,
&mut rng,
);
fn calculate(mut commands: Commands, mut bunnies: Query<Entity, IdleBunnyFilter>) {
bunnies.iter_mut().for_each(|bunny| {
commands.run_system_cached_with(calculate_next_move, bunny);
});
}
fn calculate_next_move(
commands: &mut Commands,
time: &Time,
locator: &Locator,
dog: &Transform,
bunny_transform: &mut Transform,
bunny: Entity,
obstacles: &mut Obstacles,
rng: &mut Rng,
In(bunny): In<Entity>,
mut commands: Commands,
locator: Res<Locator>,
dog: Single<&Transform, With<Dog>>,
transforms: Query<&mut Transform, Without<Dog>>,
) {
if let Some(direction) = is_dog_nearby(dog, bunny_transform) {
jump_away_from_dog(commands, bunny_transform, bunny, obstacles, direction);
} else if let Some(nearby_bunnies) = is_near_others(locator, bunny, bunny_transform) {
jump_with_others(
commands,
time,
obstacles,
bunny,
bunny_transform,
nearby_bunnies,
rng,
);
let Ok(bunny_transform) = transforms.get(bunny) else {
warn!("Could not find transform for known bunny {bunny}");
return;
};
if let Some(direction) = is_dog_nearby(&dog, bunny_transform) {
commands.run_system_cached_with(jump_away_from_dog, (bunny, direction));
} else if let Some(nearby_bunnies) = is_near_others(&locator, bunny, bunny_transform) {
commands.run_system_cached_with(jump_with_others, (bunny, nearby_bunnies));
} else {
jump_alone(commands, time, obstacles, bunny, bunny_transform, rng);
commands.run_system_cached_with(jump_alone, bunny);
}
}
fn is_dog_nearby(dog: &Transform, bunny_transform: &mut Transform) -> Option<Vec3> {
fn is_dog_nearby(dog: &Transform, bunny_transform: &Transform) -> Option<Vec3> {
let direction = bunny_transform.translation - dog.translation;
if direction.length_squared() > DETECTION_DISTANCE * DETECTION_DISTANCE {
return None;
@@ -154,12 +127,16 @@ fn is_dog_nearby(dog: &Transform, bunny_transform: &mut Transform) -> Option<Vec
}
fn jump_away_from_dog(
commands: &mut Commands,
bunny_transform: &mut Transform,
bunny: Entity,
obstacles: &mut Obstacles,
direction: Vec3,
In((bunny, direction)): In<(Entity, Vec3)>,
mut commands: Commands,
mut transforms: Query<&mut Transform>,
mut obstacles: Obstacles,
) {
let Ok(mut bunny_transform) = transforms.get_mut(bunny) else {
warn!("Could not find transform for known bunny {bunny}");
return;
};
let dog_direction = direction.with_y(0.0).normalize();
let Some(to) = obstacles.avoid(
bunny_transform.translation,
@@ -167,7 +144,7 @@ fn jump_away_from_dog(
) else {
return;
};
start_jump(commands, bunny, bunny_transform, to);
start_jump(&mut commands, bunny, &mut bunny_transform, to);
}
fn is_near_others(
@@ -184,14 +161,18 @@ fn is_near_others(
}
fn jump_with_others(
commands: &mut Commands,
time: &Time,
obstacles: &mut Obstacles,
bunny: Entity,
bunny_transform: &mut Transform,
nearby_bunnies: Vec<(Entity, (Vec3, Dir3))>,
rng: &mut Rng,
In((bunny, nearby_bunnies)): In<(Entity, Vec<(Entity, (Vec3, Dir3))>)>,
mut commands: Commands,
mut transforms: Query<&mut Transform>,
time: Res<Time>,
mut obstacles: Obstacles,
mut rng: ResMut<Rng>,
) {
let Ok(mut bunny_transform) = transforms.get_mut(bunny) else {
warn!("Could not find transform for known bunny {bunny}");
return;
};
if !rng.random_bool((COLLECTIVE_JUMPS_PER_SECOND * time.delta_secs_f64()).clamp(0.0, 1.0)) {
return;
}
@@ -204,7 +185,7 @@ fn jump_with_others(
return;
};
start_jump(commands, bunny, bunny_transform, to);
start_jump(&mut commands, bunny, &mut bunny_transform, to);
fn calculate_average_direction(nearby_bunnies: Vec<(Entity, (Vec3, Dir3))>) -> Vec3 {
let (sin, cos) = nearby_bunnies
@@ -221,13 +202,18 @@ fn jump_with_others(
}
fn jump_alone(
commands: &mut Commands,
time: &Time,
obstacles: &mut Obstacles,
bunny: Entity,
bunny_transform: &mut Transform,
rng: &mut Rng,
In(bunny): In<Entity>,
mut commands: Commands,
mut transforms: Query<&mut Transform>,
time: Res<Time>,
mut obstacles: Obstacles,
mut rng: ResMut<Rng>,
) {
let Ok(mut bunny_transform) = transforms.get_mut(bunny) else {
warn!("Could not find transform for known bunny {bunny}");
return;
};
if !rng.random_bool((LONE_JUMPS_PER_SECOND * time.delta_secs_f64()).clamp(0.0, 1.0)) {
return;
}
@@ -241,7 +227,7 @@ fn jump_alone(
return;
};
start_jump(commands, bunny, bunny_transform, to);
start_jump(&mut commands, bunny, &mut bunny_transform, to);
}
fn start_jump(commands: &mut Commands, bunny: Entity, bunny_transform: &mut Transform, to: Vec3) {
@@ -314,7 +300,7 @@ fn cooldown(commands: &mut Commands, time: &Time, bunny: Entity, ready: Duration
fn look_for_partner(
mut commands: Commands,
bunnies: IdleBunniesQuery,
bunnies: Query<(Entity, &mut Transform), IdleBunnyFilter>,
time: Res<Time>,
locator: Res<Locator>,
mut rng: ResMut<Rng>,