bevy-learning

A learning project of mine.
git clone git://archive.git.mtrnord.blog/MTRNord/bevy-learning.git
Log | Files | Refs

world.rs (6040B)


      1 use crate::entities::markers::Wall;
      2 use crate::plugins::player::PLAYER_START;
      3 use crate::{AssetsLoading, GameState};
      4 use bevy::asset::LoadState;
      5 use bevy::prelude::*;
      6 use noise::{NoiseFn, OpenSimplex, Seedable};
      7 use rand::Rng;
      8 use std::ops;
      9 
     10 pub struct MainCamera;
     11 
     12 #[derive(Default, Clone)]
     13 pub struct WorldState {
     14     pub map_loaded: bool,
     15     pub collisions_loaded: bool,
     16     pub level: usize,
     17     pub requested_level: usize,
     18     pub world: Option<Entity>,
     19     pub collisions: Vec<Vec2>,
     20     pub world_noise: OpenSimplex,
     21     pub seed: Option<u32>,
     22 }
     23 
     24 pub struct WorldPlugin;
     25 
     26 #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
     27 pub struct GridLocation(pub i32, pub i32);
     28 
     29 impl ops::Add<GridLocation> for GridLocation {
     30     type Output = GridLocation;
     31 
     32     fn add(self, rhs: GridLocation) -> Self::Output {
     33         GridLocation(self.0 + rhs.0, self.1 + rhs.1)
     34     }
     35 }
     36 
     37 impl From<Vec2> for GridLocation {
     38     fn from(vec: Vec2) -> Self {
     39         Self {
     40             0: vec.x.round() as i32,
     41             1: vec.y.round() as i32,
     42         }
     43     }
     44 }
     45 
     46 impl From<[f64; 2]> for GridLocation {
     47     fn from(coords: [f64; 2]) -> Self {
     48         Self {
     49             0: coords[0] as i32,
     50             1: coords[1] as i32,
     51         }
     52     }
     53 }
     54 
     55 impl From<GridLocation> for [f64; 2] {
     56     fn from(loc: GridLocation) -> Self {
     57         [loc.0 as f64, loc.1 as f64]
     58     }
     59 }
     60 
     61 const LERP_LAMBDA: f32 = 5.0;
     62 
     63 fn render_grid_location_to_transform(
     64     time: Res<Time>,
     65     mut query: Query<(&GridLocation, &mut Transform)>,
     66 ) {
     67     for (grid_location, mut transform) in query.iter_mut() {
     68         let target_x = SPRITE_WIDTH * grid_location.0 as f32;
     69         transform.translation.x = transform.translation.x
     70             * (1.0 - LERP_LAMBDA * time.delta_seconds())
     71             + target_x * LERP_LAMBDA * time.delta_seconds();
     72         let target_y = SPRITE_WIDTH * grid_location.1 as f32;
     73         transform.translation.y = transform.translation.y
     74             * (1.0 - LERP_LAMBDA * time.delta_seconds())
     75             + target_y * LERP_LAMBDA * time.delta_seconds();
     76     }
     77 }
     78 
     79 impl Plugin for WorldPlugin {
     80     fn build(&self, app: &mut AppBuilder) {
     81         app.add_system(draw.system())
     82             .add_system(render_grid_location_to_transform.system());
     83     }
     84 }
     85 
     86 pub const SPRITE_WIDTH: f32 = 16.0;
     87 pub const SPRITE_HEIGHT: f32 = 16.0;
     88 
     89 #[derive(Bundle)]
     90 struct WallBundle {
     91     pub grid_location: GridLocation,
     92     pub _wall: Wall,
     93 
     94     #[bundle]
     95     pub sprite: SpriteSheetBundle,
     96 }
     97 
     98 fn setup_wall(
     99     grid_location: GridLocation,
    100     game_state: &ResMut<GameState>,
    101     texture_atlases: &mut ResMut<Assets<TextureAtlas>>,
    102     textures: &Res<Assets<Texture>>,
    103 ) -> WallBundle {
    104     let texture_handle = game_state.asset_map.get("Sunnyland").unwrap();
    105     let texture: &Texture = textures.get(texture_handle.id).unwrap();
    106     let cols = texture.size.width / 16;
    107     let rows = texture.size.height / 16;
    108     let texture_atlas = TextureAtlas::from_grid(
    109         texture_handle.clone(),
    110         Vec2::new(17.0, 17.0),
    111         cols as usize,
    112         rows as usize,
    113     );
    114     let texture_atlas_handle = texture_atlases.add(texture_atlas);
    115     WallBundle {
    116         grid_location,
    117         _wall: Wall,
    118         sprite: SpriteSheetBundle {
    119             sprite: TextureAtlasSprite {
    120                 index: 48,
    121                 ..Default::default()
    122             },
    123             transform: Transform::from_translation(Vec3::new(
    124                 SPRITE_WIDTH * grid_location.0 as f32,
    125                 SPRITE_HEIGHT * grid_location.1 as f32,
    126                 -1.0,
    127             )),
    128             texture_atlas: texture_atlas_handle,
    129             ..Default::default()
    130         },
    131     }
    132 }
    133 
    134 fn draw(
    135     mut commands: Commands,
    136     mut game_state: ResMut<GameState>,
    137     mut texture_atlases: ResMut<Assets<TextureAtlas>>,
    138     server: Res<AssetServer>,
    139     loading: Res<AssetsLoading>,
    140     textures: Res<Assets<Texture>>,
    141 ) {
    142     if game_state.world_state.map_loaded
    143         && game_state.world_state.level == game_state.world_state.requested_level
    144     {
    145         return;
    146     }
    147 
    148     let mut ready = true;
    149     for handle in loading.0.iter() {
    150         match server.get_load_state(handle) {
    151             LoadState::Failed => {
    152                 ready = false;
    153             }
    154             LoadState::Loaded => {}
    155             _ => {
    156                 ready = false;
    157             }
    158         }
    159     }
    160 
    161     if !ready {
    162         return;
    163     }
    164 
    165     if game_state.world_state.seed.is_none() {
    166         let mut rng = rand::thread_rng();
    167         game_state.world_state.seed = Some(rng.gen());
    168     }
    169 
    170     game_state.world_state.world_noise =
    171         OpenSimplex::new().set_seed(game_state.world_state.seed.unwrap());
    172     let noise = &game_state.world_state.world_noise;
    173 
    174     for chunk_y in -2..2 {
    175         for chunk_x in -2..2 {
    176             let chunk = generate_chunk(
    177                 chunk_x,
    178                 chunk_y,
    179                 noise,
    180                 &game_state,
    181                 &mut texture_atlases,
    182                 &textures,
    183             );
    184             commands.spawn_batch(chunk);
    185         }
    186     }
    187 
    188     game_state.world_state.level = game_state.world_state.requested_level;
    189     game_state.world_state.map_loaded = true;
    190 }
    191 
    192 fn generate_chunk(
    193     chunk_x: i32,
    194     chunk_y: i32,
    195     noise: &OpenSimplex,
    196     game_state: &ResMut<GameState>,
    197     texture_atlases: &mut ResMut<Assets<TextureAtlas>>,
    198     textures: &Res<Assets<Texture>>,
    199 ) -> Vec<WallBundle> {
    200     let mut bundles = Vec::new();
    201     for y in -8..8 {
    202         for x in -8..8 {
    203             let full_x = (chunk_x * 16) + x;
    204             let full_y = (chunk_y * 16) + y;
    205             let coord = GridLocation(full_x, full_y);
    206 
    207             if full_x == PLAYER_START.0 && full_y == PLAYER_START.1 {
    208                 continue;
    209             }
    210             let f = noise.get([(full_x as f32 / 16.0) as f64, (full_y as f32 / 16.0) as f64]);
    211             let noise_value = f * 16.0 + (16.0 / 2.0);
    212 
    213             if noise_value > 4.8 {
    214                 bundles.push(setup_wall(coord, game_state, texture_atlases, textures));
    215             }
    216         }
    217     }
    218     bundles
    219 }