Source code for gale.game

"""
This file contains the implementation of the class Game: the window,
virtual-resolution scaling, and game loop (fixed-ish update/render,
driven by real elapsed time) every gale game is built on top of.

Importing this module calls pygame.init().

Author: Alejandro Mujica (aledrums@gmail.com)
"""

import sys

from typing import Optional, Any, Tuple, Dict

import pygame

from .timer import Timer
from .input_handler import InputHandler, InputListener, InputData, INPUT_EVENTS

pygame.init()


[docs] class Game(InputListener): """ Base class to implemente a game by using pygame. This class handles the window to show the game an a virtual screen with the resolution that you want to emulate. This also handles timer and the game loop. Usage example: class MyGame(Game): def init(self) -> None: # Set your own initial configuration of the game. self.player = Player() self.world = World() def on_input(self, input_id: str, input_data: InputData) -> None: # Make your action when an input is detected. if input_id == "quit" and input_data.pressed: self.quit() def update(self, dt: float) -> None: # Update of all your game elements here. # dt is the elapsed time in secconds. self.world.update(dt) self.player.update(dt) self.player.interact_with(self.world) def render(self, surface: pygame.Surface) -> None: # Render all of your game elements on the virtual # screen surface. self.world.render(surface) self.player.render(surface) game = MyGame(title='Title of my game') game.exec() """ def __init__( self, title: Optional[str] = None, window_width: int = 800, window_height: int = 600, virtual_width: Optional[int] = None, virtual_height: Optional[int] = None, fps: int = 60, *args: Tuple[Any], **kwargs: Dict[str, Any], ) -> None: """ Set the basic elements of the game in their initial values. :param title: Title of the game to show in the window title. By default is None to set the title 'Game'. :param window_width: Width of the window to show the game. By default is 800. :param window_height: Height of the window to show the game. By default is 600. :param virtual_width: Width we're trying to emulate. By default is None to set the same value of window_width. :param virtual_height: Height we're trying to emulate. By default is None to set the same value of window_height. :param fps: Number of frame per seconds. *args and **kwargs Any argument list of keyword arguments that are accepted by pygame.display.set_mode. """ self.window_width: int = window_width self.window_height: int = window_height self.virtual_width: int = virtual_width or self.window_width self.virtual_height: int = virtual_height or self.window_height self.fps = fps # Setting the screen self.screen: pygame.Surface = pygame.display.set_mode( (self.window_width, self.window_height), *args, **kwargs ) self.title: str = title or "Game" pygame.display.set_caption(self.title) # Creating the virtual screen self.render_surface = pygame.Surface((self.virtual_width, self.virtual_height)) self.clock = pygame.time.Clock() self.running: bool = False InputHandler.register_listener(self) self.init()
[docs] def init(self) -> None: """ Empty. This should be implemented by the extension class. """ pass
[docs] def on_input(self, input_id: str, input_data: InputData) -> None: """ Empty. This should be implemented by the extension class. """ pass
[docs] def update(self, dt: float) -> None: """ Empty. This should be implemented by the extension class. :param dt: Time elapsed (in seconds) since the last time this function has been executed. """ pass
[docs] def render(self, surface: pygame.Surface) -> None: """ Empty. This should be implemented by the extension class. :param render_surface: The surface where you should render all of the game elements on. Its dimensions are virtual_width x virtual_height. """ pass
def __update(self, dt: float) -> None: """ Update the timer and call the the method update that you should implement. """ Timer.update(dt) self.update(dt) def __render(self) -> None: """ Prepare screen for render and calls the method render that you should implement. """ self.render_surface.fill((0, 0, 0)) self.render(self.render_surface) self.screen.blit( pygame.transform.scale(self.render_surface, self.screen.get_size()), (0, 0) ) pygame.display.update()
[docs] def exec(self) -> None: """ Execute the game loop. """ self.running = True while self.running: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type in INPUT_EVENTS: InputHandler.handle_input(event) dt = self.clock.tick(self.fps) / 1000.0 self.__update(dt) self.__render() pygame.font.quit() pygame.mixer.quit() pygame.quit()
[docs] def quit(self) -> None: """ Mark the game to exit. """ self.running = False