Added NoiseGenerator

This commit is contained in:
klangner 2020-09-23 10:31:13 +02:00
parent 416cd633eb
commit be1d9e95bb
8 changed files with 120 additions and 55 deletions

View File

@ -7,28 +7,27 @@
Generate procedural maps for games. [Try it in the browser](https://klangner.github.io/mapgen.rs/) using WebAssembly. Generate procedural maps for games. [Try it in the browser](https://klangner.github.io/mapgen.rs/) using WebAssembly.
## Features ## Map filters
### Dungeons This library consists of different map filters which can be combined to create custom map generator.
* Map generators ### Implemented filters
* [x] BSP Interior
* [x] BSP Rooms * [x] Area exit point
* [x] Cellular automata * [x] Area starting point
* [ ] Diffusion-Limited Aggregation (DLA) * [x] BSP Interior
* [x] Drunkard's walk * [x] BSP Rooms
* [ ] Maze * [x] Cellular automata
* [ ] Prefabs * [x] Cull unreachable areas
* [x] Simple rooms * [ ] Diffusion-Limited Aggregation (DLA)
* [ ] Voronoi hive * [x] Drunkard's walk
* [ ] Wave Function Collapse * [ ] Maze
* Map modifiers (filters) * [x] Noise generator
* [x] Area exit point * [ ] Prefabs
* [x] Area starting point * [x] Room corridors nearest
* [x] Cellular automata * [x] Simple rooms
* [x] Cull unreachable areas * [ ] Voronoi hive
* [x] Room corridors nearest * [ ] Wave Function Collapse
* [ ] Voronoi spawning
## Usage ## Usage

View File

@ -5,7 +5,13 @@
``` ```
wasm-pack build wasm-pack build
cd www cd www
npm run start npm run server
```
Deply application
```
cd www
npm run deploy
``` ```
This app uses: This app uses:

View File

@ -2,16 +2,7 @@ use wasm_bindgen::prelude::*;
use web_sys; use web_sys;
use rand::prelude::*; use rand::prelude::*;
use mapgen::{MapBuilder, TileType}; use mapgen::{MapBuilder, TileType};
use mapgen::filter::{ use mapgen::filter::*;
CellularAutomata,
SimpleRooms,
BspInterior,
{AreaStartingPosition, XStart, YStart},
CullUnreachable,
DistantExit,
NearestCorridors,
DrunkardsWalk,
};
#[wasm_bindgen] #[wasm_bindgen]
@ -36,6 +27,7 @@ impl World {
World::print_map_info(format!("Cellular Automata with the seed: {}", seed)); World::print_map_info(format!("Cellular Automata with the seed: {}", seed));
let mut rng = StdRng::seed_from_u64(seed as u64); let mut rng = StdRng::seed_from_u64(seed as u64);
let map = MapBuilder::new() let map = MapBuilder::new()
.with(NoiseGenerator::uniform())
.with(CellularAutomata::new()) .with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
.with(CullUnreachable::new()) .with(CullUnreachable::new())

View File

@ -8,7 +8,7 @@
}, },
"scripts": { "scripts": {
"build": "webpack --config webpack.config.js", "build": "webpack --config webpack.config.js",
"start": "webpack-dev-server", "server": "webpack-dev-server",
"deploy": "gh-pages -d dist" "deploy": "gh-pages -d dist"
}, },
"repository": { "repository": {

View File

@ -1,12 +1,12 @@
//! Cellular automata map generator and modifier. //! Cellular automata map filter.
//! //!
//! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels) //! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels)
//! for more information about the algorithm behind this generator. //! for more information about the algorithm behind this generator.
//! //!
//! Since this algorithm works in interations it is possible to take existing map //! This algorithm requires that map first is filtered with some noise.
//! and apply single interaction to it. This is the idea behind MapModifier implementation. //! For example `UniformNoise`. It can also be apply to any other non empty map.
//! //!
//! Example generator usage: //! Example usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter};
@ -26,35 +26,27 @@ use crate::MapFilter;
use crate::{Map, TileType}; use crate::{Map, TileType};
/// Map generator and modifier /// Map filter
pub struct CellularAutomata {} pub struct CellularAutomata {
num_iteraction: u32,
}
impl MapFilter for CellularAutomata { impl MapFilter for CellularAutomata {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map {
self.build(map, rng) self.build(map)
} }
} }
impl CellularAutomata { impl CellularAutomata {
/// Create generator which will create map with the given dimension. /// Create generator which will create map with the given dimension.
pub fn new() -> Box<CellularAutomata> { pub fn new() -> Box<CellularAutomata> {
Box::new(CellularAutomata {}) Box::new(CellularAutomata { num_iteraction: 15})
} }
/// Generate map /// Generate map
fn build(&self, map: &Map, rng: &mut StdRng) -> Map { fn build(&self, map: &Map) -> Map {
let mut new_map = map.clone(); let mut new_map = map.clone();
// First we completely randomize the map, setting 55% of it to be floor. for _ in 0..self.num_iteraction {
for y in 1..new_map.height-1 {
for x in 1..new_map.width-1 {
let roll = rng.next_u32() % 100;
if roll > 55 { new_map.set_tile(x, y, TileType::Floor) }
else { new_map.set_tile(x, y, TileType::Wall) }
}
}
// Now we iteratively apply cellular automata rules
for _ in 0..15 {
new_map = apply_iteration(&new_map); new_map = apply_iteration(&new_map);
} }

View File

@ -10,12 +10,14 @@
//! ``` //! ```
//! use mapgen::{MapFilter, MapBuilder, Map, TileType}; //! use mapgen::{MapFilter, MapBuilder, Map, TileType};
//! use mapgen::filter::{ //! use mapgen::filter::{
//! NoiseGenerator,
//! CellularAutomata, //! CellularAutomata,
//! starting_point::{AreaStartingPosition, XStart, YStart} //! starting_point::{AreaStartingPosition, XStart, YStart}
//! }; //! };
//! use mapgen::geometry::Point; //! use mapgen::geometry::Point;
//! //!
//! let map = MapBuilder::new() //! let map = MapBuilder::new()
//! .with(NoiseGenerator::uniform())
//! .with(CellularAutomata::new()) //! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) //! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
//! .build_map(80, 50); //! .build_map(80, 50);
@ -32,6 +34,7 @@ pub mod cellular_automata;
pub mod cull_unreachable; pub mod cull_unreachable;
pub mod distant_exit; pub mod distant_exit;
pub mod drunkard; pub mod drunkard;
pub mod noise_generator;
pub mod simple_rooms; pub mod simple_rooms;
pub mod rooms_corridors_nearest; pub mod rooms_corridors_nearest;
pub mod starting_point; pub mod starting_point;
@ -42,6 +45,7 @@ pub use cellular_automata::CellularAutomata;
pub use cull_unreachable::CullUnreachable; pub use cull_unreachable::CullUnreachable;
pub use distant_exit::DistantExit; pub use distant_exit::DistantExit;
pub use drunkard::DrunkardsWalk; pub use drunkard::DrunkardsWalk;
pub use noise_generator::NoiseGenerator;
pub use simple_rooms::SimpleRooms; pub use simple_rooms::SimpleRooms;
pub use rooms_corridors_nearest::NearestCorridors; pub use rooms_corridors_nearest::NearestCorridors;
pub use starting_point::{AreaStartingPosition, XStart, YStart}; pub use starting_point::{AreaStartingPosition, XStart, YStart};

View File

@ -0,0 +1,65 @@
//! Apply noise to the map.
//! Each cell will be set to Floor with the given probabilty.
//!
//! Example usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter};
//! use mapgen::filter::NoiseGenerator;
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = NoiseGenerator::uniform();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
use rand::prelude::*;
use crate::MapFilter;
use crate::{Map, TileType};
/// Map noise generator
pub struct NoiseGenerator {
prob: f32,
}
impl MapFilter for NoiseGenerator {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build(map, rng)
}
}
impl NoiseGenerator {
/// Create noise with custom probability
pub fn new(prob: f32) -> Box<NoiseGenerator> {
Box::new(NoiseGenerator {
prob,
})
}
/// Create uniform noise (Probablity 0.5)
pub fn uniform() -> Box<NoiseGenerator> {
Box::new(NoiseGenerator {
prob: 0.5,
})
}
/// Generate map
fn build(&self, map: &Map, rng: &mut StdRng) -> Map {
let mut new_map = map.clone();
let p = (self.prob * 100.0) as u32;
for y in 1..new_map.height-1 {
for x in 1..new_map.width-1 {
let roll = rng.next_u32() % 100;
if roll > p { new_map.set_tile(x, y, TileType::Floor) }
else { new_map.set_tile(x, y, TileType::Wall) }
}
}
new_map
}
}

View File

@ -10,12 +10,14 @@
//! ``` //! ```
//! use mapgen::{MapFilter, MapBuilder, Map, TileType}; //! use mapgen::{MapFilter, MapBuilder, Map, TileType};
//! use mapgen::filter::{ //! use mapgen::filter::{
//! NoiseGenerator,
//! CellularAutomata, //! CellularAutomata,
//! starting_point::{AreaStartingPosition, XStart, YStart} //! starting_point::{AreaStartingPosition, XStart, YStart}
//! }; //! };
//! use mapgen::geometry::Point; //! use mapgen::geometry::Point;
//! //!
//! let map = MapBuilder::new() //! let map = MapBuilder::new()
//! .with(NoiseGenerator::uniform())
//! .with(CellularAutomata::new()) //! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) //! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
//! .build_map(80, 50); //! .build_map(80, 50);
@ -31,6 +33,7 @@ pub mod geometry;
pub mod map; pub mod map;
pub use map::{Map, Symmetry, TileType}; pub use map::{Map, Symmetry, TileType};
pub use filter::*;
pub (crate) mod dijkstra; pub (crate) mod dijkstra;
pub (crate) mod random; pub (crate) mod random;
@ -90,12 +93,16 @@ impl MapBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use filter::CellularAutomata; use filter::{
use filter::{AreaStartingPosition, XStart, YStart}; CellularAutomata,
NoiseGenerator,
{AreaStartingPosition, XStart, YStart},
};
#[test] #[test]
fn test_ca_map() { fn test_ca_map() {
let map = MapBuilder::new() let map = MapBuilder::new()
.with(NoiseGenerator::new(0.55))
.with(CellularAutomata::new()) .with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
.build_map(80, 50); .build_map(80, 50);