Skip to content

Archived on 2026-10-24. The Godot rewrite is cancelled. Migration decisions and work checklists below are withdrawn. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.

Core Game Loop

Purpose

The core loop controls a run from selected stage/characters through spawning, combat, leveling, upgrades, boss/stage progression, game over, rewards, save updates, and leaderboard submission.

Current Unity Implementation

Main scripts:

Path Script Role
Assets/Script/App/Core/AppInitiator.cs AppInitiator App boot, static manager initialization, data loading, services, save startup, menu load.
Assets/Script/Shared/Session/GameSession.cs GameSession Holds selected characters, level, difficulty, player count, shop modifiers.
Assets/Script/Game/GameSystem/Bootstrap/GameInitializer.cs GameInitialization Initializes reward tables, spawner data, players, and runtime flags.
Assets/Script/Game/Managers/GameManager.cs GameManager Main run singleton: score, level UI, reward queue, pause/resume, game over, stage unlock, save, leaderboard.
Assets/Script/Game/GameSystem/GameState/GameStateService.cs GameStateService Tracks paused/rewarding/game-over/boss-defeated flags and time scale.
Assets/Script/Game/GameSystem/Player/PlayerManager.cs PlayerManager Run XP, level, level-up reward event.
Assets/Script/Game/Stage/Timer.cs Timer Tracks game time, stage progression time, and spawn schedule time.
Assets/Script/Game/Entities/EnemySpawner/SpawnerEventManager.cs SpawnerEventManager Spawns creep/special/boss waves from SpawnerData.
Assets/Script/Game/GameSystem/Rewards/RewardRoller.cs RewardRoller Queues reward events, rolls upgrade cards, displays reward UI phases.
Assets/Script/Game/GameSystem/Upgrades/UpgradeSelection.cs UpgradeSelection Player input for card selection.

Key Data

  • GameSession.Instance.Character: selected Character assets.
  • GameSession.Instance.LevelData: selected LevelData.
  • GameSession.Instance.Difficulty: enum index used to choose StageMap.
  • GameSession.Instance.PlayerNumber: one or two players.
  • LevelData.stageMap[difficulty]: supplies game duration, creep spawner data, special spawner data, optional dialog.
  • GameManager.BasicRewardTable, SpecialRewardTable, UltimateRewardTable: runtime card roll pools.
  • PlayerManager.currentExp, expToNextLevel, level: per-run progression.
  • Timer.currentTime: stage/boss end timer.
  • Timer.currentSpawnerTime: spawn schedule timer affected by multiplier.
  • Timer.currentGameTime: display/playtime timer.
  • GameStateService: isPaused, isRewarding, isGameOver, isBossDefeated.

Runtime Flow

  1. Boot scene creates long-lived app objects.
  2. AppInitiator.Awake() initializes input, localization, achievement/shop/collection managers, and GameDataLoader.
  3. AppInitiator.RunStartupFlow() reads Steam id if available, creates AudioService and SaveService, runs SaveManager.StartupAsync, loads settings, then loads main menu.
  4. Main menu configures GameSession with selected stage, difficulty, player count, characters, and shop modifiers.
  5. Gameplay scene starts. GameManager.Awake() creates or resets run state, initializes PlayerManager, finds components, and creates RewardRoller.
  6. GameManager.Start() asks GameInitialization to run setup, hides HUD, and pauses.
  7. GameInitialization.ImportRewardTable() filters upgrades by stage and rarity/chest rules into basic/special/ultimate reward tables.
  8. GameInitialization.PrepareSpawnAndGameManagerData() copies selected stage spawner data, end time, boss, player count, character list, and reroll count into runtime managers.
  9. GameInitialization.PreparePlayer() activates selected players, applies character weapon/stats, adds camera targets, and enables HUD status panels.
  10. GameManager.OpenGate() resumes the game, starts timers, plays gate animation, and shows HUD.
  11. During gameplay:
    • Timer advances active timers.
    • SpawnerEventManager checks spawn windows and starts spawn coroutines.
    • Players move, target enemies, fire weapons, collect items, and take damage.
    • Enemies update their state machines and drop rewards on death.
    • Items grant XP, soul, chests, potions, and reward events.
  12. XP calls GameManager.AddExp, then PlayerManager.AddExperience.
  13. If XP crosses threshold, PlayerManager.OnRewardReady invokes GameManager.EnqueueReward(LevelUpReward).
  14. RewardRoller.ProcessRewardQueue() rolls cards when no reward phase is active.
  15. Players select cards via UpgradeSelection; UpgradeButton.SelectedUpgrade() calls UpgradeFunction.SelectUpgrade.
  16. RewardRoller.CompleteReward() clears reward state and cards.
  17. Game over starts when both players are inactive or Timer.currentTime >= SpawnManager.EndGameTime.
  18. GameManager.GameOver() stops timers/spawning, saves currencies/counters, fills game-over UI, pushes leaderboard data, checks achievements, saves, and pauses after UI animation.

Dependencies

  • Heavy dependence on GameManager.instance.
  • GameSession.Instance is the bridge from menu to run.
  • GameDataLoader supplies ScriptableObject lists and dictionary lookups.
  • Save static wrapper writes currencies, unlocks, config, counters.
  • Game.Audio, AudioResources.Instance, DOTween, and UI object references are used inside core flow.
  • PoolManager.Instance is used by combat, spawn, items, and feedback.

Problems / Risks

  • GameManager owns too many unrelated responsibilities.
  • GameStateService.SetRewarding() does not call UpdateTimeScale() directly; reward UI code checks GameManager.isPaused, so reward pause behavior may be inconsistent. This needs runtime confirmation.
  • Reward rolling mutates shared Upgrade asset fields such as upgradeTaken and modifiedWeight.
  • GameInitialization uses boolean init flags and coroutines waiting for flags; a missing flag can stall systems.
  • The end condition uses Timer.currentTime >= EndGameTime, while boss transitions stop/start timers separately.
  • Some logic assumes two player array slots exist even when NumberOfPlayer == 1.
  • UI, save, leaderboard, and gameplay run-state behavior are intertwined in game over.

Migration Notes for Godot

Preserve:

  • Stage/character/session selection behavior.
  • Three timer concepts: visible playtime, stage progress/end timer, and spawn schedule timer.
  • Reward queue behavior: level-up/chest/boss rewards are queued and processed one at a time.
  • One/two player reward selection rules.
  • Stage unlock and achievement/save outcomes.

Redesign:

  • GameSession as an autoload or explicit resource passed into the run scene.
  • GameSession should be immutable enough during a run to avoid accidental asset mutation.
  • Split run scene into:
  • RunState
  • GameSession
  • StageController
  • SpawnDirector
  • PlayerManager
  • RewardDirector
  • ScoreManager
  • GameOverController
  • UIFlowController
  • Use Godot signals instead of global C# events where possible.
  • Keep save/leaderboard calls out of core gameplay update loops.

Open Questions

  • Does reward selection currently pause gameplay correctly in the latest playable build?
  • Does easy difficulty intentionally end at 300 seconds while other difficulties depend on boss defeat?
  • Which timer should drive final score/survival achievements in Godot: currentTime or currentGameTime?
  • Unknown / needs manual confirmation.