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 Objectcontent 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.