Merge pull request 'dev-jakklu' (#6) from dev-jakklu into master

Reviewed-on: #6
This commit is contained in:
Jakub Klupieć 2021-05-10 14:52:59 +02:00
commit 6c0074f52d
20 changed files with 183 additions and 57 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,4 +1,5 @@
import os import os
import pygame as pg import pygame as pg
for i in os.listdir('.'): for i in os.listdir('.'):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -3,9 +3,9 @@ import pygame
from settings import SCREEN_WIDTH, SCREEN_HEIGHT from settings import SCREEN_WIDTH, SCREEN_HEIGHT
from survival.camera import Camera from survival.camera import Camera
from survival.game_map import GameMap from survival.game_map import GameMap
from survival.player_generator import PlayerGenerator from survival.generators.player_generator import PlayerGenerator
from survival.resource_generator import ResourceGenerator from survival.generators.resource_generator import ResourceGenerator
from survival.world_generator import WorldGenerator from survival.generators.world_generator import WorldGenerator
if __name__ == '__main__': if __name__ == '__main__':
pygame.init() pygame.init()

View File

@ -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)

View File

@ -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

6
survival/biomes/noise.py Normal file
View File

@ -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)]

View File

@ -1,13 +1,11 @@
import time as _time import time as _time
from functools import lru_cache as _lru_cache from functools import lru_cache as _lru_cache
from typing import Any as _Any
from typing import Iterable as _Iterable
from typing import List as _List from typing import List as _List
from typing import Tuple as _Tuple
from typing import Type as _Type from typing import Type as _Type
from typing import TypeVar as _TypeVar from typing import TypeVar as _TypeVar
from typing import Any as _Any
from typing import Tuple as _Tuple
from typing import Iterable as _Iterable
version = '1.3' version = '1.3'

View File

@ -24,3 +24,6 @@ class GameMap:
def is_colliding(self, pos): def is_colliding(self, pos):
return pos[0] < 0 or pos[0] >= self.width or pos[1] < 0 or pos[1] >= self.height or self.entity_layer.is_colliding(pos) return pos[0] < 0 or pos[0] >= self.width or pos[1] < 0 or pos[1] >= self.height or self.entity_layer.is_colliding(pos)
def get_cost(self, pos):
return self.tile_layer.get_cost(pos)

View File

@ -0,0 +1,73 @@
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:
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("Forest", min_height=0.2, min_moisture=0.4, min_heat=0.4, tiles=[Tiles["Sand"]]),
BiomePreset("Grassland", min_height=0.2, min_moisture=0.5, min_heat=0.3, tiles=[Tiles["Sand"]]),
BiomePreset("Marsh", min_height=0.3, min_moisture=0.5, min_heat=0.62, tiles=[Tiles["Puddle"]]),
BiomePreset("Ocean", min_height=0, min_moisture=0, min_heat=0, tiles=[Tiles["Sand"]]),
BiomePreset("Tundra", min_height=0.2, min_moisture=0, min_heat=0, tiles=[Tiles["Puddle"]])
]
@staticmethod
def get_random_tile():
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

View File

@ -1,4 +1,5 @@
from enum import Enum from enum import Enum
from queue import PriorityQueue
from survival import GameMap from survival import GameMap
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
@ -12,57 +13,44 @@ class Action(Enum):
class State: class State:
def __init__(self, position, direction): def __init__(self, position: tuple[int, int], direction: Direction):
self.position = position self.position = position
self.direction = direction self.direction = direction
class Node: class Node:
def __init__(self, state: State, parent=None, action=None): def __init__(self, state: State, parent=None, action=None, cost=None):
self.state = state self.state = state
self.parent = parent self.parent = parent
self.action = action self.action = action
self.cost = cost
def __lt__(self, other):
return self.cost < other.cost
def __eq__(self, other):
return self.cost == other.cost
def get_moved_position(position, direction): def get_moved_position(position: tuple[int, int], direction: Direction):
vector = Direction.get_vector(direction) vector = Direction.get_vector(direction)
return position[0] + vector[0], position[1] + vector[1] return position[0] + vector[0], position[1] + vector[1]
def get_states(state: State, game_map: GameMap): def get_states(state: State, game_map: GameMap) -> list[tuple[Action, State, int]]:
states = list() states = list()
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)))) states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1))
states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction)))) states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction)), 1))
target_state = get_moved_position(state.position, state.direction) target_position = get_moved_position(state.position, state.direction)
if not game_map.is_colliding(target_state): if not game_map.is_colliding(target_position):
states.append((Action.MOVE, State(target_state, state.direction))) states.append((Action.MOVE, State(target_position, state.direction), game_map.get_cost(target_position)))
return states return states
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple): def build_path(node: Node):
fringe = list()
explored = list()
explored_states = set()
fringe_states = set()
start = State(start.grid_position, start.direction)
fringe.append(Node(start))
fringe_states.add((tuple(start.position), start.direction))
while True:
# No solutions found
if not any(fringe):
return []
node = fringe.pop(0)
fringe_states.remove((tuple(node.state.position), node.state.direction))
# Check goal
if node.state.position == goal:
actions = [node.action] actions = [node.action]
parent = node.parent parent = node.parent
@ -74,15 +62,52 @@ def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
actions.reverse() actions.reverse()
return actions return actions
def heuristic(new_node: Node, goal: tuple[int, int]):
return abs(new_node.state.position[0] - goal[0]) + abs(new_node.state.position[1] - goal[1])
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
fringe = PriorityQueue()
explored = list()
explored_states = set()
fringe_states = set() # Stores positions and directions of states
start = State(start.grid_position, start.direction)
fringe.put((0, Node(start, cost=0)))
fringe_states.add((tuple(start.position), start.direction))
while True:
# No solutions found
if fringe.empty():
return []
node = fringe.get()
node_priority = node[0]
node = node[1]
fringe_states.remove((tuple(node.state.position), node.state.direction))
# Check goal
if node.state.position == goal:
return build_path(node)
explored.append(node) explored.append(node)
explored_states.add((tuple(node.state.position), node.state.direction)) explored_states.add((tuple(node.state.position), node.state.direction))
# Get all possible states # Get all possible states
for state in get_states(node.state, game_map): for state in get_states(node.state, game_map):
sub_state = (tuple(state[1].position), state[1].direction) sub_state = (tuple(state[1].position), state[1].direction)
if sub_state not in fringe_states and sub_state not in explored_states:
new_node = Node(state=state[1], new_node = Node(state=state[1],
parent=node, parent=node,
action=state[0]) action=state[0],
fringe.append(new_node) cost=(state[2] + node.cost))
priority = new_node.cost + heuristic(new_node, goal)
if sub_state not in fringe_states and sub_state not in explored_states:
fringe.put((priority, new_node))
fringe_states.add((tuple(new_node.state.position), new_node.state.direction))
elif sub_state in fringe_states and node.cost > new_node.cost:
fringe.get(node)
fringe.put((priority, new_node))
fringe_states.add((tuple(new_node.state.position), new_node.state.direction)) fringe_states.add((tuple(new_node.state.position), new_node.state.direction))

View File

@ -1,4 +1,3 @@
import pygame
from pygame.rect import Rect from pygame.rect import Rect

View File

@ -21,14 +21,3 @@ class MovementSystem(esper.Processor):
pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed: pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed:
pos.position = [moving.target[0] * 32, moving.target[1] * 32] pos.position = [moving.target[0] * 32, moving.target[1] * 32]
self.world.remove_component(ent, MovingComponent) self.world.remove_component(ent, MovingComponent)
# if moving.direction[0] != 0:
# pos.position[0] += moving.direction[0] * mov.speed * dt / 100
# if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed:
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
# self.world.remove_component(ent, MovingComponent)
# else:
# pos.position[1] += moving.direction[1] * mov.speed * dt / 100
# if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed:
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
# self.world.remove_component(ent, MovingComponent)

View File

@ -5,7 +5,6 @@ from survival.components.moving_component import MovingComponent
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
from survival.enums import Direction from survival.enums import Direction
from survival.graph_search import graph_search, Action from survival.graph_search import graph_search, Action
from survival.pathfinding import breadth_first_search
from survival.systems.input_system import PathfindingComponent from survival.systems.input_system import PathfindingComponent

View File

@ -1,9 +1,6 @@
import random
class Tile: class Tile:
origins = [(0, 0), (32, 0), (64, 0), (96, 0)] def __init__(self, origin: tuple = (0, 0), cost: int = 1, biome=None):
self.origin = origin
def __init__(self, origin=(0, 0)): self.cost = cost
self.origin = random.choice(Tile.origins) self.biome = biome
self.image = None self.image = None

View File

@ -1,3 +1,4 @@
from survival.generators.tile_generator import TileGenerator
from survival.image import Image from survival.image import Image
from survival.tile import Tile from survival.tile import Tile
@ -6,7 +7,8 @@ class TileLayer:
def __init__(self, width, height): def __init__(self, width, height):
self.width = width self.width = width
self.height = height self.height = height
self.tiles = [[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') self.image = Image('atlas.png')
def draw(self, camera, visible_area): def draw(self, camera, visible_area):
@ -18,3 +20,6 @@ class TileLayer:
self.image.pos = (x * 32, y * 32) self.image.pos = (x * 32, y * 32)
self.image.origin = self.tiles[y][x].origin self.image.origin = self.tiles[y][x].origin
camera.draw(self.image) camera.draw(self.image)
def get_cost(self, pos):
return self.tiles[pos[1]][pos[0]].cost