bevy-playground/src/map.rs

151 lines
4.8 KiB
Rust
Raw Normal View History

use crate::View;
use crate::{inspector::HideFromInspector, prelude::*};
2024-08-03 00:52:19 -05:00
use bevy::ecs::system::SystemState;
use bevy::tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task};
2024-08-02 16:43:30 -05:00
use bevy_ecs_tilemap::prelude::*;
2024-08-02 13:40:43 -05:00
use rand::prelude::*;
use rand_pcg::Pcg64;
use rand_seeder::Seeder;
use std::sync::mpsc::{channel, Receiver};
use std::sync::Mutex;
2024-08-02 13:40:43 -05:00
#[derive(Component, Debug)]
pub struct Tilemap;
#[derive(Component, Debug)]
pub struct TileChan(Mutex<Receiver<TileBundle>>);
#[derive(Component, Debug)]
pub struct TileInit(Task<()>);
pub fn init(mut commands: Commands, assets: Res<AssetServer>) {
2024-08-02 17:04:16 -05:00
// 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?
2024-08-02 13:40:43 -05:00
let mut rng: Pcg64 = Seeder::from("default_seed").make_rng();
2024-08-02 16:43:30 -05:00
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::<TileBundle>();
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
2024-08-02 17:04:16 -05:00
});
tx.send(TileBundle {
position,
tilemap_id: TilemapId(tilemap),
texture_index,
..Default::default()
})
.unwrap();
}
2024-08-02 17:04:16 -05:00
}
return ();
});
commands
.entity(tilemap)
.insert((TileInit(task), TileChan(Mutex::new(rx))));
}
2024-08-02 17:04:16 -05:00
const MAX_TILES_TO_LOAD_IN_ONE_UPDATE: usize = 5_000;
pub fn tile_loaders(
2024-08-03 00:52:19 -05:00
world: &mut World,
params: &mut SystemState<(
Query<(Entity, &mut TileStorage), With<Tilemap>>,
Query<&TileChan, With<Tilemap>>,
Query<&mut TileInit>,
ResMut<NextState<View>>,
)>,
) {
2024-08-03 00:52:19 -05:00
let (mut query, mut chan, mut tasks, mut next_view) = params.get_mut(world);
let mut tiles_done = true;
let (tilemap, mut storage) = query.single_mut();
2024-08-03 00:52:19 -05:00
let mut tasks_iter = tasks.iter_mut().peekable();
let mut remove_tasks = false;
if tasks_iter.peek().is_none() && tiles_done {
next_view.set(View::InGame)
} else {
for mut task in tasks_iter {
let poll = future::poll_once(&mut task.0);
if block_on(poll).is_some() {
remove_tasks = true;
// task is done, remove it so we don't poll it again
}
}
}
let tile_bundles: Vec<TileBundle> = vec![];
if let Ok(chan) = chan.get_single_mut() {
let chan = chan.0.lock().unwrap();
tiles_done = false;
let mut counter = 0;
loop {
match chan.recv() {
Ok(ev) => {
let ref_position = &ev.position;
2024-08-03 00:52:19 -05:00
let tile = world.spawn((HideFromInspector, ev)).id();
storage.set(ref_position, tile);
counter += 1;
if counter >= MAX_TILES_TO_LOAD_IN_ONE_UPDATE {
info!("Finished {}!", counter);
break;
}
}
Err(_) => {
2024-08-03 00:52:19 -05:00
world.entity_mut(tilemap).remove::<TileChan>();
if counter > 0 {
info!("Finished {counter}! Probably finishing mapgen soon...");
}
// the channel is likely closed, so let's note that and be done
tiles_done = true;
break;
}
}
}
}
2024-08-03 00:52:19 -05:00
if remove_tasks {
world.entity_mut(tilemap).remove::<TileInit>();
}
2024-08-03 00:52:19 -05:00
world.spawn_batch(
tile_bundles
.into_iter()
.map(|t| (HideFromInspector, t))
.into_iter(),
);
}
2024-08-02 16:43:30 -05:00
2024-08-02 13:40:43 -05:00
pub fn exit(mut commands: Commands, q: Query<Entity, With<Tilemap>>) {
for id in q.iter() {
commands.entity(id).despawn_recursive();
}
}