prototypes/life: refactor: message based cell lifecycle

main
Elijah Voigt 1 month ago
parent a587c17978
commit 59996c9c86

@ -1 +1,4 @@
pub use bevy::{color::palettes::basic::*, input::common_conditions::*, prelude::*, platform::collections::HashSet}; pub use bevy::{
color::palettes::basic::*, input::common_conditions::*, platform::collections::HashSet,
prelude::*,
};

@ -24,6 +24,7 @@ fn main() {
})) }))
.init_resource::<Coordinates>() .init_resource::<Coordinates>()
.init_state::<SimulationState>() .init_state::<SimulationState>()
.add_message::<Lifecycle>()
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems( .add_systems(
Update, Update,
@ -42,6 +43,7 @@ fn main() {
Update, Update,
simulation_step.run_if(input_just_pressed(KeyCode::Space)), simulation_step.run_if(input_just_pressed(KeyCode::Space)),
) )
.add_systems(Update, cell_lifecycle.run_if(on_message::<Lifecycle>))
.add_observer(on_add_ui_button) .add_observer(on_add_ui_button)
.add_observer(on_add_coordinates) .add_observer(on_add_coordinates)
.run(); .run();
@ -71,7 +73,6 @@ impl UiButton {
Self::ZoomIn => "zoomIn.png", Self::ZoomIn => "zoomIn.png",
} }
} }
} }
fn on_add_ui_button( fn on_add_ui_button(
@ -118,13 +119,16 @@ fn setup(mut commands: Commands) {
)); ));
commands commands
.spawn((Node { .spawn((
top: Val::Px(0.0), Node {
left: Val::Px(0.0), top: Val::Px(0.0),
height: Val::Px(40.0), left: Val::Px(0.0),
width: Val::Percent(100.0), height: Val::Px(40.0),
..default() width: Val::Percent(100.0),
}, Transform::default())) ..default()
},
Transform::default(),
))
.with_children(|parent| { .with_children(|parent| {
parent.spawn(UiButton::Power); parent.spawn(UiButton::Power);
parent.spawn(UiButton::Pause); parent.spawn(UiButton::Pause);
@ -192,18 +196,14 @@ impl Coordinates {
.into_iter() .into_iter()
} }
fn is_neighbor_of(&self, other: &Coordinates) -> bool {
let (a1, b1) = (self.x, self.y);
let (a2, b2) = (other.x, other.y);
(a1 == a2 + 1 || a1 == a2 - 1) && (b1 == b2 + 1 || b1 == b2 - 1)
}
fn as_translation(&self) -> Vec3 { fn as_translation(&self) -> Vec3 {
(Vec2::new(self.x as f32, self.y as f32) * SCALE).extend(0.0) (Vec2::new(self.x as f32, self.y as f32) * SCALE).extend(0.0)
} }
} }
fn on_add_coordinates(trigger: On<Add, Coordinates>, coordinates: Query<&Coordinates>, fn on_add_coordinates(
trigger: On<Add, Coordinates>,
coordinates: Query<&Coordinates>,
cells: Query<&Cell>, cells: Query<&Cell>,
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
@ -307,24 +307,34 @@ enum SimulationState {
/// 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. /// 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
/// ///
/// TODO: There is a lot of optimizations to be had here /// TODO: There is a lot of optimizations to be had here
fn simulation_step(q: Query<(Entity, &Coordinates), With<Cell>>, mut commands: Commands) { fn simulation_step(
fn is_cell_alive(coordinates: &Coordinates, query: &Query<(Entity, &Coordinates), With<Cell>>) -> bool { q: Query<(Entity, &Coordinates), With<Cell>>,
query mut writer: MessageWriter<Lifecycle>,
.iter() ) {
.any(|(_e, c)| *c == *coordinates) fn is_cell_alive(
coordinates: &Coordinates,
query: &Query<(Entity, &Coordinates), With<Cell>>,
) -> bool {
query.iter().any(|(_e, c)| *c == *coordinates)
} }
fn living_neighbors(c: &Coordinates, query: &Query<(Entity, &Coordinates), With<Cell>>) -> usize { fn living_neighbors(
c: &Coordinates,
query: &Query<(Entity, &Coordinates), With<Cell>>,
) -> usize {
c.neighbors() c.neighbors()
.filter(|neighbor| { .filter(|neighbor| {
query.iter().any( query
|(_e, coordinates)| { *neighbor == *coordinates }, .iter()
) .any(|(_e, coordinates)| *neighbor == *coordinates)
}) })
.count() .count()
} }
fn cell_entity(coordinates: &Coordinates, query: Query<(Entity, &Coordinates), With<Cell>>) -> Option<Entity> { fn cell_entity(
coordinates: &Coordinates,
query: Query<(Entity, &Coordinates), With<Cell>>,
) -> Option<Entity> {
query query
.iter() .iter()
.find_map(|(e, c)| (*c == *coordinates).then_some(e)) .find_map(|(e, c)| (*c == *coordinates).then_some(e))
@ -353,18 +363,14 @@ fn simulation_step(q: Query<(Entity, &Coordinates), With<Cell>>, mut commands: C
match num_living_neighbors { match num_living_neighbors {
..2 | 4.. => { ..2 | 4.. => {
if let Some(e) = cell_entity(c, q) { if is_living {
debug!("killing {:?}", c); writer.write(Lifecycle::Dead(cell_entity(c, q).unwrap()));
commands.entity(e).despawn();
} else {
debug!("failed to kill {:?}", c);
} }
} }
2 => debug!("continue"), 2 => debug!("continue"),
3 => { 3 => {
if !is_living { if !is_living {
debug!("creating {:?}", c); writer.write(Lifecycle::Alive(c.clone()));
commands.spawn((c.clone(), Cell));
} else { } else {
debug!("continue") debug!("continue")
} }
@ -382,3 +388,27 @@ fn responsive_button_out(trigger: On<Pointer<Out>>, mut bg: Query<&mut Backgroun
let mut background_color = bg.get_mut(trigger.entity).unwrap(); let mut background_color = bg.get_mut(trigger.entity).unwrap();
background_color.0 = GRAY.into(); background_color.0 = GRAY.into();
} }
/// Bring a cell to life, or kill it
#[derive(Message)]
enum Lifecycle {
Alive(Coordinates),
Dead(Entity),
}
fn cell_lifecycle(
mut reader: MessageReader<Lifecycle>,
mut commands: Commands,
) {
reader.read().for_each(|msg| match msg {
Lifecycle::Alive(c) => {
debug!("creating {:?}", c);
// TODO: Safeguard: ensure no other cell there?
commands.spawn((c.clone(), Cell));
}
Lifecycle::Dead(e) => {
// TODO: Maybe include the entity instead of the coordinates for Dead
commands.entity(*e).despawn();
}
})
}

Loading…
Cancel
Save