Scope: Assets/_Scripts/ (C#). This is a static review (no runtime profiling).
- Files:
Assets/_Scripts/ShieldHandler.cs:38-41(WaitForSeconds(duration))Assets/_Scripts/PlayerPowerupHandler.cs:53-60(WaitForSeconds(duration))Assets/_Scripts/DevConsole.cs:51-54(Time.timeScale = 0when console open)Assets/_Scripts/GameManager.cs:80(Time.timeScale = 0on game over)
- What happens:
- Both shield expiry and powerup expiry depend on scaled time. If the game is paused (
Time.timeScale == 0) while shield is active, the timers stop andshieldActivecan staytrue, making the player permanently invulnerable until something force-disables it.
- Both shield expiry and powerup expiry depend on scaled time. If the game is paused (
- Fix (pick one, but be consistent):
- Use unscaled time for shield/powerup durations (e.g.,
WaitForSecondsRealtime) so effects expire even while paused. - Keep scaled time, but add an explicit “pause/game over cleanup” path that force-disables shield and deactivates all timed powerups when pausing.
- Add a safety net: on player
OnDisable/OnDestroy, forceshieldActive = false(prevents state leak across respawns).
- Use unscaled time for shield/powerup durations (e.g.,
- File:
Assets/_Scripts/EnemySpawner.cs:74-77 - Issue:
shooter.bulletPrefab.GetComponent<EnemyBullet>().damage *= multiplier; - Impact: Scales the prefab’s component state (shared reference). Depending on how that prefab is shared and on Unity reload settings, damage can accumulate or become inconsistent.
- Fix: Apply scaling to the instantiated bullet instance, or store a per-enemy/per-wave damage multiplier and apply it on spawn.
- File:
Assets/_Scripts/PlayerShooting.cs:54-67 - Issue: In scattershot mode, multiple bullets are instantiated, but
GetStats(stats)is only called on the last assignedbulletafter the loop. - Impact: Many bullets have
playerStats == null, leading to incorrect vampiric/critical behavior and possible null refs. - Fix: Call
GetStats(stats)for each instantiated bullet (inside the loop) and avoid using a sharedbulletfield for coroutine spawns.
- File:
Assets/_Scripts/PlayerBullet.cs:73-77 - Issue:
playerStats.GetCurrentHealth()used without ensuringplayerStats != null. - Impact: Rare crashes if a bullet wasn’t initialized properly (see Scattershot issue).
- Fix: Guard
playerStatsbefore use; ensure stats always injected during spawn.
- File:
Assets/_Scripts/PlayerStats.cs:50-55 - Issue:
new public void OnDamageTaken()hides base method instead of overriding. - Impact: Polymorphism breaks; base calls may bypass player-specific feedback like camera shake.
- Fix: Keep
Entity.OnDamageTakenasprotected virtual, and implementprotected overrideinPlayerStats.
- File:
Assets/_Scripts/PlayerController.cs:15-19 - Issue: Uses
stats.rbinPlayerController.Awake, butstats.rbis assigned inPlayerStats.Awake. - Impact: Intermittent null refs depending on component Awake order.
- Fix: Initialize Rigidbody/Animator references in one place, move PlayerController rb usage to
Start, or enforce script execution order.
- File:
Assets/_Scripts/Entity.cs:61-68,Assets/_Scripts/Entity.cs:76-77 - Issues:
animator.SetBool("isDead", true)can null-ref if animator missing.healthBar.Change(amount)is called without checkinghealthBar != null.
- Fix: Add guards or enforce requirements via
[RequireComponent]/ validation.
- File:
Assets/_Scripts/Entity.cs:74-77 - Issue:
if(currentHealth != MaxHealth) healthBar.Change(amount);can skip updating UI on the last heal tick that reaches full. - Fix: Compute actual healed amount and apply it consistently; always null-check
healthBar.
- File:
Assets/_Scripts/Projectile.cs:13-15 - Issue: Can null-ref if a projectile prefab is missing a
SpriteRenderer. - Fix: Guard
sror require it via[RequireComponent(typeof(SpriteRenderer))].
- Files:
Assets/_Scripts/EnemyMovement.cs:23-25(FindGameObjectWithTag("Player").transformwithout null check)Assets/_Scripts/Chaser.cs:69-75(other.GetComponent<PlayerStats>()without null check)
- Fix: Check for null and fail gracefully.
- File:
Assets/_Scripts/WaveManager.cs:34 - Issue:
WaitUntil(() => GameObject.FindGameObjectsWithTag("Enemy").Length == 0)allocates/scans every frame. - Fix: Maintain an enemy counter (increment on spawn, decrement on enemy death) or use events.
- File:
Assets/_Scripts/GameManager.cs:92-100 - Issue: Full-scene tag scans for enemies and bullets.
- Fix: Track spawned entities/bullets in lists, or pool projectiles.
- Files:
Assets/_Scripts/PlayerShooting.cs:53-76Assets/_Scripts/EnemyShooting.cs:34-47Assets/_Scripts/Projectile.cs:18-19,41-42Assets/_Scripts/Background.cs:47-53
- Fix: Use object pooling (
UnityEngine.Pool.ObjectPool<T>) for bullets/tiles.
- Files:
Assets/_Scripts/Scriptable Objects/Power Ups/DamageBoostPowerUp.cs:7-27Assets/_Scripts/Scriptable Objects/Power Ups/CriticalSurgePowerUp.cs:9-23Assets/_Scripts/Scriptable Objects/Power Ups/PowerCoreSyncPowerUp.cs:7-24Assets/_Scripts/Scriptable Objects/Power Ups/RapidFirePowerup.cs:9-21Assets/_Scripts/Scriptable Objects/Power Ups/JuggernautPowerup.cs:8-47
- Issue: Static/instance mutable fields on ScriptableObjects can leak across runs/players and behave oddly with domain reload settings.
- Suggestion: Treat ScriptableObjects as immutable config; keep active state on the player (component) with timers.
- File:
Assets/_Scripts/PlayerPowerupHandler.cs:27-35,53-61 - Issue: Removing “oldest” clears slot 0 but doesn’t shift slot 1 down; stored
slotIndexcan become stale after list mutations. - Suggestion: Derive UI slots from current list each time (render function) or maintain stable slot assignment.
- Files:
Assets/_Scripts/EnemyShooting.cs:51Assets/_Scripts/Background.cs:40
- Suggestion: Cache camera reference once in
Awake/Start.
- Fix scattershot stat injection (
PlayerShooting+PlayerBulletnull guards). - Fix shield duration semantics (realtime vs scaled time) and add a safety net force-disable.
- Fix prefab mutation damage scaling (
EnemySpawner). - Fix method hiding / damage feedback polymorphism (
Entity/PlayerStats). - Remove per-frame tag scans in wave loop (
WaveManager) and consider pooling.