Replace update method with a block-based cellular automata
This commit is contained in:
parent
759479cc21
commit
7a9839d402
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
app::{Plugin, Startup},
|
app::{Plugin, Startup},
|
||||||
asset::Assets,
|
asset::Assets,
|
||||||
|
@ -60,19 +62,63 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
step: usize,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
cells: Vec<Element>,
|
cells: Vec<Element>,
|
||||||
dirty_rect: DirtyRect,
|
dirty_rect: DirtyRect,
|
||||||
|
rules: HashMap<u32, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
|
// Pre-computed air-sand rules
|
||||||
|
let rules = HashMap::from([
|
||||||
|
gen_rule(
|
||||||
|
(Element::Air, Element::Sand, Element::Air, Element::Air),
|
||||||
|
(Element::Air, Element::Air, Element::Air, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Air, Element::Sand, Element::Air, Element::Sand),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Air, Element::Sand, Element::Sand, Element::Air),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Air, Element::Air, Element::Air),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Air),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Air, Element::Air, Element::Sand),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Air, Element::Sand, Element::Air),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Sand, Element::Air, Element::Air),
|
||||||
|
(Element::Air, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Sand, Element::Air, Element::Sand),
|
||||||
|
(Element::Air, Element::Sand, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
gen_rule(
|
||||||
|
(Element::Sand, Element::Sand, Element::Sand, Element::Air),
|
||||||
|
(Element::Sand, Element::Air, Element::Sand, Element::Sand),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
step: 0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
cells: vec![Element::Air; width * height],
|
cells: vec![Element::Air; width * height],
|
||||||
dirty_rect: DirtyRect::default(),
|
dirty_rect: DirtyRect::default(),
|
||||||
|
rules,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +143,19 @@ impl Chunk {
|
||||||
self.dirty_rect.add_point(x1, y1);
|
self.dirty_rect.add_point(x1, y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cell(&self, x: usize, y: usize) -> Option<&Element> {
|
pub fn get_cell(&self, x: usize, y: usize) -> Option<Element> {
|
||||||
if x >= self.width || y >= self.height {
|
if x >= self.width || y >= self.height {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(&self.cells[x + y * self.width])
|
Some(self.cells[x + y * self.width])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rule_result(&self, input: u32) -> u32 {
|
||||||
|
match self.rules.get(&input) {
|
||||||
|
Some(&result) => result,
|
||||||
|
None => input,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,28 +183,42 @@ pub fn simulate_chunk_system(mut chunk: Query<&mut Chunk>) {
|
||||||
let mut chunk = chunk.unwrap();
|
let mut chunk = chunk.unwrap();
|
||||||
|
|
||||||
// Simulate sand
|
// Simulate sand
|
||||||
for y in 0..chunk.height {
|
let offset = if chunk.step == 0 {
|
||||||
for x in 0..chunk.width {
|
(0, 0)
|
||||||
match chunk.get_cell(x, y) {
|
} else if chunk.step == 1 {
|
||||||
Some(&Element::Air) => {}
|
(1, 1)
|
||||||
Some(&Element::Sand) => {
|
} else if chunk.step == 2 {
|
||||||
if y == 0 {
|
(0, 1)
|
||||||
continue;
|
} else {
|
||||||
}
|
(1, 0)
|
||||||
|
};
|
||||||
|
chunk.step = (chunk.step + 1) % 4;
|
||||||
|
|
||||||
let bottom = chunk.get_cell(x, y - 1);
|
for block_y in 0..(chunk.height / 2) {
|
||||||
let bottom_left = chunk.get_cell(x - 1, y - 1);
|
let y = block_y * 2 + offset.1;
|
||||||
let bottom_right = chunk.get_cell(x + 1, y - 1);
|
for block_x in 0..(chunk.width / 2) {
|
||||||
|
let x = block_x * 2 + offset.0;
|
||||||
|
|
||||||
if bottom == Some(&Element::Air) {
|
let start_state = to_rule_state((
|
||||||
chunk.swap_cells(x, y, x, y - 1);
|
chunk.get_cell(x, y + 1).unwrap_or(Element::None),
|
||||||
} else if x != 0 && bottom_left == Some(&Element::Air) {
|
chunk.get_cell(x + 1, y + 1).unwrap_or(Element::None),
|
||||||
chunk.swap_cells(x, y, x - 1, y - 1);
|
chunk.get_cell(x, y).unwrap_or(Element::None),
|
||||||
} else if x != chunk.width - 1 && bottom_right == Some(&Element::Air) {
|
chunk.get_cell(x + 1, y).unwrap_or(Element::None),
|
||||||
chunk.swap_cells(x, y, x + 1, y - 1);
|
));
|
||||||
}
|
let end_state = chunk.get_rule_result(start_state);
|
||||||
|
if start_state != end_state {
|
||||||
|
if (start_state & 0xFF000000) != (end_state & 0xFF000000) {
|
||||||
|
chunk.set_cell(x, y + 1, Element::from((start_state >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
if (start_state & 0x00FF0000) != (end_state & 0x00FF0000) {
|
||||||
|
chunk.set_cell(x + 1, y + 1, Element::from((start_state >> 16) & 0xFF));
|
||||||
|
}
|
||||||
|
if (start_state & 0x0000FF00) != (end_state & 0x0000FF00) {
|
||||||
|
chunk.set_cell(x, y, Element::from((start_state >> 8) & 0xFF));
|
||||||
|
}
|
||||||
|
if (start_state & 0x000000FF) != (end_state & 0x000000FF) {
|
||||||
|
chunk.set_cell(x + 1, y, Element::from(start_state & 0xFF));
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +247,7 @@ pub fn update_chunk_texture_system(
|
||||||
match element {
|
match element {
|
||||||
Element::Air => colour = (25, 24, 26),
|
Element::Air => colour = (25, 24, 26),
|
||||||
Element::Sand => colour = (255, 216, 102),
|
Element::Sand => colour = (255, 216, 102),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +265,28 @@ pub fn update_chunk_texture_system(
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Element {
|
pub enum Element {
|
||||||
|
None,
|
||||||
Air,
|
Air,
|
||||||
Sand,
|
Sand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Element {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
x if x == Element::Air as u32 => Element::Air,
|
||||||
|
x if x == Element::Sand as u32 => Element::Sand,
|
||||||
|
_ => Element::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_rule(
|
||||||
|
input: (Element, Element, Element, Element),
|
||||||
|
output: (Element, Element, Element, Element),
|
||||||
|
) -> (u32, u32) {
|
||||||
|
(to_rule_state(input), to_rule_state(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_rule_state(input: (Element, Element, Element, Element)) -> u32 {
|
||||||
|
((input.0 as u32) << 24) + ((input.1 as u32) << 16) + ((input.2 as u32) << 8) + input.3 as u32
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue