|
RapidGameFramework
Reusable Godot managers for data-driven small games
|
n
RapidGameFramework should stay fast, lightweight, and platform independent. The target is that a released game built on the framework can run smoothly on a five-year-old phone after normal content and art optimization.
This plan treats performance as framework work first. Game code should improve by adopting optimized shared systems instead of each game carrying its own special-case fixes.
Arcade Pong is the active released game and should be the first rollout target for performance-sensitive work. Post-release feedback is tracked in Arcade Pong Release Feedback and mapped into the sprint tasks below so release polish does not drift away from the optimization plan.
The broad optimization/refactor sprint track is closed for the current release cycle. This page is retained as the implementation record, baseline reference, and future optimization backlog. Active release work now lives in Release Sprint Plan, and new engine work should be targeted to a measured problem or an active release checklist.
Use these targets as release-readiness goals. They can tighten later when the framework has more profiling data.
Goal: know where time and memory are going before refactoring.
Status: complete. Sprint 1 delivered performanceMonitor, a reusable opt-in monitor for timers, counters, gauges, frame samples, node counts, manager cache stats, callable timing, provider cache deltas, saveable summary dictionaries, scene instantiation loop benchmarks, and a compact debug overlay. The active scene-entry baseline list lives in data/performance/sprint1_baselines.json and is smoke-tested as informational coverage without threshold gating. Headless smoke writes a compact report to builds/performance/performance_baseline_report.json.
Tasks:
performanceMonitor system for timers, counters, frame samples, cache sizes, node counts, and scene transition marks.Acceptance:
Initial hot-path candidates:
Crash fix:
Goal: make asset lookup predictable and cheap.
Status: started. spriteManager now separates decoded source-image caching from final texture, scaled texture, animation-frame, path-texture, and sheet-cell caches. This reduces repeated image decoding/get-image work during spritesheet cell extraction and path-based scaled UI lookups. Sprite manifests now support warm_profiles; Arcade Pong seeds a match profile and Project Alchemy seeds a field_slice profile for data-driven scene warmup. Scene transitions can now call clear_transient_cache() or trim_texture_cache() to drop generated scene/UI texture products while preserving decoded source images for the next warm profile. Arcade Pong, Card Game, Reflecting Pool, Platformer, and Project Alchemy now use targeted warm profiles at scene startup instead of warming every loaded sprite pack. spriteRenderManager now exposes draw diagnostics so dense custom playfields can report sprite hits, fallbacks, missing lookups, and rotated draws to performanceMonitor.
Tasks:
spriteManager, spriteRenderManager, paletteManager, and renderNodePool cache behavior.Game rollout:
match profile wiring is complete.visible_card_shell profile wiring is complete.field_slice profile wiring is complete.hud_and_pool profile wiring is complete for resources and pool effects; event image warming should stay current-event scoped as that renderer is profiled.level_core profile wiring is complete.Acceptance:
Goal: reduce allocation and layout churn from reusable UI.
Status: started. inventoryList now pools row buttons across renders, hides unused rows instead of freeing them, and exposes row reuse stats through get_stats()/get_cache_stats(). This is the first step toward larger list virtualization for card inventories, battle pickers, editor palettes, and future RPG/platformer inventories. layoutManager now caches reusable panel, button, and achievement-row StyleBoxFlat resources per theme/state so repeated theme application does not recreate identical style boxes. buttonRowPool now covers fixed action rows such as Arcade Pong power-up slots, future spell/action bars, HUD command rows, and editor toolbars without destroying/recreating buttons each refresh. Smoke coverage now captures layout style-cache and row-pool providers through performanceMonitor.capture_provider_stats(), so future baseline reports can include UI cache diagnostics.
Tasks:
layoutManager, achievement screens, settings screens, inventory lists, and menu rendering.StyleBox, font, and color-role outputs per active theme. StyleBox caching is started for panel, button, and achievement rows.buttonRowPool wiring is complete for Arcade Pong power-up slots.Game rollout:
Acceptance:
Goal: move repeated active-game update logic into reusable, allocation-light systems.
Tasks:
_process and _draw usage in active scenes.entityManager, entityRenderer, projectileLifecycleManager, and runManager.Game rollout:
arcadePhysicsManager.step_projectiles(...).Acceptance:
Goal: prevent slow startup and repeated parsing as content grows.
Closeout status:
dataCacheManager provides a reusable runtime JSON cache for managers and scenes that repeatedly load stable data files. It now tracks cache hits and misses through get_cache_stats() so performanceMonitor can include JSON catalog reuse in provider snapshots.statsManager.record_events(...) calls so each gameplay event evaluates achievements once instead of repeatedly after every counter write.saveManager now exposes a reusable delayed save queue. Arcade Pong uses it for mid-match stat/cosmetic/settings changes, then flushes immediately at match completion, game-settings close, start-menu navigation, or scene exit.dataCacheManager, and smoke coverage validates both cached startup files.dataCacheManager via statsManager.load_achievement_data(...)._load_json(...) helper now routes through dataCacheManager, giving its remaining prototype/menu JSON reads the same cached path without rewriting the owning managers mid-sprint.cardManager, packManager, alchemyManager, and combatCardFactory now expose data-based loader APIs. Card Game cards/packs and Project Alchemy elements/reactions/combat cards use cached data while retaining old file-based convenience loaders for tools and small scripts.adventureManager and gridCombatManager now expose data-based loader APIs; Project Alchemy adventure definitions and enemy archetypes use cached startup data as well.buildProfileManager.audit_single_game_profile(...) now exposes a reusable single-game packaging audit so CI/editor tooling can verify required shared roots, game roots, other-game excludes, and accidental cross-game data includes.Tasks:
Game rollout:
Closeout:
FileAccess/JSON.parse reads in the audit; sprite pack loading remains owned by spriteManager.dataCacheManager plus manager-level load_*_data(...) APIs from the start.Acceptance:
Goal: apply the optimized framework paths consistently.
Handoff: see Framework Handoff for the current completed-cache rollout, validation status, known shutdown-warning caveat, and the recommended next pickup point.
Current status:
buildProfileManager now exposes a batch audit for individual game profiles, and the CI profile validator mirrors those checks before export work begins.platformer_demo was normalized to the same shared_roots plus game-specific include_roots shape used by the other individual profiles, avoiding broad res://scripts packaging.achievementScreen page renderer, so title/filter/list/back layout follows the same reusable path as in-game achievement overlays instead of a scene-local hand-built variant.dataCacheManager, so launcher-level game catalogs follow the same cached JSON path as gameplay scenes instead of reopening/parsing level indexes directly.cardInventoryAdapter to prepare filtered, sorted, selected, stat-highlighted inventoryList rows. This removes scene-local card table rules and gives future card/deck games a reusable adapter instead of rebuilding list behavior per game.gridViewportManager. This keeps the six-tile reveal/half-cell context stable while making the same responsive grid camera available to future tactical, dungeon, and editor-backed games.levelManager cached semantic rects and visible tile-record slices. Larger side-scrolling/metroidvania levels can reuse the same camera-window query instead of rebuilding every tile record or scanning whole maps during draw/pickup loops.levelManager object/world-rect culling helpers. Future large levels can keep off-camera objects out of draw work without each game inventing a separate visibility check.runManager state at level load, update run counters/collected coin totals during play, and complete/fail the run before result popups. This gives future platformers a shared place for level summaries without making the movement loop own result bookkeeping.resultsScreenController.show_popup(...) now bundles result-popup creation, viewport fitting, theme/text application, and focus into one reusable call. Platformer retry/completion popups use this path so future level games avoid repeating popup sizing and focus boilerplate.spriteManager cache instead of falling back to raw per-effect load(...) calls.spriteManager.get_texture_for_path(...), so launcher art such as the Arcade Pong menu background uses the shared path texture cache instead of direct scene-local loads.spriteManager path cache rather than direct theme style loads.resource_hud icons now resolve through a default spriteManager instance, so sprite_id and legacy icon_path resource art share the same scaled texture cache without scene-local load(...) fallbacks.spriteManager scaled texture cache only, removing the last direct icon_path load in that button render path.gameSettingsPanel.load_definition(...), aiController.load_profiles(...), and arcadePowerupManager.load_definitions(...) now use dataCacheManager internally. Future games that use these convenience file loaders get cached JSON reads even before wiring their own scene-level data cache.cardManager.load_cards(...), packManager.load_packs(...), combatCardFactory.load_catalog(...), and combatCardFactory.load_definitions(...) all route through dataCacheManager while preserving their data-based loader APIs.alchemyManager.load_definitions(...), alchemyManager.load_reactions(...), gridCombatManager.load_archetypes(...), and adventureManager.load_adventure(...) reuse parsed JSON while keeping their parsed-data APIs available for scene-level caches and tests.menuManager.load_menu(...), inputManager.load_actions(...), and levelManager.load_level(...) reuse parsed JSON while preserving existing menu stack, input binding, and derived level-geometry cache behavior.paletteManager.load_palettes(...), tutorialManager.load_steps(...), and unlockManager.load_rules(...) keep stable catalog data cached while leaving recolored texture and progression-state caches separate._save_state() calls queue the latest state, _process() advances the pending timer, and scene exit flushes any pending write. This removes direct disk writes from grid-step gameplay while preserving the dictionary save contract and global achievement persistence on flush._ready(). This keeps the initial Shop entry lighter while preserving the reusable inventoryList battle picker path.hud_and_pool sprite warm profile instead of warming every loaded UI, resource, event, and pool-effect sprite at startup. Event artwork remains lazy-loaded as events are drawn.arcadePhysicsManager.step_projectiles(...) for batch movement, target-hit partitioning, and out-of-bounds cleanup. The scene keeps only the game-specific stun/SFX/stat response.arcadePhysicsManager.step_ball_group(...) and collect_ball_group_scoring(...), moving primary/extra ball promotion out of the scene and into reusable arcade framework code.statsPanel popup instance and refresh its rows when reopened instead of freeing and rebuilding the same modal tree.achievementScreen now supports in-place popup refreshes. Reflecting Pool, Arcade Pong, and Card Game use that shared path so reopening achievements updates current global achievement state without freeing and rebuilding the entire popup tree.resourceManager.load(...) now uses dataCacheManager internally and exposes load_resource_data(...) for callers that already have parsed resource JSON. Reflecting Pool keeps its existing API while avoiding repeated resource-file parsing on reset/startup paths.eventManager now uses dataCacheManager for directory/event-pack loads, including top-level JSON array packs, and exposes load_event_pack_data(...) for callers that already have parsed event data.actionEffectManager.load_actions(...) and alchemyGraphManager.load_graph(...) now route through dataCacheManager, while load_action_data(...) and load_graph_data(...) remain available for editor/import tools that already have parsed dictionaries.buildProfileManager.load_profile(...) now uses dataCacheManager, reducing repeated parse work in CI/editor validation paths that reload the profile directory for menus, audits, and export plans.statsManager.load_achievements(...) now uses dataCacheManager internally and exposes get_cache_stats(), so achievement catalogs loaded by main menus, game menus, and smoke tests share the same cached JSON path.buildInfo.load_info(...) now routes build metadata through dataCacheManager while preserving local/editor date resolution. Splash and menu helpers can reload metadata without reparsing the same build-info file.performanceMonitor.load_baseline_profiles(...) now uses dataCacheManager and exposes cache diagnostics, so repeated baseline scene audits do not reparse the same profile catalog.audioManager now reads its music manifest through dataCacheManager while leaving actual audio streams on Godot's ResourceLoader path. This avoids repeated manifest parsing without duplicating imported audio resources.sprite_mapper.gd repeatedly loading the same sheet images while validating mappings during _ready(). The mapper now caches validation images per loaded sprite file, so tool validation stays useful without wedging headless CI. On the Windows Codex desktop sandbox, Godot headless script runs must be launched with filesystem access to Godot's normal AppData editor/cache folders; otherwise Godot 4.7 can crash before any GDScript executes. With that access, local full-smoke validation runs cleanly.quit() was rejected because deferred callbacks from older scene checks can still fire against freed nodes and crash Godot headless. The right fix is to make individual scene/popup checks disconnect or close their own deferred work before freeing._ready(). Keep teardown work local to the scene/helper that owns the callback instead of sweeping the whole root after the run.pauseOverlayController.dispose() disconnects submenu/theme hooks, standard_settings_popup.dispose() drops managed popup references and settings-change signals, achievementNotifier.dispose() cancels queued and active toast notifications, and audioManager.dispose() stops playback and clears stream caches for temporary tool/test instances. audioManager also exposes get_cache_stats() for provider snapshots so browser music/SFX cache state can be tracked alongside sprite and UI caches.scripts/ci/build_profiles.py --validate-only --profiles all --platforms all.Latest smoke benchmark snapshot:
emoji_card_collector_entry: ~415 nodes. This includes more initialized card-game shell state than the earlier lazy-only snapshot, but remains far below the original eager Collection/Battle allocation path.project_alchemy_entry: ~391 nodes. Active-field performance is still the important measure here, but startup is no longer the largest cross-game outlier.arcade_pong_entry: ~127 nodes.main_menu_startup: ~23 nodes.platformer_demo_entry: 1 measured root node.reflecting_pool_entry: 1 measured root node.Tasks:
Deferred targeted tasks:
Release handoff:
Order:
Acceptance:
Goal: make old-phone performance part of normal release discipline.
Status: complete for the current release-hardening pass. The repeatable validation checklist now lives in Mobile And Browser Hardening Checklist and covers Arcade Pong release paths, cross-game menu/settings/input behavior, low-power mode requirements, and asset/package checks. The first low-power slice is now wired through settingsManager, the standard settings popup, res://data/system/low_power_presets.json, optional spriteManager warm-profile entries, Arcade Pong's field power-up/effect-label path, inputManager no-churn touch/control switching, and audioManager next-track queue suppression.
Tasks:
low_power_mode setting plus Arcade Pong field power-up cap, slower spawn pace, smaller held-slot icons, and hidden transient field effect text from low_power_presets.json. The audio manager also skips queued next-track streams from the same profile in low-power mode to reduce browser/mobile memory pressure.inputManager.update_input_module(...) avoids redundant on-screen-control visibility writes, and onscreenControlManager.get_stats() exposes visibility-change diagnostics for smoke/performance checks.Acceptance:
all_demos remain responsive.Follow-up:
Prioritize modules with the largest impact on frame time, memory, or repeated allocation:
spriteManager, spriteRenderManager, renderNodePool, paletteManager.layoutManager, achievementScreen, settingsManager, gameSettingsPanel.inventoryList, inventoryCategoryPanel, menuManager, uiFlowManager.entityManager, entityRenderer, runManager, projectileLifecycleManager.arcadePhysicsManager, arcadePowerupManager, aiController, matchManager.levelManager, levelEditorManager, characterController2D.gridCombatManager, gridWorldManager, targetingManager, battlefieldGenerator.saveManager, statsManager, audioManager, buildProfileManager.Each optimization sprint should finish with: