From 639ed421439c4adcd0a5a2be26d0570eb514672e Mon Sep 17 00:00:00 2001 From: papush! Date: Sat, 23 Nov 2019 10:46:25 +0100 Subject: [PATCH] archivage initial --- .gitignore | 2 + engine/components/__init__.py | 0 engine/components/collide_rect.py | 7 +++ engine/components/component.py | 14 ++++++ engine/components/script.py | 4 ++ engine/components/sprite.py | 16 +++++++ engine/entity.py | 18 +++++++ engine/game.py | 29 +++++++++++ engine/resources.py | 5 ++ engine/scene.py | 11 +++++ engine/scene_manager.py | 18 +++++++ engine/servers/__init__.py | 0 engine/servers/graphics.py | 23 +++++++++ engine/servers/input.py | 17 +++++++ engine/servers/physics.py | 10 ++++ engine/servers/script.py | 10 ++++ engine/servers/server.py | 20 ++++++++ engine/servers/sound.py | 9 ++++ engine/singleton.py | 23 +++++++++ engine/vec2.py | 4 ++ pac-man.py | 7 +++ res/lvl0.png | Bin 0 -> 12025 bytes scenes/lvl0.py | 77 ++++++++++++++++++++++++++++++ scenes/main_menu.py | 4 ++ 24 files changed, 328 insertions(+) create mode 100644 .gitignore create mode 100644 engine/components/__init__.py create mode 100644 engine/components/collide_rect.py create mode 100644 engine/components/component.py create mode 100644 engine/components/script.py create mode 100644 engine/components/sprite.py create mode 100644 engine/entity.py create mode 100644 engine/game.py create mode 100644 engine/resources.py create mode 100644 engine/scene.py create mode 100644 engine/scene_manager.py create mode 100644 engine/servers/__init__.py create mode 100644 engine/servers/graphics.py create mode 100644 engine/servers/input.py create mode 100644 engine/servers/physics.py create mode 100644 engine/servers/script.py create mode 100644 engine/servers/server.py create mode 100644 engine/servers/sound.py create mode 100644 engine/singleton.py create mode 100644 engine/vec2.py create mode 100755 pac-man.py create mode 100644 res/lvl0.png create mode 100644 scenes/lvl0.py create mode 100644 scenes/main_menu.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/engine/components/__init__.py b/engine/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/engine/components/collide_rect.py b/engine/components/collide_rect.py new file mode 100644 index 0000000..bdc118e --- /dev/null +++ b/engine/components/collide_rect.py @@ -0,0 +1,7 @@ +from .component import Component + + +class CollideRect(Component): + def __init__(self, rect): + super().__init__() + self.rect = rect diff --git a/engine/components/component.py b/engine/components/component.py new file mode 100644 index 0000000..fb3c5e6 --- /dev/null +++ b/engine/components/component.py @@ -0,0 +1,14 @@ +from abc import abstractmethod + + +class Component: + def __init__(self): + self.parent = None + + @abstractmethod + def register(self): + pass + + @abstractmethod + def unregister(self): + pass diff --git a/engine/components/script.py b/engine/components/script.py new file mode 100644 index 0000000..431a5b6 --- /dev/null +++ b/engine/components/script.py @@ -0,0 +1,4 @@ +class Script: + def __init__(self, parent, func): + super().__init__(parent) + self.func = func diff --git a/engine/components/sprite.py b/engine/components/sprite.py new file mode 100644 index 0000000..c506a66 --- /dev/null +++ b/engine/components/sprite.py @@ -0,0 +1,16 @@ +from .component import Component +from ..servers.graphics import GraphicsServer + + +class Sprite(Component): + def __init__(self, surf, z): + super().__init__() + self.surf = surf + self.width = self.surf.get_width() + self.z = z + + def register(self): + GraphicsServer().register_component(self) + + def unregister(self): + GraphicsServer().register_component(self) diff --git a/engine/entity.py b/engine/entity.py new file mode 100644 index 0000000..8e0571e --- /dev/null +++ b/engine/entity.py @@ -0,0 +1,18 @@ +class Entity: + def __init__(self): + self._components = [] + self.x = 0 + self.y = 0 + self.script = None + + def add(self, component): + self._components.append(component) + component.parent = self + + def register(self): + for component in self._components: + component.register() + + def unregister(self): + for component in self._components: + component.unregister() diff --git a/engine/game.py b/engine/game.py new file mode 100644 index 0000000..da21da4 --- /dev/null +++ b/engine/game.py @@ -0,0 +1,29 @@ +from pygame.time import Clock + +from .servers.graphics import GraphicsServer +from .servers.sound import SoundServer +from .servers.physics import PhysicsServer +from .servers.input import InputServer, StopException +from .scene_manager import SceneManager + + +class Game: + def __init__(self, start_scene): + self._graphics_server = GraphicsServer() + self._sound_server = SoundServer() + self._physics_server = PhysicsServer() + self._input_server = InputServer() + self._scene_manager = SceneManager(start_scene) + + def run(self): + clock = Clock() + while True: + try: + self._input_server.step() + except StopException: + break + self._scene_manager.step() + self._physics_server.step() + self._sound_server.step() + self._graphics_server.step() + clock.tick(60) diff --git a/engine/resources.py b/engine/resources.py new file mode 100644 index 0000000..db82627 --- /dev/null +++ b/engine/resources.py @@ -0,0 +1,5 @@ +from os.path import join + + +def res(filename): + return join('res', filename) diff --git a/engine/scene.py b/engine/scene.py new file mode 100644 index 0000000..7f2bbe2 --- /dev/null +++ b/engine/scene.py @@ -0,0 +1,11 @@ +class Scene: + def __init__(self): + self.entities = [] + + def unload(self): + for entity in self.entities: + entity.unregister() + + def load(self): + for entity in self.entities: + entity.register() diff --git a/engine/scene_manager.py b/engine/scene_manager.py new file mode 100644 index 0000000..9d6e339 --- /dev/null +++ b/engine/scene_manager.py @@ -0,0 +1,18 @@ +from .singleton import Singleton + + +class SceneManager(metaclass=Singleton): + def __init__(self, scene): + super().__init__() + self._scene = scene + self._scene.load() + + def change_scene(self, new_scene): + self._scene.unload() + self._scene = new_scene + self._scene.load() + + def step(self): + for entity in self._scene.entities: + if entity.script is not None: + entity.script(entity) diff --git a/engine/servers/__init__.py b/engine/servers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/engine/servers/graphics.py b/engine/servers/graphics.py new file mode 100644 index 0000000..6d0af28 --- /dev/null +++ b/engine/servers/graphics.py @@ -0,0 +1,23 @@ +from .server import Server + +from pygame import display + + +class GraphicsServer(Server): + def __init__(self, size=(640, 480)): + super().__init__() + display.init() + self.window = display.set_mode(size) + + def register_component(self, component): + super().register_component(component) + self._components.sort(key=lambda e: e.z) + + def step(self): + for component in self._components: + self.window.blit(component.surf, + (component.parent.x, component.parent.y)) + display.flip() + + def resize(self, new_size): + self.window = display.set_mode(new_size) diff --git a/engine/servers/input.py b/engine/servers/input.py new file mode 100644 index 0000000..116df35 --- /dev/null +++ b/engine/servers/input.py @@ -0,0 +1,17 @@ +from .server import Server + +from pygame import event +from pygame.locals import QUIT + + +class StopException(RuntimeError): pass + + +class InputServer(Server): + def __init__(self): + super().__init__() + + def step(self): + for e in event.get(): + if e.type == QUIT: + raise StopException diff --git a/engine/servers/physics.py b/engine/servers/physics.py new file mode 100644 index 0000000..0f1f234 --- /dev/null +++ b/engine/servers/physics.py @@ -0,0 +1,10 @@ +from .server import Server + + +class PhysicsServer(Server): + def __init__(self): + super().__init__() + + def step(self): + for component in self._components: + pass diff --git a/engine/servers/script.py b/engine/servers/script.py new file mode 100644 index 0000000..81f7b8b --- /dev/null +++ b/engine/servers/script.py @@ -0,0 +1,10 @@ +from .server import Server + + +class ScriptServer(Server): + def __init__(self): + super().__init__() + + def step(self): + for component in self._components: + component.func(component.parent) diff --git a/engine/servers/server.py b/engine/servers/server.py new file mode 100644 index 0000000..ed0b856 --- /dev/null +++ b/engine/servers/server.py @@ -0,0 +1,20 @@ +from abc import abstractmethod + +from ..singleton import Singleton + + +class Server(metaclass=Singleton): + def __init__(self): + super().__init__() + self._components = [] + + def register_component(self, component): + self._components.append(component) + + def unregister_component(self, component): + self._components.remove(component) + + + @abstractmethod + def step(self): + pass diff --git a/engine/servers/sound.py b/engine/servers/sound.py new file mode 100644 index 0000000..7537730 --- /dev/null +++ b/engine/servers/sound.py @@ -0,0 +1,9 @@ +from .server import Server + + +class SoundServer(Server): + def __init__(self): + super().__init__() + + def step(self): + pass diff --git a/engine/singleton.py b/engine/singleton.py new file mode 100644 index 0000000..53b0b87 --- /dev/null +++ b/engine/singleton.py @@ -0,0 +1,23 @@ +class Singleton(type): + def __init__(cls, name, bases, dict): + super(Singleton, cls).__init__(name, bases, dict) + cls.instance = None + + def __call__(cls,*args,**kw): + if cls.instance is None: + cls.instance = super(Singleton, cls).\ + __call__(*args, **kw) + return cls.instance + + +# class Singleton(): +# instance = None + +# def __init__(self): +# self.__class__.instance = self + +# def get_instance(): +# if self.__class__.instance == None: +# raise RuntimeError('Server has not been initialized.') +# else: +# return self.__class__.instance diff --git a/engine/vec2.py b/engine/vec2.py new file mode 100644 index 0000000..fb62a6b --- /dev/null +++ b/engine/vec2.py @@ -0,0 +1,4 @@ +class Vec2: + def __init__(self, x=0, y=0): + self.x = x + self.y = y diff --git a/pac-man.py b/pac-man.py new file mode 100755 index 0000000..5e93b5f --- /dev/null +++ b/pac-man.py @@ -0,0 +1,7 @@ +#!/bin/python3 + +from engine.game import Game +from scenes.lvl0 import scene + + +Game(scene).run() diff --git a/res/lvl0.png b/res/lvl0.png new file mode 100644 index 0000000000000000000000000000000000000000..503e9b941b93768c767e76ca646138a8aa94dbea GIT binary patch literal 12025 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