From e83d5323a6e2e8bb2253d6abf9ea8c55734f9055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 10 May 2021 12:56:08 +0200 Subject: [PATCH] Add biomes generation --- survival/biomes/biome_data.py | 9 ++++ survival/biomes/biome_preset.py | 22 +++++++++ survival/biomes/noise.py | 6 +++ survival/generators/tile_generator.py | 69 +++++++++++++++++++++++---- survival/tile.py | 3 +- survival/tile_layer.py | 4 +- 6 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 survival/biomes/biome_data.py create mode 100644 survival/biomes/biome_preset.py create mode 100644 survival/biomes/noise.py diff --git a/survival/biomes/biome_data.py b/survival/biomes/biome_data.py new file mode 100644 index 0000000..eae1966 --- /dev/null +++ b/survival/biomes/biome_data.py @@ -0,0 +1,9 @@ +from survival.biomes.biome_preset import BiomePreset + + +class BiomeData: + def __init__(self, preset: BiomePreset): + self.biome = preset + + def get_diff_value(self, height: float, moisture: float, heat: float): + return (height - self.biome.min_height) + (moisture - self.biome.min_moisture) + (heat - self.biome.min_heat) diff --git a/survival/biomes/biome_preset.py b/survival/biomes/biome_preset.py new file mode 100644 index 0000000..b11cd3c --- /dev/null +++ b/survival/biomes/biome_preset.py @@ -0,0 +1,22 @@ +import random + +from survival.tile import Tile + + +class BiomePreset: + def __init__(self, name, min_height: float, min_moisture: float, min_heat: float, tiles: list[Tile]): + self.name = name + self.min_height = min_height + self.min_moisture = min_moisture + self.min_heat = min_heat + self.tiles = tiles + + def get_new_tile(self): + tile = random.choice(self.tiles) + return Tile(origin=tile.origin, cost=tile.cost, biome=self) + + def get_tile_sprite(self): + pass + + def match_conditions(self, height, moisture, heat): + return height >= self.min_height and moisture >= self.min_moisture and heat >= self.min_heat diff --git a/survival/biomes/noise.py b/survival/biomes/noise.py new file mode 100644 index 0000000..7635935 --- /dev/null +++ b/survival/biomes/noise.py @@ -0,0 +1,6 @@ +from perlin_noise import PerlinNoise + + +def generate_noise(width: int, height: int, octaves, seed): + noise_map = PerlinNoise(octaves=octaves, seed=seed) + return [[noise_map([x / width, y / height]) for y in range(width)] for x in range(height)] diff --git a/survival/generators/tile_generator.py b/survival/generators/tile_generator.py index 5479e79..22555cb 100644 --- a/survival/generators/tile_generator.py +++ b/survival/generators/tile_generator.py @@ -1,19 +1,70 @@ import random +from survival.biomes.biome_data import BiomeData +from survival.biomes.biome_preset import BiomePreset +from survival.biomes.noise import generate_noise from survival.tile import Tile class TileGenerator: - templates = [ - [(0, 0), 1], # Grass 1 - [(32, 0), 1], # Grass 2 - [(64, 0), 1], # Grass 3 - [(96, 0), 1], # Grass 4 - [(64, 64), 20], # Sand - [(96, 64), 20], # Puddle + Tiles = { + "Grass1": Tile(origin=(0, 0), cost=1), + "Grass2": Tile(origin=(32, 0), cost=1), + "Grass3": Tile(origin=(64, 0), cost=1), + "Grass4": Tile(origin=(96, 0), cost=1), + "Sand": Tile(origin=(64, 64), cost=20), + "Puddle": Tile(origin=(96, 64), cost=20), + } + + TilesValues = list(Tiles.values()) + + Biomes = [ + BiomePreset("Desert", min_height=0.2, min_moisture=0, min_heat=0.5, tiles=[Tiles["Grass1"], Tiles["Grass2"], + Tiles["Grass3"], Tiles["Grass4"]]), + BiomePreset("Grassland", min_height=0.2, min_moisture=0.5, min_heat=0.3, tiles=[Tiles["Sand"]]), + BiomePreset("Marsh", min_height=0.1, min_moisture=0.6, min_heat=0.2, tiles=[Tiles["Puddle"]]) ] @staticmethod def get_random_tile(): - template = random.choice(TileGenerator.templates) - return Tile(template[0], template[1]) + tile = random.choice(TileGenerator.TilesValues) + return Tile(origin=tile.origin, cost=tile.cost) + + @staticmethod + def generate_random_tiles(width: int, height: int) -> list[list[Tile]]: + return [[TileGenerator.get_random_tile() for _ in range(width)] for _ in range(height)] + + @staticmethod + def generate_biome_tiles(width: int, height: int): + seed = random.randint(0, 9999999) + octaves = 10 + height_map = generate_noise(width, height, octaves, seed) + moisture_map = generate_noise(width, height, octaves, seed) + heat_map = generate_noise(width, height, octaves, seed) + + return [[TileGenerator.get_biome(height_map[y][x], moisture_map[y][x], heat_map[y][x]).get_new_tile() for x in + range(width)] for y in range(height)] + + @staticmethod + def get_biome(height, moisture, heat) -> BiomePreset: + matching_biomes = list() + + for biome in TileGenerator.Biomes: + if biome.match_conditions(height, moisture, heat): + matching_biomes.append(BiomeData(biome)) + + current_value = 0 + found_biome = None + + for biome in matching_biomes: + if found_biome is None: + found_biome = biome.biome + current_value = biome.get_diff_value(height, moisture, heat) + elif biome.get_diff_value(height, moisture, heat) < current_value: + found_biome = biome.biome + current_value = biome.get_diff_value(height, moisture, heat) + + if found_biome is None: + found_biome = TileGenerator.Biomes[0] + + return found_biome diff --git a/survival/tile.py b/survival/tile.py index 6c23f22..00aa7fc 100644 --- a/survival/tile.py +++ b/survival/tile.py @@ -1,5 +1,6 @@ class Tile: - def __init__(self, origin, cost): + def __init__(self, origin: tuple = (0, 0), cost: int = 1, biome=None): self.origin = origin self.cost = cost + self.biome = biome self.image = None diff --git a/survival/tile_layer.py b/survival/tile_layer.py index 64d2b45..78ac9e0 100644 --- a/survival/tile_layer.py +++ b/survival/tile_layer.py @@ -1,12 +1,14 @@ from survival.image import Image from survival.generators.tile_generator import TileGenerator +from survival.tile import Tile class TileLayer: def __init__(self, width, height): self.width = width self.height = height - self.tiles = [[TileGenerator.get_random_tile() for x in range(self.width)] for y in range(self.height)] + self.tiles: list[list[Tile]] = TileGenerator.generate_biome_tiles(width, height) + # self.tiles: list[list[Tile]] = TileGenerator.generate_random_tiles(width, height) self.image = Image('atlas.png') def draw(self, camera, visible_area):