use crate::View; use crate::{inspector::HideFromInspector, prelude::*}; use bevy::tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task}; use bevy_ecs_tilemap::prelude::*; use rand::prelude::*; use rand_pcg::Pcg64; use rand_seeder::Seeder; use std::sync::mpsc::{channel, Receiver}; use std::sync::Mutex; #[derive(Component, Debug)] pub struct Tilemap; #[derive(Component, Debug)] pub struct TileChan(Mutex>); #[derive(Component, Debug)] pub struct TileInit(Task<()>); pub fn init(mut commands: Commands, assets: Res) { // TODO: I'm pretty determined to not have this sieze up the game despite the large number of entities being added. Should work with a "loading" screen and doing this in the background? let mut rng: Pcg64 = Seeder::from("default_seed").make_rng(); let size = TilemapSize::new(1024, 1024); let tile_size = TilemapTileSize::new(16., 16.); let grid_size = TilemapGridSize::new(16., 16.); let map_type = TilemapType::Square; let texture = TilemapTexture::Single(assets.load("img/Tileset Grass.png")); let storage = TileStorage::empty(size); let tilemap = commands .spawn(( Tilemap, Name::new("Tilemap"), TilemapBundle { grid_size, map_type, size, storage, texture, tile_size, transform: get_tilemap_center_transform(&size, &grid_size, &map_type, f32::MIN), ..Default::default() }, )) .id(); let pool = AsyncComputeTaskPool::get(); let (tx, rx) = channel::(); let task = pool.spawn(async move { for x in 0..size.x { for y in 0..size.y { let position = TilePos::new(x, y); let texture_index = TileTextureIndex(if rng.gen_range(0..1000) > 925 { rng.gen_range(0..(16 * 8)) } else { 0 }); tx.send(TileBundle { position, tilemap_id: TilemapId(tilemap), texture_index, ..Default::default() }) .unwrap(); } } return (); }); commands .entity(tilemap) .insert((TileInit(task), TileChan(Mutex::new(rx)))); } pub fn tile_loaders( mut commands: Commands, mut query: Query<(Entity, &mut TileStorage, &TileChan), With>, mut tasks: Query<&mut TileInit>, mut app_state: ResMut>, ) { // TODO: to avoid locking up the universe we only want to handle a certain number per iteration (Update) let (tilemap, mut storage, chan) = query.single_mut(); let chan = chan.0.lock().unwrap(); let mut counter = 0; let mut tiles_done = false; loop { match chan.recv() { Ok(ev) => { let ref_position = &ev.position; let tile = commands.spawn((HideFromInspector, ev)).id(); storage.set(ref_position, tile); counter += 1; if counter > 5_000 { info!("Finished 100k!"); break; } } Err(err) => { error!("{err:#?}"); tiles_done = true; break; } } } info!("Task: {tasks:#?}"); for mut task in &mut tasks { let poll = future::poll_once(&mut task.0); info!("Poll: {poll:#?}"); if block_on(poll).is_some() { commands.entity(tilemap).remove::(); // need to check if the map is fully loaded before switching // app_state.set(View::InGame); // TODO: transition to game? info!("poll was some"); } } if tasks.iter().peekable().peek().is_none() && tiles_done { app_state.set(View::InGame); } } pub fn exit(mut commands: Commands, q: Query>) { for id in q.iter() { commands.entity(id).despawn_recursive(); } }