"""
Module containing main Game class
"""
import sys
from typing import Tuple
import inspect
import pygame
from pyggui.controller import Controller
from pyggui.input import Input
from pyggui.window import Window
from pyggui.configure import pages as configure_pages
from pyggui.configure import asset_builder as configure_asset_builder
[docs]class Game:
"""
Main class for game, holds every game wide property, setting and the main run loop.
"""
def __init__(
self,
display_size: Tuple[int, int] = (720, 360),
page_directory: str = None,
entry_page: str = "_WelcomePage",
assets_directory: str = None,
fps: int = 0,
display: pygame.surface.Surface = None
):
"""
Args:
display_size (Tuple[int, int]): Size of display in px. Defaults to (720, 360).
page_directory (str): Absolute or relative path to directory containing pages.
Defaults to directory of where this object is initialised.
fps (int): Fps constant for game loop.
display (pygame.surface.Surface): Pass your own surface as the main game object display.
"""
pygame.init() # Init Pygame on import time
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): # If running from PyInstaller
# Todo: Implement functionality for running through bundled application
pass
else: # Running from a normal Python process
# Import all modules containing pages
configure_pages.setup(inspect.stack()[1], directory=page_directory)
# Build assets object
self.assets = configure_asset_builder.AssetBuilder(assets_directory).build()
# Pygame initial configuration
if display:
self._display = display
self._display_size = display.get_size()
else:
self._display = pygame.display.set_mode(display_size, pygame.RESIZABLE)
self._display_size = display_size
pygame.display.set_caption("Pygame Window w/pyggui")
self.clock = pygame.time.Clock()
# Attributes
self._fps = fps
self._dt = 0 # Change of time between seconds
self.paused = False # If game is paused
self.entry_page = entry_page
# Objects
self.input = Input(self)
self.controller = Controller(self)
self.window = Window(self)
# Add handler object for screen re-size
self.input.add_event_type_handler(
event_type=pygame.VIDEORESIZE,
handler=self.display_resize_handler
)
@property
def display(self):
return self._display
@property
def display_size(self):
return self._display_size
@property
def dt(self) -> float:
"""
Difference in time (milliseconds) between current frame and previous frame.
"""
return self._dt
@property
def dt_s(self) -> float:
"""
Difference in time (seconds) between current frame and previous frame.
"""
return self._dt * 0.001
@property
def fps(self) -> float:
"""
Current FPS the game is running at.
"""
return round(1000 / self._dt)
@fps.setter
def fps(self, frame_rate: int) -> None:
"""
Cap FPS of game at given integer value.
"""
self._fps = int(frame_rate)
[docs] def display_resize_handler(self, event) -> None:
"""
Handler updates the display and its size once the display window has been re-sized.
"""
self._display_size = (event.w, event.h)
self._display = pygame.display.set_mode(self.display_size, pygame.RESIZABLE)
[docs] def run(self) -> None:
"""
Run main game loop. Will update Window, Input and grab time passed from previous frame.
Loop ends if Input.update returns False i.e. a quit event appeared.
"""
running = True
while running:
self.window.update()
running = self.input.update()
self._dt = self.clock.tick(self._fps)