Compare commits
2 Commits
a181cbf7ad
...
00f75f2de6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
00f75f2de6 | ||
![]() |
22f59b059e |
106
src/map.rs
106
src/map.rs
@ -26,21 +26,27 @@ use picking::*;
|
|||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
impl bevy::prelude::Plugin for Plugin {
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup)
|
app.add_plugins(cells::Plugin)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
|
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
|
||||||
.add_systems(Update, (update_map_mesh, cells_regeneration, expand))
|
.add_systems(Update, update_map_mesh)
|
||||||
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
|
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
|
||||||
.insert_resource(Seed(thread_rng().gen()))
|
.insert_resource(Seed(thread_rng().gen()));
|
||||||
.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const HEIGHT: f32 = 16.;
|
/// Determined empirically
|
||||||
pub const WIDTH: f32 = 16.;
|
pub const AVERAGE_NEIGHBORS_NUMBER: f64 = 6.;
|
||||||
|
|
||||||
|
pub const HEIGHT: f64 = 16.;
|
||||||
|
pub const WIDTH: f64 = 16.;
|
||||||
pub const REAL_HEIGHT: f32 = 8000.;
|
pub const REAL_HEIGHT: f32 = 8000.;
|
||||||
pub const REAL_WIDTH: f32 = 8000.;
|
pub const REAL_WIDTH: f32 = 8000.;
|
||||||
pub const CELL_AREA: f32 = REAL_HEIGHT * REAL_WIDTH / SIZE as f32;
|
pub const CELL_AREA: f32 = REAL_HEIGHT * REAL_WIDTH / CELLS as f32;
|
||||||
pub const SIZE: usize = 100_000;
|
pub const CELLS_TARGET_NUMBER: usize = 100_000;
|
||||||
|
pub const CHUNKS_RESOLUTION: usize = 2;
|
||||||
|
pub const CELLS_PER_CHUNK: usize = CELLS_TARGET_NUMBER / CHUNKS_RESOLUTION.pow(2);
|
||||||
|
pub const CELLS: usize = CELLS_PER_CHUNK * CHUNKS_RESOLUTION.pow(2);
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct Seed(u32);
|
struct Seed(u32);
|
||||||
@ -68,24 +74,41 @@ fn setup(
|
|||||||
seed: Res<Seed>,
|
seed: Res<Seed>,
|
||||||
) {
|
) {
|
||||||
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 as u64);
|
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 as u64);
|
||||||
let mut sites = Vec::with_capacity(SIZE);
|
let mut sites = Vec::with_capacity(CELLS);
|
||||||
for _ in 0..SIZE {
|
for chunk_x in 0..CHUNKS_RESOLUTION {
|
||||||
sites.push(Point {
|
let min_x = WIDTH / 2. * ((2. * (chunk_x as f64) / (CHUNKS_RESOLUTION as f64)) - 1.);
|
||||||
x: rng.gen_range(-WIDTH / 2.0..WIDTH / 2.0) as f64,
|
let max_x = min_x + (WIDTH as f64 / CHUNKS_RESOLUTION as f64);
|
||||||
y: rng.gen_range(-HEIGHT / 2.0..HEIGHT / 2.0) as f64,
|
for chunk_y in 0..CHUNKS_RESOLUTION {
|
||||||
})
|
let min_y = HEIGHT / 2. * ((2. * (chunk_y as f64) / (CHUNKS_RESOLUTION as f64)) - 1.);
|
||||||
|
let max_y = min_y + (HEIGHT as f64 / CHUNKS_RESOLUTION as f64);
|
||||||
|
println!(
|
||||||
|
"[{},{}] x:[{}, {}], y:[{}, {}]",
|
||||||
|
chunk_x, chunk_y, min_x, max_x, min_y, max_y
|
||||||
|
);
|
||||||
|
for _ in 0..CELLS_PER_CHUNK {
|
||||||
|
sites.push(Point {
|
||||||
|
x: rng.gen_range(min_x..max_x),
|
||||||
|
y: rng.gen_range(min_y..max_y),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// dbg!(sites.len());
|
||||||
|
|
||||||
let voronoi = VoronoiBuilder::default()
|
let voronoi = VoronoiBuilder::default()
|
||||||
.set_sites(sites)
|
.set_sites(sites)
|
||||||
.set_bounding_box(BoundingBox::new_centered(WIDTH as f64, HEIGHT as f64))
|
.set_bounding_box(BoundingBox::new_centered(WIDTH, HEIGHT))
|
||||||
.set_lloyd_relaxation_iterations(3)
|
.set_lloyd_relaxation_iterations(3)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut cells = Vec::with_capacity(SIZE);
|
|
||||||
|
let mut cells = Vec::with_capacity(CELLS);
|
||||||
let z_noise = Fbm::<Perlin>::new(seed.0);
|
let z_noise = Fbm::<Perlin>::new(seed.0);
|
||||||
let moisture_noise = Fbm::<Perlin>::new(seed.0 + 1).set_frequency(2.);
|
let moisture_noise = Fbm::<Perlin>::new(seed.0 + 1).set_frequency(2.);
|
||||||
for i in 0..SIZE {
|
let mut res = 0;
|
||||||
|
for i in 0..CELLS {
|
||||||
let c = voronoi.cell(i);
|
let c = voronoi.cell(i);
|
||||||
|
res += c.iter_neighbors().count();
|
||||||
let site = c.site_position();
|
let site = c.site_position();
|
||||||
let z = get_altitude(&z_noise, &[site.x as f32, site.y as f32]);
|
let z = get_altitude(&z_noise, &[site.x as f32, site.y as f32]);
|
||||||
let _m = (
|
let _m = (
|
||||||
@ -108,6 +131,7 @@ fn setup(
|
|||||||
vertices: vec![],
|
vertices: vec![],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
dbg!(res as f64 / CELLS as f64);
|
||||||
|
|
||||||
let mut poss = Vec::new();
|
let mut poss = Vec::new();
|
||||||
let mut indices = Vec::new();
|
let mut indices = Vec::new();
|
||||||
@ -181,27 +205,27 @@ fn setup(
|
|||||||
MapMeshColors(colors),
|
MapMeshColors(colors),
|
||||||
MeshNeedsUpdate(true),
|
MeshNeedsUpdate(true),
|
||||||
MapMarker,
|
MapMarker,
|
||||||
))
|
));
|
||||||
.with_children(|parent| {
|
|
||||||
for cell in cells {
|
for cell in cells {
|
||||||
let kind = cell.kind;
|
let kind = cell.kind;
|
||||||
let mut cmd = parent.spawn(cell);
|
let mut cmd = cmds.spawn(cell);
|
||||||
match kind {
|
match kind {
|
||||||
CellKind::Grass | CellKind::Forest => {
|
CellKind::Grass | CellKind::Forest => {
|
||||||
cmd.insert((
|
cmd.insert((
|
||||||
Wealth(0),
|
Wealth(0),
|
||||||
Regeneration {
|
Regeneration {
|
||||||
last_update: Duration::ZERO,
|
last_update: Duration::ZERO,
|
||||||
full_growth_duration: kind.regen_full_growth_duration(),
|
full_growth_duration: kind.regen_full_growth_duration(),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
cmd.observe(self::cells::on_click);
|
_ => {}
|
||||||
cells_entities.push(cmd.id());
|
|
||||||
}
|
}
|
||||||
});
|
cmd.observe(self::cells::on_click);
|
||||||
|
cells_entities.push(cmd.id());
|
||||||
|
}
|
||||||
|
|
||||||
cmds.insert_resource(Voronoi(voronoi));
|
cmds.insert_resource(Voronoi(voronoi));
|
||||||
cmds.insert_resource(CellsEntities(cells_entities));
|
cmds.insert_resource(CellsEntities(cells_entities));
|
||||||
}
|
}
|
||||||
@ -219,10 +243,12 @@ fn update_map_mesh(
|
|||||||
for (cell, wealth) in cells.iter() {
|
for (cell, wealth) in cells.iter() {
|
||||||
// let col: [f32; 4] = [rng.gen(), rng.gen(), rng.gen(), 1.];
|
// let col: [f32; 4] = [rng.gen(), rng.gen(), rng.gen(), 1.];
|
||||||
let col = cell.color(wealth.map(|w| w.0).unwrap_or_default());
|
let col = cell.color(wealth.map(|w| w.0).unwrap_or_default());
|
||||||
for id in cell.vertices.iter() {
|
modified = modified || cols.0[cell.voronoi_id] != col;
|
||||||
modified = modified || cols.0[*id] != col;
|
cols.0[cell.voronoi_id] = col;
|
||||||
cols.0[*id] = col.clone();
|
// for id in cell.vertices.iter() {
|
||||||
}
|
// modified = modified || cols.0[*id] != col;
|
||||||
|
// cols.0[*id] = col.clone();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if modified {
|
if modified {
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
|
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
|
||||||
|
@ -1,13 +1,31 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{prelude::*, time::common_conditions::on_timer};
|
||||||
use rand::{seq::IteratorRandom, thread_rng, Rng};
|
use rand::{seq::IteratorRandom, thread_rng, Rng};
|
||||||
|
|
||||||
use crate::{time::GameTime, ui::CurrentAction};
|
use crate::{time::GameTime, ui::CurrentAction};
|
||||||
|
|
||||||
use super::{animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi};
|
use super::{
|
||||||
|
animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi,
|
||||||
|
AVERAGE_NEIGHBORS_NUMBER, CELL_AREA,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod material;
|
pub mod material;
|
||||||
|
use material::CellMaterial;
|
||||||
|
|
||||||
|
pub struct Plugin;
|
||||||
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
(
|
||||||
|
cells_regeneration.run_if(on_timer(Duration::from_secs(1))),
|
||||||
|
expand.run_if(on_timer(Duration::from_secs(1))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum CellKind {
|
pub enum CellKind {
|
||||||
@ -79,6 +97,7 @@ pub struct Regeneration {
|
|||||||
pub full_growth_duration: Duration,
|
pub full_growth_duration: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STEP: u8 = u8::MAX / 4;
|
||||||
pub fn cells_regeneration(
|
pub fn cells_regeneration(
|
||||||
mut cells: Query<(&mut Regeneration, &mut Wealth)>,
|
mut cells: Query<(&mut Regeneration, &mut Wealth)>,
|
||||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||||
@ -86,10 +105,15 @@ pub fn cells_regeneration(
|
|||||||
) {
|
) {
|
||||||
let mut map_needs_update = map_needs_update.single_mut();
|
let mut map_needs_update = map_needs_update.single_mut();
|
||||||
for (mut regen, mut wealth) in cells.iter_mut() {
|
for (mut regen, mut wealth) in cells.iter_mut() {
|
||||||
if gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
while gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
||||||
regen.last_update = gt.current;
|
regen.last_update = gt
|
||||||
|
.current
|
||||||
|
.min(regen.last_update + (regen.full_growth_duration / u8::MAX as u32));
|
||||||
wealth.0 = wealth.0.saturating_add(1);
|
wealth.0 = wealth.0.saturating_add(1);
|
||||||
map_needs_update.0 = true;
|
// Not update map each time for optimization
|
||||||
|
if wealth.0 % STEP == 0 {
|
||||||
|
map_needs_update.0 = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,28 +137,43 @@ pub fn expand(
|
|||||||
wealth.unwrap().0 as f64 / 255.
|
wealth.unwrap().0 as f64 / 255.
|
||||||
};
|
};
|
||||||
if random.gen_bool(
|
if random.gen_bool(
|
||||||
(t.elapsed_secs_f64() * (gt.speed as f64) * wealth / (60. * 60. * 24. * 30.))
|
dbg!(
|
||||||
.min(1.),
|
t.elapsed_secs_f64() * (gt.speed as f64) * wealth * 1e-7
|
||||||
|
/ (CELL_AREA as f64).sqrt()
|
||||||
|
)
|
||||||
|
.min(1.),
|
||||||
) {
|
) {
|
||||||
// Let say that grass takes 1 months to expand
|
// "Grass can expand into adjacent dirt at a rate of 2.5 to 7.5 cm (1 to 3 inches) per month.", DeepSeek
|
||||||
|
// With some (way too complicated and probably false) computation, we get for a good grass an extension rate of (1e-7 / sqrt(cell_area)) cells per seconds
|
||||||
let target = voronoi
|
let target = voronoi
|
||||||
.0
|
.0
|
||||||
.cell(cell.voronoi_id)
|
.cell(cell.voronoi_id)
|
||||||
.iter_neighbors()
|
.iter_neighbors()
|
||||||
.choose(&mut random)
|
.choose(&mut random)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
changes.push((target, CellKind::Grass));
|
changes.extend(
|
||||||
|
voronoi
|
||||||
|
.0
|
||||||
|
.cell(cell.voronoi_id)
|
||||||
|
.iter_neighbors()
|
||||||
|
.map(|t| (t, CellKind::Grass)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cell.kind == CellKind::Forest {
|
if cell.kind == CellKind::Forest {
|
||||||
if random.gen_bool(
|
if random.gen_bool(
|
||||||
(t.elapsed_secs_f64()
|
dbg!(
|
||||||
* (gt.speed as f64)
|
t.elapsed_secs_f64()
|
||||||
* (wealth.unwrap().0 as f64 / 255.).sqrt()
|
* (gt.speed as f64)
|
||||||
/ (60. * 60. * 24. * 365. * 5.))
|
* (wealth.unwrap().0 as f64 / 255.).sqrt()
|
||||||
.min(1.),
|
* 5.6e-7
|
||||||
|
/ (CELL_AREA as f64).sqrt()
|
||||||
|
* AVERAGE_NEIGHBORS_NUMBER
|
||||||
|
)
|
||||||
|
.min(1.),
|
||||||
) {
|
) {
|
||||||
// Let say that forest takes 5 years to expand
|
// "Forests can expand at a rate of 1 to 10 meters per year", DeepSeek
|
||||||
|
// Same computations as above : (5.6e-7 / sqrt(cell_area)) cells per seconds
|
||||||
let target = voronoi
|
let target = voronoi
|
||||||
.0
|
.0
|
||||||
.cell(cell.voronoi_id)
|
.cell(cell.voronoi_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user