Skip to main content

gizmo_engine/systems/
chunk_system.rs

1use crate::core::World;
2use gizmo_math::Vec3;
3use std::collections::{HashMap, HashSet};
4
5pub const CHUNK_SIZE: f32 = 100.0;
6pub const CHUNK_LOAD_RADIUS: i32 = 2; // Oyuncunun etrafındaki 5x5'lik grid yüklenir
7
8pub type ChunkCoord = (i32, i32);
9
10/// Hangi Entity'nin hangi Chunk'a ait olduğunu tutan Bileşen (Component)
11pub struct ChunkEntity {
12    pub coord: ChunkCoord,
13}
14
15/// Tüm harita yükleme/silme işlemlerini yöneten Sistem Kaynağı (Resource)
16pub struct ChunkManager {
17    /// O an RAM'de yüklü olan Chunk'ların koordinatları
18    pub active_chunks: HashSet<ChunkCoord>,
19    /// Hangi Chunk'ta hangi Entity'lerin olduğu (Silmek için kullanacağız)
20    pub chunk_entities: HashMap<ChunkCoord, Vec<u64>>,
21    /// Oyuncunun bir önceki frame'deki konumu (Sadece Chunk değiştiğinde işlem yapmak için)
22    pub last_player_chunk: ChunkCoord,
23}
24
25impl Default for ChunkManager {
26    fn default() -> Self {
27        Self {
28            active_chunks: HashSet::new(),
29            chunk_entities: HashMap::new(),
30            last_player_chunk: (i32::MAX, i32::MAX), // Başlangıçta tetiklenmesi için imkansız bir değer
31        }
32    }
33}
34
35impl ChunkManager {
36    pub fn world_pos_to_chunk(pos: Vec3) -> ChunkCoord {
37        (
38            (pos.x / CHUNK_SIZE).floor() as i32,
39            (pos.z / CHUNK_SIZE).floor() as i32,
40        )
41    }
42
43    pub fn chunk_to_world_pos(coord: ChunkCoord) -> Vec3 {
44        Vec3::new(
45            (coord.0 as f32) * CHUNK_SIZE + (CHUNK_SIZE / 2.0),
46            0.0,
47            (coord.1 as f32) * CHUNK_SIZE + (CHUNK_SIZE / 2.0),
48        )
49    }
50
51    /// Bir Entity oluşturulduğunda onu Chunk sistemine kaydeder
52    pub fn register_entity(&mut self, coord: ChunkCoord, entity_id: u64) {
53        self.chunk_entities
54            .entry(coord)
55            .or_default()
56            .push(entity_id);
57    }
58}
59
60/// Bu sistem her frame çağrılır. Oyuncunun pozisyonunu kontrol eder.
61/// Eğer oyuncu yeni bir Chunk sınırından geçtiyse, eski Chunk'ları silip yenilerini yükler.
62/// `load_callback` fonksiyonu: Yeni yüklenen her Chunk için kullanıcının obje spawn etmesini sağlar.
63pub fn open_world_chunk_system<F, U>(
64    world: &mut World,
65    player_pos: Vec3,
66    mut load_callback: F,
67    mut unload_callback: U,
68) where
69    F: FnMut(&mut World, ChunkCoord),
70    U: FnMut(&mut World, ChunkCoord, Vec<u64>),
71{
72    // ChunkManager resource'unu al (Yoksa oluştur)
73    if world.get_resource::<ChunkManager>().is_none() {
74        world.insert_resource(ChunkManager::default());
75    }
76
77    let current_chunk = ChunkManager::world_pos_to_chunk(player_pos);
78    let mut chunks_to_load = Vec::new();
79    let mut chunks_to_unload = Vec::new();
80
81    // Sadece Chunk değiştiyse işlem yap (Performans optimizasyonu)
82    {
83        let mut manager = world.get_resource_mut::<ChunkManager>().unwrap();
84        if manager.last_player_chunk == current_chunk {
85            return; // Hala aynı bölgedeyiz, bir şey yapmaya gerek yok
86        }
87        manager.last_player_chunk = current_chunk;
88
89        let mut expected_chunks = HashSet::new();
90
91        // Oyuncunun etrafındaki 5x5 gridi hesapla
92        for x in -CHUNK_LOAD_RADIUS..=CHUNK_LOAD_RADIUS {
93            for z in -CHUNK_LOAD_RADIUS..=CHUNK_LOAD_RADIUS {
94                expected_chunks.insert((current_chunk.0 + x, current_chunk.1 + z));
95            }
96        }
97
98        // Hangi Chunk'lar silinecek? (Şu an aktif olan ama expected içinde olmayanlar)
99        for &chunk in &manager.active_chunks {
100            if !expected_chunks.contains(&chunk) {
101                chunks_to_unload.push(chunk);
102            }
103        }
104
105        // Hangi Chunk'lar yüklenecek? (Expected içinde olup aktif olmayanlar)
106        for &chunk in &expected_chunks {
107            if !manager.active_chunks.contains(&chunk) {
108                chunks_to_load.push(chunk);
109            }
110        }
111    } // manager borrow biter
112
113    // --- SİLME İŞLEMİ (UNLOAD) ---
114    for chunk in chunks_to_unload {
115        let mut manager = world.get_resource_mut::<ChunkManager>().unwrap();
116        manager.active_chunks.remove(&chunk);
117
118        if let Some(entities) = manager.chunk_entities.remove(&chunk) {
119            // Callback çağrılacak
120            drop(manager);
121            unload_callback(world, chunk, entities);
122        }
123        tracing::info!("🗑️ Chunk Yüklemesi Kaldırıldı (Havuzlandı): {:?}", chunk);
124    }
125
126    // --- YÜKLEME İŞLEMİ (LOAD) ---
127    for chunk in chunks_to_load {
128        {
129            let mut manager = world.get_resource_mut::<ChunkManager>().unwrap();
130            manager.active_chunks.insert(chunk);
131        }
132        tracing::info!("🌍 Yeni Chunk Yüklendi: {:?}", chunk);
133
134        // Kullanıcının sağladığı (Örn: rpg_demo.rs içindeki) ağaç oluşturma fonksiyonunu çağırıyoruz
135        load_callback(world, chunk);
136    }
137}