partial chunking

This commit is contained in:
StephenAdamson
2025-01-07 21:35:44 +00:00
parent 11ba882d54
commit ed88d5bdce
8 changed files with 305 additions and 120 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

+61 -21
View File
@@ -1,4 +1,7 @@
use crate::constants::*;
use crate::{
constants::{self, *},
tilemap,
};
use bevy::prelude::*;
use rand::prelude::*;
@@ -10,7 +13,7 @@ pub struct Ambulatory {
#[derive(Bundle)]
pub struct Citizen {
walker: Ambulatory,
walkness: Ambulatory,
sprite: Sprite,
transform: Transform,
}
@@ -18,9 +21,9 @@ pub struct Citizen {
impl Citizen {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
Citizen {
walker: Ambulatory { speed: TILE_SIZE },
walkness: Ambulatory { speed: TILE_SIZE },
sprite: Sprite {
image: asset_server.load("character.png"),
image: asset_server.load("dorf.png"),
..Default::default()
},
transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)),
@@ -30,29 +33,66 @@ impl Citizen {
pub fn citizen_movement(mut query: Query<(&Ambulatory, &mut Transform), With<Ambulatory>>) {
for (citizen, mut transform) in query.iter_mut() {
if random::<f32>() < 0.66666 {
// Skip processing if the random check fails
if rand::random::<f32>() >= 0.66666 {
continue;
}
let mut direction = Vec2::ZERO;
direction.y += rand::random::<f32>();
direction.y -= rand::random::<f32>();
direction.x -= rand::random::<f32>();
direction.x += rand::random::<f32>();
// Generate random directions
let direction_x = (rand::random::<f32>() - rand::random::<f32>()).round();
let direction_y = (rand::random::<f32>() - rand::random::<f32>()).round();
if direction.length() > 0.0 {
direction.x = direction.x.round();
direction.y = direction.y.round();
// Skip if there's no movement
if direction_x == 0.0 && direction_y == 0.0 {
continue;
}
let movement = direction * citizen.speed;
transform.translation.x += movement.x;
transform.translation.y += movement.y;
// Calculate movement only once
let speed = citizen.speed;
transform.translation.x += direction_x * speed;
transform.translation.y += direction_y * speed;
if direction.x > 0.0 {
transform.scale.x = PIXEL_RATIO;
} else if direction.x < 0.0 {
transform.scale.x = -PIXEL_RATIO;
}
// Adjust scale for x direction
if direction_x != 0.0 {
transform.scale.x = PIXEL_RATIO * direction_x.signum();
}
}
}
pub fn spawn_citizens(mut commands: Commands, asset_server: Res<AssetServer>) {
let mut rng = rand::thread_rng();
// Spawn a handful of citizens
for _ in 0..10 {
let x = rng.gen_range(-10.0..10.0);
let y = rng.gen_range(-10.0..10.0);
let position = Vec3::new(x, y, 0.0) * PIXEL_RATIO;
commands.spawn(Citizen::new(&asset_server, position));
}
}
pub fn check_citizen_positions_for_chunks(
query: Query<&Transform, With<Ambulatory>>,
chunk_map: Res<tilemap::ChunkMap>,
mut event_writer: EventWriter<tilemap::LoadChunkEvent>,
) {
for transform in query.iter() {
let position = transform.translation;
// Convert citizen position to chunk coordinates
let chunk_x =
(position.x / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32;
let chunk_y =
(position.y / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32;
let chunk_pos = IVec2::new(chunk_x, chunk_y);
// Load chunk if not already loaded
if !chunk_map.loaded_chunks.contains_key(&chunk_pos) {
event_writer.send(tilemap::LoadChunkEvent {
chunk_position: chunk_pos,
});
}
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
pub const PIXEL_RATIO: f32 = 3.;
pub const PIXEL_RATIO: f32 = 1.;
pub const TILE_SIZE: f32 = 16.0 * PIXEL_RATIO;
+51 -1
View File
@@ -1,4 +1,7 @@
use std::process::exit;
use bevy::prelude::*;
use camera::PanningCamera;
mod camera;
mod citizen;
@@ -7,6 +10,7 @@ mod cursor;
mod game;
mod item;
mod tile;
mod tilemap;
mod tiles;
fn main() {
@@ -24,12 +28,14 @@ fn main() {
.set(ImagePlugin::default_nearest()),
)
.insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0)))
.add_plugins(tilemap::TilemapPlugin)
.add_systems(
Startup,
(
game::setup_level,
setup_initial_chunks,
camera::spawn_panning_camera,
cursor::setup_cursor,
citizen::spawn_citizens, // New citizen spawning system
),
)
.add_systems(
@@ -47,6 +53,8 @@ fn main() {
camera::camera_movement,
citizen::citizen_movement,
cursor::move_cursor,
check_camera_position_for_chunks,
citizen::check_citizen_positions_for_chunks, // New citizen-based chunk loading system
),
)
.init_resource::<tile::CameraMoved>()
@@ -60,3 +68,45 @@ fn main() {
)
.run();
}
fn setup_initial_chunks(mut event_writer: EventWriter<tilemap::LoadChunkEvent>) {
// Spawn a 3x3 grid of chunks around the origin
for x in -1..=1 {
for y in -1..=1 {
println!("setup_initial_chunks: x = {}, y = {}", x, y);
event_writer.send(tilemap::LoadChunkEvent {
chunk_position: IVec2::new(x, y),
});
}
}
}
// New system to check camera position and load nearby chunks
fn check_camera_position_for_chunks(
camera_query: Query<&Transform, With<Camera>>,
chunk_map: Res<tilemap::ChunkMap>,
mut event_writer: EventWriter<tilemap::LoadChunkEvent>,
) {
let camera_transform = camera_query.single();
let camera_pos = camera_transform.translation;
// Convert camera position to chunk coordinates
let chunk_x =
(camera_pos.x / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32;
let chunk_y =
(camera_pos.y / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32;
// Check a 3x3 area around the camera's current chunk
for x in (chunk_x - 1)..=(chunk_x + 1) {
for y in (chunk_y - 1)..=(chunk_y + 1) {
let chunk_pos = IVec2::new(x, y);
// Only spawn new chunks if they haven't been loaded yet
if !chunk_map.loaded_chunks.contains_key(&chunk_pos) {
event_writer.send(tilemap::LoadChunkEvent {
chunk_position: chunk_pos,
});
}
}
}
}
-97
View File
@@ -1,11 +1,8 @@
use crate::constants::TILE_SIZE;
use crate::game;
use crate::item::Item;
use bevy::ecs::system::ParamSet;
use bevy::math::ivec3;
use bevy::prelude::*;
use std::collections::HashMap;
use std::process::exit;
#[derive(Component, Clone)]
#[require(Sprite)]
@@ -46,9 +43,6 @@ impl Default for FixtureTile {
}
}
#[derive(Component, Clone)]
pub struct ConnectedTexture {}
#[derive(Resource, Default)]
pub struct CameraMoved(pub bool);
@@ -211,24 +205,6 @@ pub fn tile_sprite_generate_occlusion_map(
let pos = transform.translation.as_ivec3() - IVec3::new(0, 0, 1);
fixture.visible_range = calculate_visibility(pos, &tilemap);
});
use std::time::Instant;
let total: Instant = Instant::now();
for i in 0..1000000 {
let now: Instant = Instant::now();
for x in -1..=1 {
for y in -1..=1 {
for z in 0..=1 {
tilemap.floors.get(&IVec3::new(x, y, z));
}
}
}
let elapsed = now.elapsed();
}
let elapsed = total.elapsed();
print!("{:.4?}, ", elapsed / 1000000);
exit(0);
}
pub fn update_tile_visibility(
@@ -270,79 +246,6 @@ pub fn update_tile_visibility(
});
}
fn calculate_connected_texture(floor_tiles: &HashMap<(i32, i32, i32), (bool, u32)>) -> Vec<String> {
let mut textures = vec![];
for ((x, y, z), (is_floor, tile_id)) in floor_tiles.iter() {
let mut is_wall = false;
for x_offset in -1..=1 {
for y_offset in -1..=1 {
for z_offset in 0..=1 {
if x_offset == 0 && y_offset == 0 && z_offset == 0 {
continue;
}
let neighbor_pos = (x + x_offset, y + y_offset, z + z_offset);
if let Some((_, neighbor_id)) = floor_tiles.get(&neighbor_pos) {
if *neighbor_id > 0 {
// assuming wall IDs are greater than 0
is_wall = true;
break;
}
}
}
}
}
match (is_floor, is_wall) {
(true, false) => textures.push("floor".to_string()), // or some other floor texture
(false, true) => textures.push("wall".to_string()), // or some other wall texture
_ => panic!("Unexpected tile state"),
}
}
textures
}
pub fn tile_item_sprite_update(
time: Res<Time>,
mut query_tile: Query<(Entity, &Children, &mut TileState)>,
mut query_item: Query<(&mut Visibility, &Item)>,
) {
for (_, children, mut state) in query_tile.iter_mut() {
state.timer.tick(time.delta());
if !state.timer.finished() {
continue;
}
let mut visible_index: Option<usize> = None;
for (i, &child) in children.iter().enumerate() {
if let Ok((mut visibility, _)) = query_item.get_mut(child) {
if matches!(*visibility, Visibility::Visible) {
visible_index = Some(i);
*visibility = Visibility::Hidden;
break;
}
}
}
let next_index = if let Some(current_index) = visible_index {
(current_index + 1) % children.len()
} else {
0
};
if let Some(&next_child) = children.get(next_index) {
if let Ok((mut visibility, _)) = query_item.get_mut(next_child) {
*visibility = Visibility::Visible;
}
}
}
}
#[derive(Component)]
pub struct TileState {
pub timer: Timer,
+192
View File
@@ -0,0 +1,192 @@
use crate::constants::TILE_SIZE;
use crate::tiles::{FixtureTilePrefab, FloorTilePrefab};
use bevy::prelude::*;
use noise::{NoiseFn, Perlin};
use std::collections::HashMap;
use std::process::exit;
pub const CHUNK_SIZE: i32 = 8;
pub const CHUNK_HEIGHT: i32 = 200;
#[derive(Resource)]
pub struct ChunkMap {
pub loaded_chunks: HashMap<IVec2, bool>,
}
impl Default for ChunkMap {
fn default() -> Self {
Self {
loaded_chunks: HashMap::new(),
}
}
}
#[derive(Event)]
pub struct LoadChunkEvent {
pub chunk_position: IVec2,
}
fn setup_chunk_system(mut commands: Commands) {
commands.insert_resource(ChunkMap::default());
}
fn handle_chunk_loading(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut events: EventReader<LoadChunkEvent>,
mut chunk_map: ResMut<ChunkMap>,
) {
let noise = Perlin::new(0);
for event in events.read() {
let chunk_pos = event.chunk_position;
// Skip if chunk is already loaded
if chunk_map.loaded_chunks.contains_key(&chunk_pos) {
continue;
}
// Mark chunk as loaded
chunk_map.loaded_chunks.insert(chunk_pos, true);
// Calculate world space coordinates for chunk
let start_x = chunk_pos.x * CHUNK_SIZE;
let start_y = chunk_pos.y * CHUNK_SIZE;
let mut floor_positions = Vec::new();
// Generate tiles for this chunk
for local_y in 0..CHUNK_SIZE {
for local_x in 0..CHUNK_SIZE {
let world_x = start_x + local_x;
let world_y = start_y + local_y;
let noise_value_a =
noise.get([world_x as f64 * 0.01, world_y as f64 * 0.01]) * 1.25;
let noise_value_b =
noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05]) * 0.25;
let combined_noise_value = noise_value_a + noise_value_b;
let noise_position = Vec3::new(
(world_x as f32 * TILE_SIZE).round(),
(world_y as f32 * TILE_SIZE).round(),
(combined_noise_value * 4.).round() as f32 * TILE_SIZE,
);
generate_vertical_slice(
&mut commands,
&asset_server,
world_x,
world_y,
&noise,
noise_position,
&mut floor_positions,
);
}
}
// Generate fixtures after floor tiles
generate_fixtures_for_chunk(&mut commands, &asset_server, &floor_positions);
}
}
fn generate_vertical_slice(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
x: i32,
y: i32,
noise: &Perlin,
noise_position: Vec3,
floor_positions: &mut Vec<(Vec3, &'static str)>,
) {
for z in -150..50 {
let position = Vec3::new(
(x as f32 * TILE_SIZE).round(),
(y as f32 * TILE_SIZE).round(),
(z as f32 * TILE_SIZE).round(),
);
let floor_type = if noise_position.z > position.z {
if z < -15 {
let noise_value = noise.get([x as f64 * 0.05, y as f64 * 0.05, z as f64 * 0.05]);
if noise_value < -0.5 {
spawn_tile(commands, FloorTilePrefab::air(position, asset_server));
Some("air")
} else if noise_value < 0.8 {
spawn_tile(commands, FloorTilePrefab::rock(position, asset_server));
Some("rock")
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
}
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
}
} else if noise_position.z < position.z {
spawn_tile(commands, FloorTilePrefab::air(position, asset_server));
Some("air")
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
};
if let Some(floor_type) = floor_type {
floor_positions.push((position, floor_type));
}
}
}
fn spawn_tile(commands: &mut Commands, bundle: impl Bundle) -> Entity {
commands.spawn(bundle).id()
}
fn generate_fixtures_for_chunk(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
floor_positions: &[(Vec3, &str)],
) {
let floor_map: HashMap<(i32, i32, i32), &str> = floor_positions
.iter()
.map(|(pos, floor_type)| ((pos.x as i32, pos.y as i32, pos.z as i32), *floor_type))
.collect();
for (position, _) in floor_positions {
let x = position.x as i32;
let y = position.y as i32;
let z = position.z as i32;
if let Some(&above_floor_type) = floor_map.get(&(x, y, z + TILE_SIZE as i32)) {
if above_floor_type == "air" {
continue;
}
let fixture_position = Vec3::new(position.x, position.y, position.z + 1.);
match above_floor_type {
"dirt" => {
commands.spawn(FixtureTilePrefab::dirt_wall(fixture_position, asset_server));
}
"rock" => {
commands.spawn(FixtureTilePrefab::rock_wall(fixture_position, asset_server));
}
"bedrock" => {
commands.spawn(FixtureTilePrefab::bedrock_wall(
fixture_position,
asset_server,
));
}
_ => {}
}
}
}
}
pub struct TilemapPlugin;
impl Plugin for TilemapPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ChunkMap>()
.add_event::<LoadChunkEvent>()
.add_systems(Startup, setup_chunk_system)
.add_systems(Update, handle_chunk_loading);
}
}