Source code for rubato.game

"""
The main game class. It controls everything in the game.
The Display is where your game lives at a certain aspect ratio,
The Screen is the actual size of the window which the user interacts with.

Attributes:
    scenes (SceneManager): The global scene manager.
    radio (Radio): The global radio system.
    name (str): The title of the game window.
    fps (int): The target fps of the game.
    reset_display (bool): Controls whether or not the display should reset
        every frame.
    state (STATE): The current state of the game.
"""
import sys
import pygame
from pygame.transform import scale
from typing import TYPE_CHECKING, Tuple
from rubato.classes.sprite import Sprite
from rubato.utils import Display, Vector, Time, Configs, Math
from rubato.classes import SceneManager
from enum import Enum

if TYPE_CHECKING:
    from rubato.radio import Radio


[docs]class STATE(Enum): """ An enum to keep track of the state things RUNNING: will run everything normally STOPPED: will quit the window PAUSED: will pause physics time calls. Please do not use this feature. """ RUNNING = 1 STOPPED = 2 PAUSED = 3
pygame.init() name: str = "" _window_width: int = 0 _window_height: int = 0 resolution: Vector = Vector() fps_cap: int = 0 physics_timestep: int = 0 reset_display: bool = True _use_better_clock: bool = True _physics_count: float = 0 _state = STATE.STOPPED scenes = SceneManager() radio: "Radio" = None _saved_dims = [_window_width, _window_height] _clock = pygame.time.Clock() Time.set_clock(_clock) _max_screen_size: Tuple[int, int] = (0, 0) _screen = None _display = None is_init = False
[docs]def init(options: dict = {}): """ Initializes a game. Should only be called by :meth:`rubato.init`. Args: options: A game config. Defaults to the |default| for `Game`. """ global name, _window_width, _window_height, resolution, fps_cap, \ reset_display, _use_better_clock, _saved_dims, _max_screen_size, \ _screen, _display, is_init is_init = True params = Configs.game_defaults | options name = params["name"] _window_width = params["window_size"].x _window_height = params["window_size"].y resolution = params["resolution"] fps_cap = params["fps_cap"] Time.fdt = params["physics_timestep"] reset_display = params["reset_display"] _use_better_clock = params["better_clock"] _screen = pygame.display.set_mode( (_window_width, _window_height), pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.SCALED) _display = pygame.Surface(resolution.to_tuple(), pygame.SRCALPHA) pygame.display.set_caption(name) if options.get("icon"): pygame.display.set_icon(pygame.image.load(options.get("icon"))) Display.set_display(_display) _saved_dims = [_window_width, _window_height] infos = pygame.display.Info() _max_screen_size = (infos.current_w, infos.current_h)
[docs]def constant_loop(): """ The constant game loop. Should only be called by :meth:`rubato.begin`. """ global _state _state = STATE.RUNNING while True: update()
[docs]def update(): """ The update loop for the game. Called automatically every frame. Handles the game states. Will always process timed calls. """ global _saved_dims, _screen, _physics_count, _display dnd_if_paused = get_state() != STATE.PAUSED # Event handling for event in pygame.event.get(): if event.type == pygame.QUIT: radio.broadcast("EXIT", {}) pygame.quit() sys.exit(1) if event.type == pygame.VIDEORESIZE: global _window_height, _window_width _window_width, _window_height = event.size if event.type == pygame.KEYDOWN: radio.broadcast( "keydown", { "key": pygame.key.name(event.key), "unicode": event.unicode, "code": event.key, "modifiers": event.mod, }, ) if event.type == pygame.KEYUP: radio.broadcast( "keyup", { "key": pygame.key.name(event.key), "unicode": event.unicode, "code": event.key, "modifiers": event.mod, }, ) # Window resize handling if (_saved_dims[0] != _window_width or _saved_dims[1] != _window_height): _screen = pygame.display.set_mode( (_window_width, _window_height), pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.SCALED) aspect_ratio = resolution.x / resolution.y ratio = (_window_width / _window_height) < aspect_ratio width = (_window_height * aspect_ratio, _window_width)[ratio] height = (_window_height, _window_width / aspect_ratio)[ratio] top_left = (((_window_width - width) // 2, 0), (0, (_window_height - height) // 2))[ratio] _saved_dims = [_window_width, _window_height] # Delayed calls handling if dnd_if_paused and get_state(): Time.process_calls() # Fixed Update Loop _physics_count += Time.delta_time() _physics_count = Math.clamp(_physics_count, 0, Time.fdt * 100) while dnd_if_paused and _physics_count > Time.fdt: scenes.fixed_update() _physics_count -= Time.fdt # Regular Update Loop if dnd_if_paused: scenes.update() # Draw Loop if dnd_if_paused: _screen.fill((0, 0, 0)) if reset_display: _display.fill((255, 255, 255)) scenes.draw() # Update Screen _display = Display.global_display frame = pygame.transform.smoothscale(_display, (width, height)) _screen.blit(frame, top_left) pygame.display.flip() radio.events = [] if dnd_if_paused: if _use_better_clock: _clock.tick_busy_loop(fps_cap) else: _clock.tick(fps_cap) else: pygame.time.delay(int(Time.delta_time()))
def render(sprite: Sprite, surface: pygame.Surface): if sprite.z_index <= scenes.current_scene.camera.z_index: width, height = surface.get_size() new_size = ( round(width * scenes.current_scene.camera.zoom), round(height * scenes.current_scene.camera.zoom), ) Display.update( scale(surface, new_size), scenes.current_scene.camera.transform( Sprite.center_to_tl(sprite.pos, Vector(width, height)) * scenes.current_scene.camera.zoom), ) def get_state() -> STATE: return _state def set_state(new_state: STATE): global _state _state = new_state if _state == STATE.STOPPED: pygame.event.post(pygame.event.Event(pygame.QUIT))
[docs]def get_window_width(): """ Returns: The height of the actual window (screen). """ return _window_width
[docs]def set_window_width(window_width: int) -> None: """ Sets width of actual window (screen). Args: window_width: width clamped between the max display size initialized rubato.init() """ global _window_width if _max_screen_size[0] > window_width > 0: _window_width = window_width
[docs]def get_window_height(): """ Returns: The width of the actual window (screen). """ return _window_height
[docs]def set_window_height(window_height: int) -> None: """ Sets height of actual window (screen). Args: window_height: height clamped between the max display size initialized rubato.init() """ global _window_height if _max_screen_size[1] > window_height > 0: _window_height = window_height
[docs]def get_window_size(): """ The current size of the window Returns: Vector: A vector with x representing the width and y representing the height """ return Vector(_window_width, _window_height)
def set_window_size(window_size: Vector): set_window_width(window_size.x) set_window_height(window_size.y)