dev-jakklu #4
@ -1,20 +1,22 @@
|
|||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
from settings import SCREEN_WIDTH, SCREEN_HEIGHT
|
from settings import SCREEN_WIDTH, SCREEN_HEIGHT
|
||||||
|
from survival import esper
|
||||||
from survival.camera import Camera
|
from survival.camera import Camera
|
||||||
|
from survival.components.camera_target_component import CameraTargetComponent
|
||||||
|
from survival.components.input_component import InputComponent
|
||||||
|
from survival.components.movement_component import MovementComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.components.sprite_component import SpriteComponent
|
||||||
from survival.game_map import GameMap
|
from survival.game_map import GameMap
|
||||||
|
from survival.player_generator import PlayerGenerator
|
||||||
|
from survival.resource_generator import ResourceGenerator
|
||||||
def draw_game(delta):
|
from survival.systems.camera_system import CameraSystem
|
||||||
win.fill((0, 0, 0))
|
from survival.systems.collision_system import CollisionSystem
|
||||||
game_map.draw(camera)
|
from survival.systems.draw_system import DrawSystem
|
||||||
pygame.display.update()
|
from survival.systems.input_system import InputSystem
|
||||||
|
from survival.systems.movement_system import MovementSystem
|
||||||
|
from survival.world_generator import WorldGenerator
|
||||||
def update_game(delta, pressed_keys):
|
|
||||||
game_map.update(camera, delta, pressed_keys)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pygame.init()
|
pygame.init()
|
||||||
@ -26,6 +28,12 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
game_map = GameMap(int(SCREEN_WIDTH / 32) * 2, 2 * int(SCREEN_HEIGHT / 32) + 1)
|
game_map = GameMap(int(SCREEN_WIDTH / 32) * 2, 2 * int(SCREEN_HEIGHT / 32) + 1)
|
||||||
camera = Camera(game_map.width * 32, game_map.height * 32, win)
|
camera = Camera(game_map.width * 32, game_map.height * 32, win)
|
||||||
|
|
||||||
|
world = WorldGenerator().create_world(camera, game_map)
|
||||||
|
player = PlayerGenerator().create_player(world, game_map)
|
||||||
|
|
||||||
|
ResourceGenerator(world, game_map).generate_resources()
|
||||||
|
|
||||||
run = True
|
run = True
|
||||||
|
|
||||||
while run:
|
while run:
|
||||||
@ -40,5 +48,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
|
|
||||||
draw_game(ms)
|
win.fill((0, 0, 0))
|
||||||
update_game(ms, keys)
|
game_map.draw(camera)
|
||||||
|
world.process(ms)
|
||||||
|
pygame.display.update()
|
||||||
|
@ -21,8 +21,8 @@ class Camera:
|
|||||||
SCREEN_WIDTH - self.camera.left, SCREEN_HEIGHT - self.camera.top)
|
SCREEN_WIDTH - self.camera.left, SCREEN_HEIGHT - self.camera.top)
|
||||||
|
|
||||||
def update(self, target):
|
def update(self, target):
|
||||||
x = -target.pos[0] + int(SCREEN_WIDTH / 2)
|
x = -target.position[0] + int(SCREEN_WIDTH / 2)
|
||||||
y = -target.pos[1] + int(SCREEN_HEIGHT / 2)
|
y = -target.position[1] + int(SCREEN_HEIGHT / 2)
|
||||||
|
|
||||||
x = min(0, x)
|
x = min(0, x)
|
||||||
y = min(0, y)
|
y = min(0, y)
|
||||||
|
3
survival/components/camera_target_component.py
Normal file
3
survival/components/camera_target_component.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class CameraTargetComponent:
|
||||||
|
def __init__(self, target):
|
||||||
|
self.target = target
|
3
survival/components/collision_component.py
Normal file
3
survival/components/collision_component.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class CollisionComponent:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
3
survival/components/input_component.py
Normal file
3
survival/components/input_component.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class InputComponent:
|
||||||
|
def __init__(self):
|
||||||
|
self.input_data = None
|
4
survival/components/movement_component.py
Normal file
4
survival/components/movement_component.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class MovementComponent:
|
||||||
|
def __init__(self):
|
||||||
|
self.speed = 30
|
||||||
|
self.timer = 0
|
5
survival/components/moving_component.py
Normal file
5
survival/components/moving_component.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class MovingComponent:
|
||||||
|
def __init__(self, direction, target):
|
||||||
|
self.direction = direction
|
||||||
|
self.movement_target = target
|
||||||
|
self.checked_collision = False
|
4
survival/components/position_component.py
Normal file
4
survival/components/position_component.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class PositionComponent:
|
||||||
|
def __init__(self, pos, grid_pos):
|
||||||
|
self.position = pos
|
||||||
|
self.grid_position = grid_pos
|
9
survival/components/sprite_component.py
Normal file
9
survival/components/sprite_component.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from survival.image import Image
|
||||||
|
|
||||||
|
|
||||||
|
class SpriteComponent:
|
||||||
|
def __init__(self, path):
|
||||||
|
self.image = Image(path)
|
||||||
|
|
||||||
|
def set_scale(self, scale):
|
||||||
|
self.image.set_scale(scale)
|
22
survival/entity_layer.py
Normal file
22
survival/entity_layer.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class EntityLayer:
|
||||||
|
def __init__(self, width, height):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.tiles = [[None for x in range(self.width)] for y in range(self.height)]
|
||||||
|
|
||||||
|
def draw(self, camera, visible_area):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_entity(self, entity, pos):
|
||||||
|
self.tiles[pos[1]][pos[0]] = entity
|
||||||
|
|
||||||
|
def move_entity(self, from_pos, to_pos):
|
||||||
|
ent = self.tiles[from_pos[1]][from_pos[0]]
|
||||||
|
self.tiles[from_pos[1]][from_pos[0]] = None
|
||||||
|
self.tiles[to_pos[1]][to_pos[0]] = ent
|
||||||
|
|
||||||
|
def remove_entity(self, pos):
|
||||||
|
self.tiles[pos[1]][pos[0]] = None
|
||||||
|
|
||||||
|
def is_colliding(self, pos):
|
||||||
|
return self.tiles[pos[1]][pos[0]] is not None
|
368
survival/esper.py
Normal file
368
survival/esper.py
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
import time as _time
|
||||||
|
|
||||||
|
from functools import lru_cache as _lru_cache
|
||||||
|
from typing import List as _List
|
||||||
|
from typing import Type as _Type
|
||||||
|
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'
|
||||||
|
|
||||||
|
C = _TypeVar('C')
|
||||||
|
P = _TypeVar('P')
|
||||||
|
|
||||||
|
|
||||||
|
class Processor:
|
||||||
|
"""Base class for all Processors to inherit from.
|
||||||
|
|
||||||
|
Processor instances must contain a `process` method. Other than that,
|
||||||
|
you are free to add any additional methods that are necessary. The process
|
||||||
|
method will be called by each call to `World.process`, so you will
|
||||||
|
generally want to iterate over entities with one (or more) calls to the
|
||||||
|
appropriate world methods there, such as
|
||||||
|
`for ent, (rend, vel) in self.world.get_components(Renderable, Velocity):`
|
||||||
|
"""
|
||||||
|
world = None
|
||||||
|
|
||||||
|
def process(self, *args, **kwargs):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class World:
|
||||||
|
"""A World object keeps track of all Entities, Components, and Processors.
|
||||||
|
|
||||||
|
A World contains a database of all Entity/Component assignments. The World
|
||||||
|
is also responsible for executing all Processors assigned to it for each
|
||||||
|
frame of your game.
|
||||||
|
"""
|
||||||
|
def __init__(self, timed=False):
|
||||||
|
self._processors = []
|
||||||
|
self._next_entity_id = 0
|
||||||
|
self._components = {}
|
||||||
|
self._entities = {}
|
||||||
|
self._dead_entities = set()
|
||||||
|
if timed:
|
||||||
|
self.process_times = {}
|
||||||
|
self._process = self._timed_process
|
||||||
|
|
||||||
|
def clear_cache(self) -> None:
|
||||||
|
self.get_component.cache_clear()
|
||||||
|
self.get_components.cache_clear()
|
||||||
|
|
||||||
|
def clear_database(self) -> None:
|
||||||
|
"""Remove all Entities and Components from the World."""
|
||||||
|
self._next_entity_id = 0
|
||||||
|
self._dead_entities.clear()
|
||||||
|
self._components.clear()
|
||||||
|
self._entities.clear()
|
||||||
|
self.clear_cache()
|
||||||
|
|
||||||
|
def add_processor(self, processor_instance: Processor, priority=0) -> None:
|
||||||
|
"""Add a Processor instance to the World.
|
||||||
|
|
||||||
|
:param processor_instance: An instance of a Processor,
|
||||||
|
subclassed from the Processor class
|
||||||
|
:param priority: A higher number is processed first.
|
||||||
|
"""
|
||||||
|
assert issubclass(processor_instance.__class__, Processor)
|
||||||
|
processor_instance.priority = priority
|
||||||
|
processor_instance.world = self
|
||||||
|
self._processors.append(processor_instance)
|
||||||
|
self._processors.sort(key=lambda proc: proc.priority, reverse=True)
|
||||||
|
|
||||||
|
def remove_processor(self, processor_type: Processor) -> None:
|
||||||
|
"""Remove a Processor from the World, by type.
|
||||||
|
|
||||||
|
:param processor_type: The class type of the Processor to remove.
|
||||||
|
"""
|
||||||
|
for processor in self._processors:
|
||||||
|
if type(processor) == processor_type:
|
||||||
|
processor.world = None
|
||||||
|
self._processors.remove(processor)
|
||||||
|
|
||||||
|
def get_processor(self, processor_type: _Type[P]) -> P:
|
||||||
|
"""Get a Processor instance, by type.
|
||||||
|
|
||||||
|
This method returns a Processor instance by type. This could be
|
||||||
|
useful in certain situations, such as wanting to call a method on a
|
||||||
|
Processor, from within another Processor.
|
||||||
|
|
||||||
|
:param processor_type: The type of the Processor you wish to retrieve.
|
||||||
|
:return: A Processor instance that has previously been added to the World.
|
||||||
|
"""
|
||||||
|
for processor in self._processors:
|
||||||
|
if type(processor) == processor_type:
|
||||||
|
return processor
|
||||||
|
|
||||||
|
def create_entity(self, *components) -> int:
|
||||||
|
"""Create a new Entity.
|
||||||
|
|
||||||
|
This method returns an Entity ID, which is just a plain integer.
|
||||||
|
You can optionally pass one or more Component instances to be
|
||||||
|
assigned to the Entity.
|
||||||
|
|
||||||
|
:param components: Optional components to be assigned to the
|
||||||
|
entity on creation.
|
||||||
|
:return: The next Entity ID in sequence.
|
||||||
|
"""
|
||||||
|
self._next_entity_id += 1
|
||||||
|
|
||||||
|
# TODO: duplicate add_component code here for performance
|
||||||
|
for component in components:
|
||||||
|
self.add_component(self._next_entity_id, component)
|
||||||
|
|
||||||
|
# self.clear_cache()
|
||||||
|
return self._next_entity_id
|
||||||
|
|
||||||
|
def delete_entity(self, entity: int, immediate=False) -> None:
|
||||||
|
"""Delete an Entity from the World.
|
||||||
|
|
||||||
|
Delete an Entity and all of it's assigned Component instances from
|
||||||
|
the world. By default, Entity deletion is delayed until the next call
|
||||||
|
to *World.process*. You can request immediate deletion, however, by
|
||||||
|
passing the "immediate=True" parameter. This should generally not be
|
||||||
|
done during Entity iteration (calls to World.get_component/s).
|
||||||
|
|
||||||
|
Raises a KeyError if the given entity does not exist in the database.
|
||||||
|
:param entity: The Entity ID you wish to delete.
|
||||||
|
:param immediate: If True, delete the Entity immediately.
|
||||||
|
"""
|
||||||
|
if immediate:
|
||||||
|
for component_type in self._entities[entity]:
|
||||||
|
self._components[component_type].discard(entity)
|
||||||
|
|
||||||
|
if not self._components[component_type]:
|
||||||
|
del self._components[component_type]
|
||||||
|
|
||||||
|
del self._entities[entity]
|
||||||
|
self.clear_cache()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._dead_entities.add(entity)
|
||||||
|
|
||||||
|
def entity_exists(self, entity: int) -> bool:
|
||||||
|
"""Check if a specific entity exists.
|
||||||
|
|
||||||
|
Empty entities(with no components) and dead entities(destroyed
|
||||||
|
by delete_entity) will not count as existent ones.
|
||||||
|
:param entity: The Entity ID to check existance for.
|
||||||
|
:return: True if the entity exists, False otherwise.
|
||||||
|
"""
|
||||||
|
return entity in self._entities and entity not in self._dead_entities
|
||||||
|
|
||||||
|
def component_for_entity(self, entity: int, component_type: _Type[C]) -> C:
|
||||||
|
"""Retrieve a Component instance for a specific Entity.
|
||||||
|
|
||||||
|
Retrieve a Component instance for a specific Entity. In some cases,
|
||||||
|
it may be necessary to access a specific Component instance.
|
||||||
|
For example: directly modifying a Component to handle user input.
|
||||||
|
|
||||||
|
Raises a KeyError if the given Entity and Component do not exist.
|
||||||
|
:param entity: The Entity ID to retrieve the Component for.
|
||||||
|
:param component_type: The Component instance you wish to retrieve.
|
||||||
|
:return: The Component instance requested for the given Entity ID.
|
||||||
|
"""
|
||||||
|
return self._entities[entity][component_type]
|
||||||
|
|
||||||
|
def components_for_entity(self, entity: int) -> _Tuple[C, ...]:
|
||||||
|
"""Retrieve all Components for a specific Entity, as a Tuple.
|
||||||
|
|
||||||
|
Retrieve all Components for a specific Entity. The method is probably
|
||||||
|
not appropriate to use in your Processors, but might be useful for
|
||||||
|
saving state, or passing specific Components between World instances.
|
||||||
|
Unlike most other methods, this returns all of the Components as a
|
||||||
|
Tuple in one batch, instead of returning a Generator for iteration.
|
||||||
|
|
||||||
|
Raises a KeyError if the given entity does not exist in the database.
|
||||||
|
:param entity: The Entity ID to retrieve the Components for.
|
||||||
|
:return: A tuple of all Component instances that have been
|
||||||
|
assigned to the passed Entity ID.
|
||||||
|
"""
|
||||||
|
return tuple(self._entities[entity].values())
|
||||||
|
|
||||||
|
def has_component(self, entity: int, component_type: _Any) -> bool:
|
||||||
|
"""Check if a specific Entity has a Component of a certain type.
|
||||||
|
|
||||||
|
:param entity: The Entity you are querying.
|
||||||
|
:param component_type: The type of Component to check for.
|
||||||
|
:return: True if the Entity has a Component of this type,
|
||||||
|
otherwise False
|
||||||
|
"""
|
||||||
|
return component_type in self._entities[entity]
|
||||||
|
|
||||||
|
def has_components(self, entity: int, *component_types: _Any) -> bool:
|
||||||
|
"""Check if an Entity has all of the specified Component types.
|
||||||
|
|
||||||
|
:param entity: The Entity you are querying.
|
||||||
|
:param component_types: Two or more Component types to check for.
|
||||||
|
:return: True if the Entity has all of the Components,
|
||||||
|
otherwise False
|
||||||
|
"""
|
||||||
|
return all(comp_type in self._entities[entity] for comp_type in component_types)
|
||||||
|
|
||||||
|
def add_component(self, entity: int, component_instance: _Any, type_alias: _Type = None) -> None:
|
||||||
|
"""Add a new Component instance to an Entity.
|
||||||
|
|
||||||
|
Add a Component instance to an Entiy. If a Component of the same type
|
||||||
|
is already assigned to the Entity, it will be replaced.
|
||||||
|
|
||||||
|
:param entity: The Entity to associate the Component with.
|
||||||
|
:param component_instance: A Component instance.
|
||||||
|
"""
|
||||||
|
component_type = type_alias or type(component_instance)
|
||||||
|
|
||||||
|
if component_type not in self._components:
|
||||||
|
self._components[component_type] = set()
|
||||||
|
|
||||||
|
self._components[component_type].add(entity)
|
||||||
|
|
||||||
|
if entity not in self._entities:
|
||||||
|
self._entities[entity] = {}
|
||||||
|
|
||||||
|
self._entities[entity][component_type] = component_instance
|
||||||
|
self.clear_cache()
|
||||||
|
|
||||||
|
def remove_component(self, entity: int, component_type: _Any) -> int:
|
||||||
|
"""Remove a Component instance from an Entity, by type.
|
||||||
|
|
||||||
|
A Component instance can be removed by providing it's type.
|
||||||
|
For example: world.delete_component(enemy_a, Velocity) will remove
|
||||||
|
the Velocity instance from the Entity enemy_a.
|
||||||
|
|
||||||
|
Raises a KeyError if either the given entity or Component type does
|
||||||
|
not exist in the database.
|
||||||
|
:param entity: The Entity to remove the Component from.
|
||||||
|
:param component_type: The type of the Component to remove.
|
||||||
|
"""
|
||||||
|
self._components[component_type].discard(entity)
|
||||||
|
|
||||||
|
if not self._components[component_type]:
|
||||||
|
del self._components[component_type]
|
||||||
|
|
||||||
|
del self._entities[entity][component_type]
|
||||||
|
|
||||||
|
if not self._entities[entity]:
|
||||||
|
del self._entities[entity]
|
||||||
|
|
||||||
|
self.clear_cache()
|
||||||
|
return entity
|
||||||
|
|
||||||
|
def _get_component(self, component_type: _Type[C]) -> _Iterable[_Tuple[int, C]]:
|
||||||
|
"""Get an iterator for Entity, Component pairs.
|
||||||
|
|
||||||
|
:param component_type: The Component type to retrieve.
|
||||||
|
:return: An iterator for (Entity, Component) tuples.
|
||||||
|
"""
|
||||||
|
entity_db = self._entities
|
||||||
|
|
||||||
|
for entity in self._components.get(component_type, []):
|
||||||
|
yield entity, entity_db[entity][component_type]
|
||||||
|
|
||||||
|
def _get_components(self, *component_types: _Type) -> _Iterable[_Tuple[int, ...]]:
|
||||||
|
"""Get an iterator for Entity and multiple Component sets.
|
||||||
|
|
||||||
|
:param component_types: Two or more Component types.
|
||||||
|
:return: An iterator for Entity, (Component1, Component2, etc)
|
||||||
|
tuples.
|
||||||
|
"""
|
||||||
|
entity_db = self._entities
|
||||||
|
comp_db = self._components
|
||||||
|
|
||||||
|
try:
|
||||||
|
for entity in set.intersection(*[comp_db[ct] for ct in component_types]):
|
||||||
|
yield entity, [entity_db[entity][ct] for ct in component_types]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@_lru_cache()
|
||||||
|
def get_component(self, component_type: _Type[C]) -> _List[_Tuple[int, C]]:
|
||||||
|
return [query for query in self._get_component(component_type)]
|
||||||
|
|
||||||
|
@_lru_cache()
|
||||||
|
def get_components(self, *component_types: _Type):
|
||||||
|
return [query for query in self._get_components(*component_types)]
|
||||||
|
|
||||||
|
def try_component(self, entity: int, component_type: _Type):
|
||||||
|
"""Try to get a single component type for an Entity.
|
||||||
|
|
||||||
|
This method will return the requested Component if it exists, but
|
||||||
|
will pass silently if it does not. This allows a way to access
|
||||||
|
optional Components that may or may not exist, without having to
|
||||||
|
first querty the Entity to see if it has the Component type.
|
||||||
|
|
||||||
|
:param entity: The Entity ID to retrieve the Component for.
|
||||||
|
:param component_type: The Component instance you wish to retrieve.
|
||||||
|
:return: A iterator containg the single Component instance requested,
|
||||||
|
which is empty if the component doesn't exist.
|
||||||
|
"""
|
||||||
|
if component_type in self._entities[entity]:
|
||||||
|
yield self._entities[entity][component_type]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def try_components(self, entity: int, *component_types: _Type):
|
||||||
|
"""Try to get a multiple component types for an Entity.
|
||||||
|
|
||||||
|
This method will return the requested Components if they exist, but
|
||||||
|
will pass silently if they do not. This allows a way to access
|
||||||
|
optional Components that may or may not exist, without first having
|
||||||
|
to query if the entity has the Component types.
|
||||||
|
|
||||||
|
:param entity: The Entity ID to retrieve the Component for.
|
||||||
|
:param component_types: The Components types you wish to retrieve.
|
||||||
|
:return: A iterator containg the multiple Component instances requested,
|
||||||
|
which is empty if the components do not exist.
|
||||||
|
"""
|
||||||
|
if all(comp_type in self._entities[entity] for comp_type in component_types):
|
||||||
|
yield [self._entities[entity][comp_type] for comp_type in component_types]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _clear_dead_entities(self):
|
||||||
|
"""Finalize deletion of any Entities that are marked dead.
|
||||||
|
|
||||||
|
In the interest of performance, this method duplicates code from the
|
||||||
|
`delete_entity` method. If that method is changed, those changes should
|
||||||
|
be duplicated here as well.
|
||||||
|
"""
|
||||||
|
for entity in self._dead_entities:
|
||||||
|
|
||||||
|
for component_type in self._entities[entity]:
|
||||||
|
self._components[component_type].discard(entity)
|
||||||
|
|
||||||
|
if not self._components[component_type]:
|
||||||
|
del self._components[component_type]
|
||||||
|
|
||||||
|
del self._entities[entity]
|
||||||
|
|
||||||
|
self._dead_entities.clear()
|
||||||
|
self.clear_cache()
|
||||||
|
|
||||||
|
def _process(self, *args, **kwargs):
|
||||||
|
for processor in self._processors:
|
||||||
|
processor.process(*args, **kwargs)
|
||||||
|
|
||||||
|
def _timed_process(self, *args, **kwargs):
|
||||||
|
"""Track Processor execution time for benchmarking."""
|
||||||
|
for processor in self._processors:
|
||||||
|
start_time = _time.process_time()
|
||||||
|
processor.process(*args, **kwargs)
|
||||||
|
process_time = int(round((_time.process_time() - start_time) * 1000, 2))
|
||||||
|
self.process_times[processor.__class__.__name__] = process_time
|
||||||
|
|
||||||
|
def process(self, *args, **kwargs):
|
||||||
|
"""Call the process method on all Processors, in order of their priority.
|
||||||
|
|
||||||
|
Call the *process* method on all assigned Processors, respecting their
|
||||||
|
optional priority setting. In addition, any Entities that were marked
|
||||||
|
for deletion since the last call to *World.process*, will be deleted
|
||||||
|
at the start of this method call.
|
||||||
|
|
||||||
|
:param args: Optional arguments that will be passed through to the
|
||||||
|
*process* method of all Processors.
|
||||||
|
"""
|
||||||
|
self._clear_dead_entities()
|
||||||
|
self._process(*args, **kwargs)
|
@ -1,3 +1,5 @@
|
|||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.entity_layer import EntityLayer
|
||||||
from survival.player import Player
|
from survival.player import Player
|
||||||
from survival.tile_layer import TileLayer
|
from survival.tile_layer import TileLayer
|
||||||
|
|
||||||
@ -6,16 +8,21 @@ class GameMap:
|
|||||||
def __init__(self, width, height):
|
def __init__(self, width, height):
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.player = Player()
|
self.tile_layer = TileLayer(width, height)
|
||||||
self.layers = []
|
self.entity_layer = EntityLayer(width, height)
|
||||||
self.layers.append(TileLayer(width, height))
|
|
||||||
|
|
||||||
def draw(self, camera):
|
def draw(self, camera):
|
||||||
visible_area = camera.get_visible_area()
|
visible_area = camera.get_visible_area()
|
||||||
for layer in self.layers:
|
self.tile_layer.draw(camera, visible_area)
|
||||||
layer.draw(camera, visible_area)
|
|
||||||
self.player.draw(camera)
|
|
||||||
|
|
||||||
def update(self, camera, delta, pressed_keys):
|
def add_entity(self, entity, pos):
|
||||||
self.player.update(delta, pressed_keys)
|
self.entity_layer.add_entity(entity, pos.grid_position)
|
||||||
camera.update(self.player)
|
|
||||||
|
def move_entity(self, from_pos, to_pos):
|
||||||
|
self.entity_layer.move_entity(from_pos, to_pos)
|
||||||
|
|
||||||
|
def remove_entity(self, pos):
|
||||||
|
self.entity_layer.remove_entity(pos)
|
||||||
|
|
||||||
|
def is_colliding(self, pos):
|
||||||
|
return self.entity_layer.is_colliding(pos)
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import pygame
|
|
||||||
from pygame.rect import Rect
|
|
||||||
|
|
||||||
|
|
||||||
class GameObject:
|
|
||||||
|
|
||||||
def __init__(self, pos, texture):
|
|
||||||
self.pos = pos
|
|
||||||
self.last_pos = pos
|
|
||||||
self.texture = pygame.image.load(texture)
|
|
||||||
self.texture = pygame.transform.scale(self.texture, (64, 64))
|
|
||||||
self.width = self.texture.get_width()
|
|
||||||
self.height = self.texture.get_height()
|
|
||||||
self.velocity = [0, 0]
|
|
||||||
|
|
||||||
def draw(self, window):
|
|
||||||
window.blit(self.texture, self.pos)
|
|
||||||
|
|
||||||
def get_rect(self):
|
|
||||||
return Rect(self.pos[0], self.pos[1], self.width, self.height)
|
|
@ -7,25 +7,17 @@ from survival.image import Image
|
|||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pos = [0, 0]
|
# self.pos = [1024, 512]
|
||||||
self.velocity = [0, 0]
|
# self.velocity = [0, 0]
|
||||||
self.image = Image('stevenson.png')
|
# self.image = Image('stevenson.png')
|
||||||
self.image.set_scale(2)
|
# self.image.set_scale(2)
|
||||||
self.origin = (0, 0)
|
# self.speed = 30
|
||||||
self.speed = 30
|
# self.movement_target = [self.pos[0], self.pos[1]]
|
||||||
self.movement_target = [self.pos[0], self.pos[1]]
|
# self.timer = 0
|
||||||
self.timer = 0
|
pass
|
||||||
|
|
||||||
def draw(self, camera):
|
def draw(self, camera):
|
||||||
if self.is_moving():
|
|
||||||
if self.velocity[0] == 1:
|
|
||||||
self.image.origin = (96, 0)
|
|
||||||
elif self.velocity[0] == -1:
|
|
||||||
self.image.origin = (64, 0)
|
|
||||||
elif self.velocity[1] == 1:
|
|
||||||
self.image.origin = (0, 0)
|
|
||||||
else:
|
|
||||||
self.image.origin = (32, 0)
|
|
||||||
self.image.pos = self.pos
|
self.image.pos = self.pos
|
||||||
camera.draw(self.image)
|
camera.draw(self.image)
|
||||||
|
|
||||||
@ -59,7 +51,7 @@ class Player:
|
|||||||
self.timer += delta
|
self.timer += delta
|
||||||
|
|
||||||
if self.timer > 1000:
|
if self.timer > 1000:
|
||||||
#self.move_in_random_direction()
|
self.move_in_random_direction()
|
||||||
self.timer = 0
|
self.timer = 0
|
||||||
|
|
||||||
if pressed_keys[pygame.K_LEFT]:
|
if pressed_keys[pygame.K_LEFT]:
|
||||||
|
23
survival/player_generator.py
Normal file
23
survival/player_generator.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from survival.components.camera_target_component import CameraTargetComponent
|
||||||
|
from survival.components.input_component import InputComponent
|
||||||
|
from survival.components.movement_component import MovementComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.components.sprite_component import SpriteComponent
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerGenerator:
|
||||||
|
|
||||||
|
def create_player(self, world, game_map):
|
||||||
|
player = world.create_entity()
|
||||||
|
pos = PositionComponent([0, 0], [0, 0])
|
||||||
|
world.add_component(player, pos)
|
||||||
|
world.add_component(player, MovementComponent())
|
||||||
|
world.add_component(player, InputComponent())
|
||||||
|
camera_target = CameraTargetComponent(pos)
|
||||||
|
world.add_component(player, camera_target)
|
||||||
|
game_map.add_entity(player, pos)
|
||||||
|
sprite = SpriteComponent('stevenson.png')
|
||||||
|
sprite.set_scale(1)
|
||||||
|
world.add_component(player, sprite)
|
||||||
|
|
||||||
|
return player
|
31
survival/resource_generator.py
Normal file
31
survival/resource_generator.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.components.sprite_component import SpriteComponent
|
||||||
|
from survival.settings import RESOURCES_AMOUNT
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceGenerator:
|
||||||
|
def __init__(self, world, game_map):
|
||||||
|
self.world = world
|
||||||
|
self.map = game_map
|
||||||
|
|
||||||
|
def generate_resources(self):
|
||||||
|
for x in range(RESOURCES_AMOUNT):
|
||||||
|
obj = self.world.create_entity()
|
||||||
|
sprites = ['apple.png', 'water.png', 'wood.png', 'stone.png']
|
||||||
|
|
||||||
|
empty_grid_pos = self.get_empty_grid_position()
|
||||||
|
empty_pos = [empty_grid_pos[0] * 32, empty_grid_pos[1] * 32]
|
||||||
|
|
||||||
|
pos = PositionComponent(empty_pos, empty_grid_pos)
|
||||||
|
sprite = SpriteComponent(random.choice(sprites))
|
||||||
|
self.world.add_component(obj, pos)
|
||||||
|
self.world.add_component(obj, sprite)
|
||||||
|
self.map.add_entity(obj, pos)
|
||||||
|
|
||||||
|
def get_empty_grid_position(self):
|
||||||
|
free_pos = [random.randrange(self.map.width), random.randrange(self.map.height)]
|
||||||
|
while self.map.is_colliding(free_pos):
|
||||||
|
free_pos = [random.randrange(self.map.width), random.randrange(self.map.height)]
|
||||||
|
return free_pos
|
@ -1,2 +1,3 @@
|
|||||||
SCREEN_WIDTH = 1920
|
SCREEN_WIDTH = 1920
|
||||||
SCREEN_HEIGHT = 1080
|
SCREEN_HEIGHT = 1080
|
||||||
|
RESOURCES_AMOUNT = 300
|
||||||
|
12
survival/systems/camera_system.py
Normal file
12
survival/systems/camera_system.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from survival import esper
|
||||||
|
from survival.components.camera_target_component import CameraTargetComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
|
||||||
|
|
||||||
|
class CameraSystem(esper.Processor):
|
||||||
|
def __init__(self, camera):
|
||||||
|
self.camera = camera
|
||||||
|
|
||||||
|
def process(self, dt):
|
||||||
|
for ent, (camera_target, pos) in self.world.get_components(CameraTargetComponent, PositionComponent):
|
||||||
|
self.camera.update(pos)
|
25
survival/systems/collision_system.py
Normal file
25
survival/systems/collision_system.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from survival import esper
|
||||||
|
from survival.components.moving_component import MovingComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
|
||||||
|
|
||||||
|
class CollisionSystem(esper.Processor):
|
||||||
|
def __init__(self, game_map):
|
||||||
|
self.map = game_map
|
||||||
|
|
||||||
|
def process(self, dt):
|
||||||
|
for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent):
|
||||||
|
if moving.checked_collision:
|
||||||
|
continue
|
||||||
|
|
||||||
|
moving.checked_collision = True
|
||||||
|
|
||||||
|
if self.check_collision(moving.movement_target):
|
||||||
|
self.world.remove_component(ent, MovingComponent)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.map.move_entity(pos.grid_position, moving.movement_target)
|
||||||
|
pos.grid_position = moving.movement_target
|
||||||
|
|
||||||
|
def check_collision(self, pos):
|
||||||
|
return self.map.is_colliding(pos)
|
13
survival/systems/draw_system.py
Normal file
13
survival/systems/draw_system.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from survival import esper
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.components.sprite_component import SpriteComponent
|
||||||
|
|
||||||
|
|
||||||
|
class DrawSystem(esper.Processor):
|
||||||
|
def __init__(self, camera):
|
||||||
|
self.camera = camera
|
||||||
|
|
||||||
|
def process(self, dt):
|
||||||
|
for ent, (sprite, pos) in self.world.get_components(SpriteComponent, PositionComponent):
|
||||||
|
sprite.image.pos = pos.position
|
||||||
|
self.camera.draw(sprite.image)
|
26
survival/systems/input_system.py
Normal file
26
survival/systems/input_system.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
from survival import esper
|
||||||
|
from survival.components.input_component import InputComponent
|
||||||
|
from survival.components.moving_component import MovingComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
|
||||||
|
|
||||||
|
class InputSystem(esper.Processor):
|
||||||
|
def __init__(self):
|
||||||
|
self.map = None
|
||||||
|
|
||||||
|
def process(self, dt):
|
||||||
|
for ent, (inp, pos) in self.world.get_components(InputComponent, PositionComponent):
|
||||||
|
keys = pygame.key.get_pressed()
|
||||||
|
|
||||||
|
if self.world.has_component(ent, MovingComponent):
|
||||||
|
continue
|
||||||
|
if keys[pygame.K_LEFT]:
|
||||||
|
self.world.add_component(ent, MovingComponent([-1, 0], [pos.grid_position[0] - 1, pos.grid_position[1]]))
|
||||||
|
elif keys[pygame.K_RIGHT]:
|
||||||
|
self.world.add_component(ent, MovingComponent([1, 0], [pos.grid_position[0] + 1, pos.grid_position[1]]))
|
||||||
|
elif keys[pygame.K_DOWN]:
|
||||||
|
self.world.add_component(ent, MovingComponent([0, 1], [pos.grid_position[0], pos.grid_position[1] + 1]))
|
||||||
|
elif keys[pygame.K_UP]:
|
||||||
|
self.world.add_component(ent, MovingComponent([0, -1], [pos.grid_position[0], pos.grid_position[1] - 1]))
|
35
survival/systems/movement_system.py
Normal file
35
survival/systems/movement_system.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from survival import esper
|
||||||
|
from survival.components.movement_component import MovementComponent
|
||||||
|
from survival.components.moving_component import MovingComponent
|
||||||
|
from survival.components.position_component import PositionComponent
|
||||||
|
from survival.components.sprite_component import SpriteComponent
|
||||||
|
|
||||||
|
|
||||||
|
class MovementSystem(esper.Processor):
|
||||||
|
def __init__(self):
|
||||||
|
self.map = None
|
||||||
|
|
||||||
|
def process(self, dt):
|
||||||
|
for ent, (mov, pos, moving, sprite) in self.world.get_components(MovementComponent, PositionComponent,
|
||||||
|
MovingComponent,
|
||||||
|
SpriteComponent):
|
||||||
|
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)
|
||||||
|
|
||||||
|
if moving.direction[0] == 1:
|
||||||
|
sprite.image.origin = (96, 0)
|
||||||
|
elif moving.direction[0] == -1:
|
||||||
|
sprite.image.origin = (64, 0)
|
||||||
|
elif moving.direction[1] == 1:
|
||||||
|
sprite.image.origin = (0, 0)
|
||||||
|
else:
|
||||||
|
sprite.image.origin = (32, 0)
|
||||||
|
|
@ -10,8 +10,11 @@ class TileLayer:
|
|||||||
self.image = Image('atlas.png')
|
self.image = Image('atlas.png')
|
||||||
|
|
||||||
def draw(self, camera, visible_area):
|
def draw(self, camera, visible_area):
|
||||||
for y in range(int(visible_area.top/32), int(visible_area.height/32) + 1):
|
for y in range(int(visible_area.top / 32), int(visible_area.height / 32) + 1):
|
||||||
for x in range(int(visible_area.left/32), int(visible_area.width/32) + 1):
|
for x in range(int(visible_area.left / 32), int(visible_area.width / 32) + 1):
|
||||||
|
if y >= self.height or x >= self.width:
|
||||||
|
continue
|
||||||
|
|
||||||
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)
|
||||||
|
19
survival/world_generator.py
Normal file
19
survival/world_generator.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from survival import esper
|
||||||
|
from survival.systems.camera_system import CameraSystem
|
||||||
|
from survival.systems.collision_system import CollisionSystem
|
||||||
|
from survival.systems.draw_system import DrawSystem
|
||||||
|
from survival.systems.input_system import InputSystem
|
||||||
|
from survival.systems.movement_system import MovementSystem
|
||||||
|
|
||||||
|
|
||||||
|
class WorldGenerator:
|
||||||
|
|
||||||
|
def create_world(self, camera, game_map):
|
||||||
|
world = esper.World()
|
||||||
|
world.add_processor(InputSystem())
|
||||||
|
world.add_processor(CameraSystem(camera))
|
||||||
|
world.add_processor(MovementSystem(), priority=1)
|
||||||
|
world.add_processor(CollisionSystem(game_map), priority=2)
|
||||||
|
world.add_processor(DrawSystem(camera))
|
||||||
|
|
||||||
|
return world
|
Loading…
Reference in New Issue
Block a user