engine.py

LunaEngine Main Engine - Core Game Loop and Management System

LOCATION: lunaengine/core/engine.py

DESCRIPTION:
The central engine class that orchestrates the entire game lifecycle. Manages
scene transitions, rendering pipeline, event handling, performance monitoring,
and UI system integration. This is the primary interface for game developers.

KEY RESPONSIBILITIES:
- Game loop execution with fixed timestep
- Scene management and lifecycle control
- Event distribution to scenes and UI elements
- Performance monitoring and optimization
- Theme management across the entire application
- Resource initialization and cleanup
- Atlas-based resource catalog for asset management
- Resource bundle loading (optional, with progress bar)

LIBRARIES USED:
- pygame: Window management, event handling, timing, and surface operations
- numpy: Mathematical operations for game calculations
- threading: Background task management for bundle loading
- typing: Type hints for better code documentation

DEPENDENCIES:
- ..backend.pygame_backend: Default rendering backend
- ..ui.elements: UI component system
- ..utils.performance: Performance monitoring utilities
- .scene: Scene management base class
- ..misc.atlas: Resource catalog system

class LunaEngine

Description

Main game engine class for LunaEngine.

This class manages the entire game lifecycle including initialization,
scene management, event handling, rendering, and shutdown.

Attributes:
   title (str): Window title
   width (int): Window width
   height (int): Window height
   fullscreen (bool): Whether to start in fullscreen mode
   running (bool): Whether the engine is running
   clock (pygame.time.Clock): Game clock for FPS control
   scenes (Dict[str, Scene]): Registered scenes
   current_scene (Scene): Currently active scene
   atlas (Atlas): Resource catalog for all assets
   bundle_path (Optional[Path]): Path to resource bundle (.res) if any

Attributes
updateRatio: Any = update_ratio
getLogoSurface: Any = get_logo_surface
setTitle: Any = set_title
setIcon: Any = set_icon
addScene: Any = add_scene
setScene: Any = set_scene
getFpsStats: Any = get_fps_stats
getHardwareInfo: Any = get_hardware_info
getControllers: Any = get_controllers
getController: Any = get_controller
Methods
def __init__(self: Any, title: str = 'LunaEngine Game', width: int = 800, height: int = 600, fullscreen: bool = False, icon: Optional[Union[str, Path, pygame.Surface, Icon, AtlasItem, Callable[[], Icon], None]] = None, **kwargs: dict) -> Any
Initialize the LunaEngine.

Args:
   title (str): The title of the game window (default: "LunaEngine Game")
   width (int): The width of the game window (default: 800)
   height (int): The height of the game window (default: 600)
   fullscreen (bool): Start in fullscreen mode (default: False)
   icon (str|pygame.Surface): Window icon
   **kwargs: Additional options:
       - show_splash (bool): Show splash screen (default: True)
       - splash_logo (pygame.Surface): Custom logo surface
       - splash_logo_size (int): Logo size for splash (default: 64)
       - debug (bool): Enable debug mode (default: False)
       - enable_performance_profiling (bool): Enable profiling (default: True)
       - atlas_root (str|Path): Root path for the atlas (default: current directory)
       - bundle_path (str|Path): Path to resource bundle (.res file) (default: auto-detect "game.res")
       - bundle_key (str): Obfuscation key for the bundle (if encrypted)
def _auto_discover_assets(self: Any) -> None
Scan the atlas root for common asset folders and add them automatically.
def ratio(self: Any) -> Ratio
No documentation
def ratio(self: Any, value: Ratio) -> Any
No documentation
def update_ratio(self: Any, base_width: int | float, base_height: int | float) -> Any
Update the aspect ratio scaling factor.
def initialize(self: Any) -> Any
Initialize the engine and create the game window.
def get_logo_surface(self: Any) -> Optional[pygame.Surface]
Return the engine logo surface if available.
def _show_splash(self: Any) -> Any
Display an animated splash screen with logo, title, subtitle, version, and progress bar.
def set_title(self: Any, title: str) -> Any
Set the window title.
def set_icon(self: Any, icon: Optional[Union[str, Path, pygame.Surface, Icon, AtlasItem, Callable[[], Icon], None]]) -> Any
Set the window icon.

Supports:
   - file path (str or Path)
   - pygame.Surface
   - Icon (from icons.py)
   - AtlasItem (from the resource atlas)
   - callable returning an Icon (e.g. Icons.BRAIN)

The icon is automatically scaled to 64Ă—64 for best window compatibility.
def update_camera_renderer(self: Any) -> Any
Update all scene cameras with the current renderer.
def get_focusable_elements(self: Any) -> List[ui.UIElement]
Return all focusable UI elements that are globally visible and enabled.
def move_focus(self: Any, direction: str) -> bool
Move focus to the next/previous element in the focusable list.
Direction: 'up', 'down', 'left', 'right' (uses spatial logic) or 'next', 'prev'.
def move_focus_vector(self: Any, dx: float, dy: float, _elements: Any = None) -> bool
Focus the nearest visible element in an arbitrary screen-space direction.
def handle_controller_vector(self: Any, dx: float, dy: float) -> bool
Dispatch an arbitrary analog-stick vector to the focused widget.
def _ensure_focus_visible(self: Any, element: Optional['ui.UIElement']) -> None
Scroll every ancestor until a newly focused element is visible.
def update_controller_ui_navigation(self: Any, dt: float) -> Any
Handle all controller-based UI navigation: focus, scrolling, tab switching, and activation.
def _find_tabination_ancestor(self: Any, element: 'ui.UIElement') -> Optional['ui.Tabination']
Traverse parents to find the nearest Tabination container.
def _find_scrollable_ancestor(self: Any, element: 'ui.UIElement') -> Optional['ui.ScrollingFrame']
Traverse parents to find the nearest ScrollingFrame.
def activate_focused_element(self: Any) -> bool
Activate (click/toggle) the currently focused UI element.
Returns True if something was activated.
def handle_controller_direction(self: Any, direction: str) -> bool
Let the focused widget consume direction before moving focus.
def get_clipboard(self: Any, default: str = '') -> str
Return text from the system clipboard, or ``default`` if unavailable.
def set_clipboard(self: Any, text: Any) -> bool
Set text in the system clipboard; return False if unavailable.
def _module_path(self: Any, module: types.ModuleType) -> Optional[Path]
Return a reloadable source path for an imported module.
def _resolve_watch_module(self: Any, ref: Any) -> Tuple[types.ModuleType, Path]
Resolve a module, import name, source path, or path-backed AtlasItem.
def watch_module(self: Any, ref: Any) -> str
Watch a Python module for stable source changes and reload it.
def watch_scene(self: Any, scene_ref: Any) -> str
Watch a registered scene name or Scene subclass for source changes.
def on_scene_reload(self: Any, name: str, callback: Callable[[Scene], None]) -> None
Register a callback receiving the newly constructed scene instance.
def on_module_reload(self: Any, name: str, callback: Callable[[types.ModuleType], None]) -> None
Register a callback receiving a reloaded module.
def _log_hot_reload_error(self: Any, message: str) -> None
No documentation
def _reload_module(self: Any, name: str, record: Dict[str, Any]) -> Optional[types.ModuleType]
No documentation
def _reload_scene(self: Any, scene_name: str, record: Dict[str, Any], module: types.ModuleType, *args: tuple, **kwargs: dict) -> None
No documentation
def _poll_hot_reload(self: Any) -> None
Timer callback: reload only after a changed mtime is stable once.
def add_scene(self: Any, name: str, scene_class: Type[Scene], *args: tuple, **kwargs: dict) -> Any
Add a scene to the engine by class (the engine will instantiate it).

Args:
   name (str): The name of the scene
   scene_class (Type[Scene]): The scene class to instantiate
   *args: Arguments to pass to scene constructor
   **kwargs: Keyword arguments to pass to scene constructor
def remove_scene(self: Any, ref: Any) -> bool
Remove a registered scene by name or by scene subclass.

``ref`` may be a scene name (``str``), or a ``Scene`` subclass; in the
latter case the first registered ``isinstance`` match is removed.  If
the removed scene is active, ``current_scene`` is set to ``None`` and
the engine does not automatically switch to another scene.
def _switch_scene_immediate(self: Any, name: str, *args: tuple, **kwargs: dict) -> Any
Immediately switch to a scene without any transition effect.
This is the core logic extracted from the old set_scene.
def set_scene(self: Any, name: str, effect: Optional[TransitionType] = None, duration: float = 0.5, *args: tuple, **kwargs: dict) -> Any
Set the current active scene, optionally with a transition effect.

Args:
   name (str): The name of the scene to set as current.
   effect (TransitionType, optional): Type of transition effect.
       If None or TransitionType.NONE, switch immediately.
   duration (float): Duration of the transition in seconds (default 0.5).
def find_event_handlers(self: Any, event: int, rep_id: str) -> bool
No documentation
def on_event(self: Any, event_type: int, rep_id: Optional[str] = None) -> Any
Decorator to register event handlers

Args:
   event_type (int): The Pygame event type to listen for
   rep_id (str, optional): A representative ID for the handler
Returns:
   Callable: The decorator function
def setup_notifications(self: Any, max_concurrent: int = 5, margin: int = 20, spacing: int = 10) -> Any
Setup notification system configuration.

Args:
   max_concurrent: Maximum concurrent notifications to show
   margin: Margin from screen edges
   spacing: Spacing between stacked notifications
def show_notification(self: Any, text: str, notification_type: NotificationType = NotificationType.INFO, duration: Optional[float] = None, position: Any = NotificationPosition.TOP_RIGHT, width: int = 300, height: int = 60, show_close_button: bool = True, auto_close: bool = True, animation_speed: float = 0.3, show_progress_bar: bool = False, on_close: Optional[Callable] = None, on_click: Optional[Callable] = None) -> Any
Show a notification with advanced options.

Args:
   text: Notification text
   notification_type: Type of notification
   duration: Display duration in seconds
   position: Position (NotificationPosition or custom (x, y) tuple)
   width: Width of notification
   height: Height of notification
   show_close_button: Whether to show close button
   auto_close: Whether notification auto-closes
   animation_speed: Speed of slide/fade animations
   show_progress_bar: Whether to show progress bar
   on_close: Callback when notification is closed
   on_click: Callback when notification is clicked
   
Returns:
   The created Notification object
def show_info(self: Any, text: str, duration: Optional[float] = None, position: Any = NotificationPosition.TOP_RIGHT) -> 'Notification'
Show an info notification.
def show_success(self: Any, text: str, duration: Optional[float] = None, position: Any = NotificationPosition.TOP_RIGHT) -> 'Notification'
Show a success notification.
def show_warning(self: Any, text: str, duration: Optional[float] = None, position: Any = NotificationPosition.TOP_RIGHT) -> 'Notification'
Show a warning notification.
def show_error(self: Any, text: str, duration: Optional[float] = None, position: Any = NotificationPosition.TOP_RIGHT) -> 'Notification'
Show an error notification.
def clear_all_notifications(self: Any) -> Any
Clear all notifications.
def get_notification_count(self: Any) -> int
Get current number of active notifications.
def get_notification_queue_length(self: Any) -> int
Get current notification queue length.
def has_notifications(self: Any) -> bool
Check if there are any active notifications.
def has_queued_notifications(self: Any) -> bool
Check if there are any queued notifications.
def get_all_themes(self: Any) -> Dict[str, any]
Get all available themes including user custom ones

Returns:
   Dict[str, any]: Dictionary with theme names as keys and theme objects as values
def get_theme_names(self: Any) -> List[str]
Get list of all available theme names

Returns:
   List[str]: List of theme names
def set_global_theme(self: Any, theme: Union[ThemeType, str], dark: Optional[bool] = True) -> bool
Set the global theme for the entire engine and update all UI elements

Args:
   theme_name (str): Name of the theme to set
   
Returns:
   bool: True if theme was set successfully, False otherwise
def set_dark_mode(self: Any, dark: bool | None) -> Any
Set the global dark mode for the engine and update all UI elements

Args:
   dark (bool): True
def get_dark_mode(self: Any) -> bool
Get the global dark mode for the engine

Returns:
   bool: True if dark mode is enabled, False otherwise
def _update_all_ui_themes(self: Any, theme_enum: Any) -> Any
Update all UI elements in the current scene to use the new theme.
def get_fps_stats(self: Any) -> dict
Get comprehensive FPS statistics (optimized)

Returns:
   dict: A dictionary containing FPS statistics
def get_hardware_info(self: Any) -> dict
Get hardware information
def ScaleSize(self: Any, width: float, height: float) -> Tuple[float, float] | Tuple[int, int]
Scale size is a function that will convert scales size to a pixel size

e.g.:
- 1.0, 1.0 = Full Screen
- 0.5, 0.5 = Half Screen
- 0.5, 1.0 = Half Screen Width, Full Screen Height

Args:
   width (float): Width scale
   height (float): Height scale
Returns:
   Tuple[float, float]|Tuple[int, int]: Pixel size
def ScalePos(self: Any, x: float, y: float) -> Tuple[float, float] | Tuple[int, int]
Scale position is a function that will convert scales position to a pixel position

e.g.:
- 1.0, 1.0 = Bottom Right
- 0.0, 0.0 = Top Left
- 0.5, 0.5 = Center

Args:
   x (float): X position
   y (float): Y position
Returns:
   Tuple[float, float]|Tuple[int, int]: Pixel position
def _rebuild_ui_layers(self: Any) -> Any
Rebuild the UI layer manager from the current scene's UI elements.
def _update_cursor(self: Any) -> None
Apply the topmost UI element's pressed/hover/default cursor.
def set_cursor_state(self: Any, name: str = 'arrow') -> bool
Set the cursor directly through the engine's CursorManager.
def _find_scrollable_under_mouse(self: Any, elements: Any, mouse_pos: Any) -> Any
Recursively search UI elements (and their children) for the topmost
visible, enabled element that has an 'on_scroll' method and contains the mouse.
def _dispatch_mouse_wheel(self: Any, event: Any) -> Any
Dispatch MOUSEWHEEL event to the topmost scrollable UI element under the mouse.
def run(self: Any) -> Any
Main game loop.
def transition_to(self: Any, scene_name: str, effect: TransitionType = TransitionType.NONE, duration: float = 0.5, *args: tuple, **kwargs: dict) -> Any
Start a transition using immutable snapshots of both scenes.
def _capture_scene_surface(self: Any, scene: 'Scene') -> pygame.Surface
Render the given scene (including UI and particles) to a pygame.Surface.
def _render_ui_elements_for_scene(self: Any, scene: Any) -> Any
Render all UI elements of a given scene (sorted by z_index).
def _update_transition(self: Any, dt: float) -> Any
No documentation
def _render_transition(self: Any) -> Any
No documentation
def add_timer(self: Any, name: str, duration: float, callback: Optional[Callable] = None, callback_args: tuple = (), callback_kwargs: dict = None, repeats: bool = False) -> bool
Add a timer. Returns True if added successfully.
def add_anonymous_timer(self: Any, duration: float, callback: Callable, callback_args: tuple = (), callback_kwargs: dict = None, repeats: bool = False) -> str
Add a timer with an auto‑generated name and return its name.
def remove_timer(self: Any, name: str) -> bool
Remove a timer immediately.
def destroy_timer(self: Any, name: str) -> bool
Mark a timer for destruction (removed on next update).
def pause_timer(self: Any, name: str) -> bool
Pause a timer.
def resume_timer(self: Any, name: str) -> bool
Resume a paused timer.
def reset_timer(self: Any, name: str) -> bool
Reset a timer to start counting from now.
def timer_elapsed(self: Any, name: str) -> Optional[float]
Get elapsed time for a timer.
def timer_remaining(self: Any, name: str) -> Optional[float]
Get remaining time for a timer.
def timer_done(self: Any, name: str) -> Optional[bool]
Check if a timer has completed.
def timer_exists(self: Any, name: str) -> bool
Check if a timer exists.
def timer_paused(self: Any, name: str) -> Optional[bool]
Check if a timer is paused.
def clear_timers(self: Any) -> None
Remove all timers.
def get_all_timers(self: Any) -> List[str]
Get names of all active timers.
def get_timer_count(self: Any) -> int
Get the number of active timers.
def on_window_resize(self: Any, func: Callable) -> Any
Decorator for window resize event.
def on_window_close(self: Any, func: Callable) -> Any
Decorator for window close event.
def on_window_focus(self: Any, func: Callable) -> Any
Decorator for window focus gained event.
def on_window_blur(self: Any, func: Callable) -> Any
Decorator for window blur (focus lost) event.
def on_window_move(self: Any, func: Callable) -> Any
Decorator for window move event.
def on_window_minimize(self: Any, func: Callable) -> Any
Decorator for window minimize event.
def on_window_maximize(self: Any, func: Callable) -> Any
Decorator for window maximize event.
def on_window_restore(self: Any, func: Callable) -> Any
Decorator for window restore event.
def on_window_enter(self: Any, func: Callable) -> Any
Decorator for mouse entering window event.
def on_window_leave(self: Any, func: Callable) -> Any
Decorator for mouse leaving window event.
def get_window_state(self: Any) -> Dict[str, Any]
Get current window state.
def is_window_focused(self: Any) -> bool
Check if window is focused.
def is_window_minimized(self: Any) -> bool
Check if window is minimized.
def is_window_maximized(self: Any) -> bool
Check if window is maximized.
def get_controllers(self: Any) -> List['Controller']
No documentation
def get_controller(self: Any, index: int) -> Optional['Controller']
No documentation
def is_using_controller(self: Any) -> bool
No documentation
def on_controller_connect(self: Any, callback: Callable[['Controller'], None]) -> Any
Register callback when a controller connects.
def on_controller_disconnect(self: Any, callback: Callable[['Controller'], None]) -> Any
Register callback when a controller disconnects.
def update_mouse(self: Any) -> Any
Update mouse position and button state with proper click detection.
def visibility_change(self: Any, element: ui.UIElement, visible: bool) -> Any
Change visibility of an element or list of elements.
def mouse_pos(self: Any) -> tuple
No documentation
def mouse_pressed(self: Any) -> list
No documentation
def mouse_wheel(self: Any) -> float
No documentation
def _render_focus_rect(self: Any) -> Any
Render a yellow outline around the focused UI element.
def _render(self: Any) -> Any
Rendering with GPU particles and shadows.
def live_inspector(self: Any) -> Union[LiveInspector, None]
No documentation
def add_function_to_live_inspector(self: Any, name: str, callable_obj: Callable, parameters: List[Tuple[str, type]], description: str = '') -> None
No documentation
def _render_ui_elements(self: Any) -> Any
Render UI elements with individual profiling if enabled.
def _render_particles(self: Any) -> Any
Render particles using OpenGL.
def _update_ui_elements(self: Any, dt: Any) -> Any
No documentation
def _process_ui_element_tree(self: Any, root_element: Any, dt: Any) -> Any
Process a UI element and all its children with proper event consumption.
def enable_performance_profiling(self: Any, enabled: bool = True) -> Any
Enable/disable detailed performance profiling.
def get_frame_timing_breakdown(self: Any) -> Dict[str, float]
Get the timing breakdown (ms) for the last completed frame.
def get_performance_stats(self: Any) -> Dict[str, Any]
Return both FPS stats and frame timing breakdown.
def get_update_timing_stats(self: Any, category: str = 'all') -> Dict[str, Any]
Get update timing statistics for a specific category.
def get_render_timing_stats(self: Any, category: str = 'all') -> Dict[str, Any]
Get render timing statistics for a specific category.
def get_ui_update_stats(self: Any) -> Dict[str, Any]
Get UI update timing statistics.
def get_ui_render_stats(self: Any) -> Dict[str, Any]
Get UI render timing statistics.
def get_individual_ui_update_stats(self: Any) -> Dict[str, float]
Get individual UI element update timing statistics.
def get_individual_ui_render_stats(self: Any) -> Dict[str, float]
Get individual UI element render timing statistics.
def get_scene_update_stats(self: Any) -> Dict[str, Any]
Get scene update timing statistics.
def get_scene_render_stats(self: Any) -> Dict[str, Any]
Get scene render timing statistics.
def get_total_frame_time(self: Any) -> Dict[str, float]
Get total frame time breakdown.
def add_to_atlas(self: Any, name: str, path: Union[str, Path], category: Optional[Union[str, AtlasCategory]] = None, auto_detect: bool = True, validate: bool = True) -> AtlasItem
Add a file to the atlas.

Args:
   name: Unique name for the resource.
   path: File path (string or Path).
   category: Explicit category (string or AtlasCategory). If None, will be auto-detected.
   auto_detect: If True and category is None, guess from extension.
   validate: If True, enforce extension rules for the category.
   
Returns:
   AtlasItem: The created atlas item.
def get_atlas_item(self: Any, name: str) -> Optional[AtlasItem]
Retrieve an atlas item by its name.
def get_atlas_path(self: Any, name: str, category: Optional[Union[str, AtlasCategory]] = None) -> Optional[Path]
Get the file path of an atlas item, optionally checking its category.

Args:
   name: Resource name.
   category: If provided, only return the path if the item's category matches.
   
Returns:
   Path or None if not found.
def add_texture_to_atlas(self: Any, name: str, path: Union[str, Path]) -> AtlasItem
Shortcut to add a texture (image) to the atlas.
def add_font_to_atlas(self: Any, name: str, path: Union[str, Path]) -> AtlasItem
Shortcut to add a font to the atlas.
def add_audio_to_atlas(self: Any, name: str, path: Union[str, Path]) -> AtlasItem
Shortcut to add an audio file to the atlas.
def shutdown(self: Any) -> Any
Cleanup resources.
Back to Core Module