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_state::<SimulationState>()
.add_message::<Lifecycle>()
.add_systems(Startup, setup)
.add_systems(
Update,
@ -42,6 +43,7 @@ fn main() {
Update,
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_coordinates)
.run();
@ -71,7 +73,6 @@ impl UiButton {
Self::ZoomIn => "zoomIn.png",
}
}
}
fn on_add_ui_button(
@ -118,13 +119,16 @@ fn setup(mut commands: Commands) {
));
commands
.spawn((Node {
.spawn((
Node {
top: Val::Px(0.0),
left: Val::Px(0.0),
height: Val::Px(40.0),
width: Val::Percent(100.0),
..default()
}, Transform::default()))
},
Transform::default(),
))
.with_children(|parent| {
parent.spawn(UiButton::Power);
parent.spawn(UiButton::Pause);
@ -192,18 +196,14 @@ impl Coordinates {
.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 {
(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>,
mut commands: Commands,
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.
///
/// TODO: There is a lot of optimizations to be had here
fn simulation_step(q: Query<(Entity, &Coordinates), With<Cell>>, mut commands: Commands) {
fn is_cell_alive(coordinates: &Coordinates, query: &Query<(Entity, &Coordinates), With<Cell>>) -> bool {
query
.iter()
.any(|(_e, c)| *c == *coordinates)
fn simulation_step(
q: Query<(Entity, &Coordinates), With<Cell>>,
mut writer: MessageWriter<Lifecycle>,
) {
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()
.filter(|neighbor| {
query.iter().any(
|(_e, coordinates)| { *neighbor == *coordinates },
)
query
.iter()
.any(|(_e, coordinates)| *neighbor == *coordinates)
})
.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
.iter()
.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 {
..2 | 4.. => {
if let Some(e) = cell_entity(c, q) {
debug!("killing {:?}", c);
commands.entity(e).despawn();
} else {
debug!("failed to kill {:?}", c);
if is_living {
writer.write(Lifecycle::Dead(cell_entity(c, q).unwrap()));
}
}
2 => debug!("continue"),
3 => {
if !is_living {
debug!("creating {:?}", c);
commands.spawn((c.clone(), Cell));
writer.write(Lifecycle::Alive(c.clone()));
} else {
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();
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