From a459a56b4e297a587d1e955d322b3a3efbe4b4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 2 Nov 2020 00:33:03 +0100 Subject: [PATCH 1/5] Reformat with Black --- apps/tutorial/quest.py | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index 945d35e..b8d59bc 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -19,10 +19,10 @@ from pyscroll.group import PyscrollGroup # define configuration variables here -RESOURCES_DIR = 'data' +RESOURCES_DIR = "data" HERO_MOVE_SPEED = 200 # pixels per second -MAP_FILENAME = 'grasslands.tmx' +MAP_FILENAME = "grasslands.tmx" # simple wrapper to keep the screen resizeable @@ -42,7 +42,7 @@ def load_image(filename): class Hero(pygame.sprite.Sprite): - """ Our Hero + """Our Hero The Hero has three collision rects, one for the whole sprite "rect" and "old_rect", and another to check collisions with walls, called "feet". @@ -61,12 +61,12 @@ class Hero(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) - self.image = load_image('hero.png').convert_alpha() + self.image = load_image("hero.png").convert_alpha() self.velocity = [0, 0] self._position = [0, 0] self._old_position = self.position self.rect = self.image.get_rect() - self.feet = pygame.Rect(0, 0, self.rect.width * .5, 8) + self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8) @property def position(self): @@ -84,20 +84,20 @@ def update(self, dt): self.feet.midbottom = self.rect.midbottom def move_back(self, dt): - """ If called after an update, the sprite can move back - """ + """If called after an update, the sprite can move back""" self._position = self._old_position self.rect.topleft = self._position self.feet.midbottom = self.rect.midbottom class QuestGame(object): - """ This class is a basic game. + """This class is a basic game. This class will load data, create a pyscroll group, a hero object. It also reads input and moves the Hero around the map. Finally, it uses a pyscroll group to render the map and Hero. """ + filename = get_map(MAP_FILENAME) def __init__(self): @@ -111,15 +111,17 @@ def __init__(self): # setup level geometry with simple pygame rects, loaded from pytmx self.walls = list() for object in tmx_data.objects: - self.walls.append(pygame.Rect( - object.x, object.y, - object.width, object.height)) + self.walls.append( + pygame.Rect(object.x, object.y, object.width, object.height) + ) # create new data source for pyscroll map_data = pyscroll.data.TiledMapData(tmx_data) # create new renderer (camera) - self.map_layer = pyscroll.BufferedRenderer(map_data, screen.get_size(), clamp_camera=False, tall_sprites=1) + self.map_layer = pyscroll.BufferedRenderer( + map_data, screen.get_size(), clamp_camera=False, tall_sprites=1 + ) self.map_layer.zoom = 2 # pyscroll supports layered rendering. our map has 3 'under' layers @@ -147,8 +149,7 @@ def draw(self, surface): self.group.draw(surface) def handle_input(self): - """ Handle pygame input events - """ + """Handle pygame input events""" poll = pygame.event.poll event = poll() @@ -163,10 +164,10 @@ def handle_input(self): break elif event.key == K_EQUALS: - self.map_layer.zoom += .25 + self.map_layer.zoom += 0.25 elif event.key == K_MINUS: - value = self.map_layer.zoom - .25 + value = self.map_layer.zoom - 0.25 if value > 0: self.map_layer.zoom = value @@ -195,8 +196,7 @@ def handle_input(self): self.hero.velocity[0] = 0 def update(self, dt): - """ Tasks that occur over time should be handled here - """ + """Tasks that occur over time should be handled here""" self.group.update(dt) # check if the sprite's feet are colliding with wall @@ -207,17 +207,17 @@ def update(self, dt): sprite.move_back(dt) def run(self): - """ Run the game loop - """ + """Run the game loop""" clock = pygame.time.Clock() self.running = True from collections import deque + times = deque(maxlen=30) try: while self.running: - dt = clock.tick() / 1000. + dt = clock.tick() / 1000.0 times.append(clock.get_fps()) # print(sum(times)/len(times)) @@ -234,7 +234,7 @@ def run(self): pygame.init() pygame.font.init() screen = init_screen(800, 600) - pygame.display.set_caption('Quest - An epic journey.') + pygame.display.set_caption("Quest - An epic journey.") try: game = QuestGame() From 2f15dd671988acddada4fbdc48bae1d8fa025d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 2 Nov 2020 00:35:57 +0100 Subject: [PATCH 2/5] Remove dependency on global state, proper `main()` --- apps/tutorial/quest.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index b8d59bc..2197887 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -100,7 +100,8 @@ class QuestGame(object): filename = get_map(MAP_FILENAME) - def __init__(self): + def __init__(self, screen: pygame.Surface) -> None: + self.screen = screen # true while running self.running = False @@ -140,13 +141,13 @@ def __init__(self): # add our hero to the group self.group.add(self.hero) - def draw(self, surface): + def draw(self) -> None: # center the map/screen on our Hero self.group.center(self.hero.rect.center) # draw the map and all sprites - self.group.draw(surface) + self.group.draw(self.screen) def handle_input(self): """Handle pygame input events""" @@ -173,7 +174,7 @@ def handle_input(self): # this will be handled if the window is resized elif event.type == VIDEORESIZE: - init_screen(event.w, event.h) + self.screen = init_screen(event.w, event.h) self.map_layer.set_size((event.w, event.h)) event = poll() @@ -223,22 +224,27 @@ def run(self): self.handle_input() self.update(dt) - self.draw(screen) + self.draw() pygame.display.flip() except KeyboardInterrupt: self.running = False -if __name__ == "__main__": +def main() -> None: pygame.init() pygame.font.init() screen = init_screen(800, 600) pygame.display.set_caption("Quest - An epic journey.") try: - game = QuestGame() + game = QuestGame(screen) game.run() - except: + except KeyboardInterrupt: + pass + finally: pygame.quit() - raise + + +if __name__ == "__main__": + main() \ No newline at end of file From 81b56430273374d286092682807599c651bb453e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 2 Nov 2020 00:38:36 +0100 Subject: [PATCH 3/5] Replace os.path with pathlib --- apps/tutorial/quest.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index 2197887..9e5940e 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -8,7 +8,7 @@ pip install pytmx """ -import os.path +from pathlib import Path import pygame from pygame.locals import * @@ -19,10 +19,9 @@ from pyscroll.group import PyscrollGroup # define configuration variables here -RESOURCES_DIR = "data" - +CURRENT_DIR = Path(__file__).parent +RESOURCES_DIR = CURRENT_DIR / "data" HERO_MOVE_SPEED = 200 # pixels per second -MAP_FILENAME = "grasslands.tmx" # simple wrapper to keep the screen resizeable @@ -31,14 +30,9 @@ def init_screen(width, height): return screen -# make loading maps a little easier -def get_map(filename): - return os.path.join(RESOURCES_DIR, filename) - - # make loading images a little easier def load_image(filename): - return pygame.image.load(os.path.join(RESOURCES_DIR, filename)) + return pygame.image.load(str(RESOURCES_DIR / filename)) class Hero(pygame.sprite.Sprite): @@ -98,7 +92,7 @@ class QuestGame(object): Finally, it uses a pyscroll group to render the map and Hero. """ - filename = get_map(MAP_FILENAME) + map_path = RESOURCES_DIR / "grasslands.tmx" def __init__(self, screen: pygame.Surface) -> None: self.screen = screen @@ -107,7 +101,7 @@ def __init__(self, screen: pygame.Surface) -> None: self.running = False # load data from pytmx - tmx_data = load_pygame(self.filename) + tmx_data = load_pygame(self.map_path) # setup level geometry with simple pygame rects, loaded from pytmx self.walls = list() From 9ae9b4f223a606844e2365664d1e8c1480f0a1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 2 Nov 2020 00:52:26 +0100 Subject: [PATCH 4/5] Annotate all function signatures --- apps/tutorial/quest.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index 9e5940e..9bfdf7b 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -8,7 +8,10 @@ pip install pytmx """ +from __future__ import annotations + from pathlib import Path +from typing import List import pygame from pygame.locals import * @@ -25,13 +28,13 @@ # simple wrapper to keep the screen resizeable -def init_screen(width, height): +def init_screen(width: int, height: int) -> pygame.Surface: screen = pygame.display.set_mode((width, height), pygame.RESIZABLE) return screen # make loading images a little easier -def load_image(filename): +def load_image(filename: str) -> pygame.Surface: return pygame.image.load(str(RESOURCES_DIR / filename)) @@ -53,38 +56,38 @@ class Hero(pygame.sprite.Sprite): collides with level walls. """ - def __init__(self): - pygame.sprite.Sprite.__init__(self) + def __init__(self) -> None: + super().__init__() self.image = load_image("hero.png").convert_alpha() self.velocity = [0, 0] - self._position = [0, 0] + self._position = [0.0, 0.0] self._old_position = self.position self.rect = self.image.get_rect() self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8) @property - def position(self): + def position(self) -> List[float]: return list(self._position) @position.setter - def position(self, value): + def position(self, value: List[float]) -> None: self._position = list(value) - def update(self, dt): + def update(self, dt: float) -> None: self._old_position = self._position[:] self._position[0] += self.velocity[0] * dt self._position[1] += self.velocity[1] * dt self.rect.topleft = self._position self.feet.midbottom = self.rect.midbottom - def move_back(self, dt): + def move_back(self, dt: float) -> None: """If called after an update, the sprite can move back""" self._position = self._old_position self.rect.topleft = self._position self.feet.midbottom = self.rect.midbottom -class QuestGame(object): +class QuestGame: """This class is a basic game. This class will load data, create a pyscroll group, a hero object. @@ -143,7 +146,7 @@ def draw(self) -> None: # draw the map and all sprites self.group.draw(self.screen) - def handle_input(self): + def handle_input(self) -> None: """Handle pygame input events""" poll = pygame.event.poll From 7fb732f00f885b2fe08cad4dacec7e0b91c17962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 2 Nov 2020 00:55:01 +0100 Subject: [PATCH 5/5] Minor quality-of-life nits --- apps/tutorial/quest.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index 9bfdf7b..1758df4 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -14,7 +14,8 @@ from typing import List import pygame -from pygame.locals import * +from pygame.locals import K_UP, K_DOWN, K_LEFT, K_RIGHT, K_MINUS, K_EQUALS, K_ESCAPE +from pygame.locals import KEYDOWN, VIDEORESIZE, QUIT from pytmx.util_pygame import load_pygame import pyscroll @@ -107,11 +108,9 @@ def __init__(self, screen: pygame.Surface) -> None: tmx_data = load_pygame(self.map_path) # setup level geometry with simple pygame rects, loaded from pytmx - self.walls = list() - for object in tmx_data.objects: - self.walls.append( - pygame.Rect(object.x, object.y, object.width, object.height) - ) + self.walls = [] + for obj in tmx_data.objects: + self.walls.append(pygame.Rect(obj.x, obj.y, obj.width, obj.height)) # create new data source for pyscroll map_data = pyscroll.data.TiledMapData(tmx_data) @@ -217,7 +216,6 @@ def run(self): while self.running: dt = clock.tick() / 1000.0 times.append(clock.get_fps()) - # print(sum(times)/len(times)) self.handle_input() self.update(dt) @@ -244,4 +242,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main()