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.
## 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] Area exit point
* [x] Area starting point
* [x] BSP Interior
* [x] BSP Rooms
* [x] Cellular automata
* [x] Cull unreachable areas
* [ ] Diffusion-Limited Aggregation (DLA)
* [x] Drunkard's walk
* [ ] Maze
* [x] Noise generator
* [ ] Prefabs
* [x] Room corridors nearest
* [x] Simple rooms
* [ ] Voronoi hive
* [ ] Wave Function Collapse
* Map modifiers (filters)
* [x] Area exit point
* [x] Area starting point
* [x] Cellular automata
* [x] Cull unreachable areas
* [x] Room corridors nearest
* [ ] Voronoi spawning
## Usage

View File

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

View File

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

View File

@ -8,7 +8,7 @@
},
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server",
"server": "webpack-dev-server",
"deploy": "gh-pages -d dist"
},
"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)
//! for more information about the algorithm behind this generator.
//!
//! Since this algorithm works in interations it is possible to take existing map
//! and apply single interaction to it. This is the idea behind MapModifier implementation.
//! This algorithm requires that map first is filtered with some noise.
//! For example `UniformNoise`. It can also be apply to any other non empty map.
//!
//! Example generator usage:
//! Example usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter};
@ -26,35 +26,27 @@ use crate::MapFilter;
use crate::{Map, TileType};
/// Map generator and modifier
pub struct CellularAutomata {}
/// Map filter
pub struct CellularAutomata {
num_iteraction: u32,
}
impl MapFilter for CellularAutomata {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build(map, rng)
fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map {
self.build(map)
}
}
impl CellularAutomata {
/// Create generator which will create map with the given dimension.
pub fn new() -> Box<CellularAutomata> {
Box::new(CellularAutomata {})
Box::new(CellularAutomata { num_iteraction: 15})
}
/// Generate map
fn build(&self, map: &Map, rng: &mut StdRng) -> Map {
fn build(&self, map: &Map) -> Map {
let mut new_map = map.clone();
// First we completely randomize the map, setting 55% of it to be floor.
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 {
for _ in 0..self.num_iteraction {
new_map = apply_iteration(&new_map);
}

View File

@ -10,12 +10,14 @@
//! ```
//! use mapgen::{MapFilter, MapBuilder, Map, TileType};
//! use mapgen::filter::{
//! NoiseGenerator,
//! CellularAutomata,
//! starting_point::{AreaStartingPosition, XStart, YStart}
//! };
//! use mapgen::geometry::Point;
//!
//! let map = MapBuilder::new()
//! .with(NoiseGenerator::uniform())
//! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
//! .build_map(80, 50);
@ -32,6 +34,7 @@ pub mod cellular_automata;
pub mod cull_unreachable;
pub mod distant_exit;
pub mod drunkard;
pub mod noise_generator;
pub mod simple_rooms;
pub mod rooms_corridors_nearest;
pub mod starting_point;
@ -42,6 +45,7 @@ pub use cellular_automata::CellularAutomata;
pub use cull_unreachable::CullUnreachable;
pub use distant_exit::DistantExit;
pub use drunkard::DrunkardsWalk;
pub use noise_generator::NoiseGenerator;
pub use simple_rooms::SimpleRooms;
pub use rooms_corridors_nearest::NearestCorridors;
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::filter::{
//! NoiseGenerator,
//! CellularAutomata,
//! starting_point::{AreaStartingPosition, XStart, YStart}
//! };
//! use mapgen::geometry::Point;
//!
//! let map = MapBuilder::new()
//! .with(NoiseGenerator::uniform())
//! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
//! .build_map(80, 50);
@ -31,6 +33,7 @@ pub mod geometry;
pub mod map;
pub use map::{Map, Symmetry, TileType};
pub use filter::*;
pub (crate) mod dijkstra;
pub (crate) mod random;
@ -90,12 +93,16 @@ impl MapBuilder {
#[cfg(test)]
mod tests {
use super::*;
use filter::CellularAutomata;
use filter::{AreaStartingPosition, XStart, YStart};
use filter::{
CellularAutomata,
NoiseGenerator,
{AreaStartingPosition, XStart, YStart},
};
#[test]
fn test_ca_map() {
let map = MapBuilder::new()
.with(NoiseGenerator::new(0.55))
.with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
.build_map(80, 50);