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.

Audit correction (2026-10-24): the present owner report leaves the freeze root cause unresolved. The fullscreen classification and confidence ratings below are historical hypotheses, not a captured reproduction or proof. Use GAME-01 in the current issue register.

Known Risks And Unknowns

Purpose

This file records migration risks, unclear behavior, and possible causes of the reported issue where the game can get stuck for around 1 minute.

Possible Causes of 1-Minute Freeze

Current clarification: the reported one-minute stuck behavior happens when opening the Unity game in fullscreen mode. It is currently treated as a Unity startup/window/display issue, not as a confirmed in-run gameplay freeze.

For a Unity startup probe checklist, see docs/migration/unity-analysis/20_unity_freeze_bug_investigation_notes.md. The gameplay risks below are still useful performance and soft-lock audit notes, but they should not drive the Godot gameplay architecture unless the same behavior is reproduced during a Godot run.

Risk Evidence Related Scripts Confidence Suggested Check
Special/miniboss spawn suppression may look like a 60-second stall SpawnerEventManager sets isMiniBossSpawn = true and minibossFightTimer = 60f for minibosses. Creep spawning is blocked while isMiniBossSpawn is true. SpawnerEventManager.cs High Reproduce around miniboss spawn times. Log isMiniBossSpawn, minibossFightTimer, special enemy killId, and active spawn events.
Miniboss/special state may not clear if kill event is missed MiniBossDeath clears state only if CheckSpecialKillId(id) matches special spawn data. If enemy despawns by border, pooling, missing killId, or death path without matching event, timer must expire. SpawnerEventManager.cs, Enemy.cs Medium Verify every special/miniboss prefab has a matching killId, and confirm border despawn emits or intentionally does not emit special-clear behavior.
Reward pause-state mismatch can leave reward UI/gameplay in inconsistent state RewardRoller.SetRewarding(true) does not call GameState.UpdateTimeScale(). UpgradeSelection requires GameManager.instance.isPaused and RewardPhase.Selecting. RerollUpgrade() returns if manager.isPaused. RewardRoller.cs, UpgradeSelection.cs, GameStateService.cs, GameManager.cs Medium During reward display, log isPaused, IsRewarding, Time.timeScale, RewardPhase. Confirm selection and reroll conditions.
Pool growth and heavy spawn bursts can cause frame stalls or GC spikes PoolManager instantiates new objects whenever queues are empty and has no prewarm/cap. Many systems spawn projectiles, enemies, rewards, effects, damage popups, item mergers. PoolManager.cs, SpawnerEventManager.cs, PlayerWeapon.cs, PlayerAttack.cs, Enemy.cs, Items.cs Medium Profile allocations and Instantiate spikes. Add pool metrics by prefab name during freeze reproduction.
Scene-wide searches in runtime hot paths can spike with many objects Uses GameObject.FindGameObjectsWithTag in player/enemy targeting, boss cleanup, tutorial, and game over. FindObjectsByType<Items> runs on collectible refind. Player.cs, Enemy.cs, SpawnerEventManager.cs, GameManager.cs, PlayerCollectItem.cs Medium Profile CPU around freeze. Replace or instrument searches with counts/durations.
Per-item infinite MergeChecking coroutine may multiply background work Every enabled Items starts MergeChecking() with while(true) and periodically spawns item merger objects if configured. It is not explicitly stopped except by disable. Items.cs, ItemsMerger.cs Medium Count active item coroutines/items. Confirm coroutines stop on pooling/disable and do not duplicate on repeated enable.
Target tracking sort may become expensive with many enemy hitpoints Each player updates all tracked EnemyLocations and sorts every frame. Enemies can have multiple hitpoints. Player.cs Low/Medium Log target list size and frame cost during dense waves.
Damage popup system can still create too many objects before limiting Enemy.TakeDamage spawns popup first; GameManager.HardLimitPopUpDamage returns old popup after adding. Enemy.cs, GameManager.cs, DamagePopUp.cs Low/Medium Lower DamageIndicatorLimit and profile. Check popup pool growth.
Network/leaderboard calls can wait, but likely outside active gameplay LeaderboardManager uses UnityWebRequest coroutines and file cache. Game-over calls PostLeaderboardData. LeaderboardManager.cs, GameManager.cs Low Confirm freeze timing. If during game over/menu, log request start/end/error.
Save file IO at game over can stall briefly SaveService uses synchronous File.ReadAllText, WriteAllText, File.Copy; GameManager.GameOver saves after updating counters/achievements. SaveService.cs, SaveManager.cs, GameManager.cs, AchievementManager.cs Low Profile game-over save time and achievement save frequency.
Time scale/pause interactions may make wait timers appear stuck Some coroutines use WaitForSeconds and some use WaitForSecondsRealtime. If Time.timeScale is 0, scaled waits stop. Many scripts Medium Identify whether stuck state occurs with Time.timeScale == 0. Prefer explicit paused/realtime design in Godot.

Other Architecture Risks

Risk Evidence Migration Impact
Runtime state stored on ScriptableObjects Ability.isOnCooldown, Ability.currentCooldownTime, Upgrade.upgradeTaken, Upgrade.modifiedWeight, unlock flags on assets. Must split static resource data from per-run/per-player runtime state.
Large hardcoded ability/upgrade switches AbilityHolder, UpgradeFunction, PlayerWeapon.AttackModify, Player.TakeDamage. Godot should use effect/behavior resources to avoid another monolith.
Singleton-heavy coupling GameManager.instance, AbilityFunction.Instance, PoolManager.Instance, AudioResources.Instance, GameSession.Instance, Save. Use autoloads sparingly and explicit node references/signals.
Two-player assumptions Some systems index player[1] or iterate Players.Length instead of active player count. Godot should centralize active player list.
Data id inconsistency Upgrade save uses upgradeName in some places and idname elsewhere. Need id normalization before conversion.
Platform coupling Save nickname and achievements use Steam APIs directly. Wrap platform calls behind an adapter.

Unknowns Requiring Manual Confirmation

  • Exact fullscreen mode, monitor setup, build target, and startup path that reproduce the Unity fullscreen startup issue.
  • Whether the fullscreen issue involves display mode switching, settings load, save load, Steam initialization, or Unity player startup.
  • Which branch/build exhibits the fullscreen startup issue.
  • Production abilities/upgrades should come from Assets/Resources; Assets/Scriptable Object content is test/in-development/reference unless manually promoted.
  • Exact legacy Unity save/config schema for later Godot encrypted JSON migration.
  • Exact Steam/leaderboard implementation details after the core gameplay loop is stable.