Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-atom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ jobs:
- **Type:** Atomic workflow

---
*This PR was automatically created by the atomic branch workflow.*`;
*This PR was automatically created by the atomic branch workflow.* - [KBVE Studio](https://kbve.com/)`;

try {
const pr = await github.rest.pulls.create({
Expand Down
9 changes: 9 additions & 0 deletions apps/kbve/isometric/src-tauri/src/game/terrain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ use super::player::Player;
// ---------------------------------------------------------------------------

pub const CHUNK_SIZE: i32 = 16;

#[cfg(not(target_arch = "wasm32"))]
pub const LOAD_RADIUS: i32 = 3;

#[cfg(target_arch = "wasm32")]
pub const LOAD_RADIUS: i32 = 2;
pub const MAX_HEIGHT: f32 = 6.0;
pub const NOISE_SCALE: f32 = 6.0;
pub const TERRAIN_SEED: u32 = 42;
Expand Down Expand Up @@ -187,6 +192,10 @@ impl TerrainMap {
pub fn update_around_player(&mut self, player_x: f32, player_z: f32) {
let (pcx, pcz) = Self::tile_to_chunk(player_x.round() as i32, player_z.round() as i32);

// Rebuild spawn queue fresh each frame (avoids duplicates when
// rate-limited spawning leaves chunks un-spawned across frames).
self.chunks_to_spawn.clear();

// Determine which chunks should be loaded
let mut desired: Vec<(i32, i32)> = Vec::new();
for dx in -LOAD_RADIUS..=LOAD_RADIUS {
Expand Down
34 changes: 31 additions & 3 deletions apps/kbve/isometric/src-tauri/src/game/tilemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use bevy_rapier3d::prelude::*;

use super::grass::GrassTuft;
use super::player::Player;
use super::terrain::{CHUNK_SIZE, TerrainMap, hash2d};

pub const TILE_SIZE: f32 = 1.0;
Expand Down Expand Up @@ -361,21 +362,45 @@ fn process_chunk_spawns_and_despawns(
mut meshes: ResMut<Assets<Mesh>>,
mut terrain: ResMut<TerrainMap>,
tile_materials: Option<Res<TileMaterials>>,
player_query: Query<&Transform, With<Player>>,
) {
let Some(tile_materials) = tile_materials else {
return;
};

// Despawn chunks
// Despawn chunks (all at once — despawning is cheap)
let despawns: Vec<(i32, i32, Vec<Entity>)> = terrain.chunks_to_despawn.drain(..).collect();
for (_cx, _cz, entities) in despawns {
for entity in entities {
commands.entity(entity).despawn();
}
}

// Spawn chunks
let spawns: Vec<(i32, i32)> = terrain.chunks_to_spawn.drain(..).collect();
// Find the player's chunk so we can prioritize nearby chunks for colliders
let player_chunk = player_query.single().ok().map(|tf| {
TerrainMap::tile_to_chunk(tf.translation.x.round() as i32, tf.translation.z.round() as i32)
});

// Spawn chunks — always spawn player's chunk + neighbors immediately,
// rate-limit distant chunks to avoid frame spikes.
#[cfg(target_arch = "wasm32")]
const MAX_DISTANT_SPAWNS: usize = 1;
#[cfg(not(target_arch = "wasm32"))]
const MAX_DISTANT_SPAWNS: usize = 2;

let mut near: Vec<(i32, i32)> = Vec::new();
let mut far: Vec<(i32, i32)> = Vec::new();
for (cx, cz) in terrain.chunks_to_spawn.drain(..) {
let is_near = player_chunk
.map(|(pcx, pcz)| (cx - pcx).abs() <= 1 && (cz - pcz).abs() <= 1)
.unwrap_or(true);
if is_near { near.push((cx, cz)); } else { far.push((cx, cz)); }
}
// Near chunks: always spawn (player needs colliders)
// Far chunks: rate-limit (terrain system re-queues un-spawned ones next frame)
far.truncate(MAX_DISTANT_SPAWNS);
let mut spawns = near;
spawns.extend(far);
for (cx, cz) in &spawns {
let mut entities = Vec::new();
let base_x = cx * CHUNK_SIZE;
Expand Down Expand Up @@ -468,6 +493,9 @@ fn process_chunk_spawns_and_despawns(
];

for (seed_x, seed_z, density, kind) in grass_slots {
#[cfg(target_arch = "wasm32")]
let density = density * 0.5;

let noise = hash2d(tx + seed_x, tz + seed_z);
if noise >= density {
continue;
Expand Down
2 changes: 1 addition & 1 deletion apps/kube/kbve/manifest/kbve-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ spec:
spec:
containers:
- name: kbve
image: ghcr.io/kbve/kbve:1.0.35
image: ghcr.io/kbve/kbve:1.0.36
imagePullPolicy: Always
ports:
- name: http
Expand Down