fixed UI spacing, fixed enemy spawner bug when enemies are disabled
This commit is contained in:
@@ -13455,7 +13455,7 @@ PrefabInstance:
|
|||||||
- target: {fileID: 1322085931752940309, guid: 62f5a4342bff4fc479aeeb8f946c8e59,
|
- target: {fileID: 1322085931752940309, guid: 62f5a4342bff4fc479aeeb8f946c8e59,
|
||||||
type: 3}
|
type: 3}
|
||||||
propertyPath: m_AnchoredPosition.y
|
propertyPath: m_AnchoredPosition.y
|
||||||
value: 0.000035671357
|
value: -0.00086940586
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 1322085931763303459, guid: 62f5a4342bff4fc479aeeb8f946c8e59,
|
- target: {fileID: 1322085931763303459, guid: 62f5a4342bff4fc479aeeb8f946c8e59,
|
||||||
type: 3}
|
type: 3}
|
||||||
@@ -18997,7 +18997,7 @@ MonoBehaviour:
|
|||||||
deselectHighlightColor: {r: 0, g: 0, b: 0, a: 1}
|
deselectHighlightColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
highlightFadeDuration: 0.3
|
highlightFadeDuration: 0.3
|
||||||
preferSkinnedMeshRenderer: 1
|
preferSkinnedMeshRenderer: 1
|
||||||
autoLockSelectedTarget: 0
|
autoLockSelectedTarget: 1
|
||||||
targetLockSystem: {fileID: 0}
|
targetLockSystem: {fileID: 0}
|
||||||
manualSwitchCooldownDuration: 0.75
|
manualSwitchCooldownDuration: 0.75
|
||||||
--- !u!4 &6425420852750441961 stripped
|
--- !u!4 &6425420852750441961 stripped
|
||||||
@@ -24293,7 +24293,7 @@ PrefabInstance:
|
|||||||
- target: {fileID: 1549731741953142080, guid: 38ca8b4bc26702b40a70a342950990ee,
|
- target: {fileID: 1549731741953142080, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
type: 3}
|
type: 3}
|
||||||
propertyPath: m_IsActive
|
propertyPath: m_IsActive
|
||||||
value: 0
|
value: 1
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 1613961274298732141, guid: 38ca8b4bc26702b40a70a342950990ee,
|
- target: {fileID: 1613961274298732141, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
type: 3}
|
type: 3}
|
||||||
@@ -24305,6 +24305,11 @@ PrefabInstance:
|
|||||||
propertyPath: disableObjectOnEmpty
|
propertyPath: disableObjectOnEmpty
|
||||||
value: 1
|
value: 1
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 1714913128145838672, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
|
type: 3}
|
||||||
|
propertyPath: m_AnchoredPosition.y
|
||||||
|
value: -3.5100076
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 1771614490565708014, guid: 38ca8b4bc26702b40a70a342950990ee,
|
- target: {fileID: 1771614490565708014, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
type: 3}
|
type: 3}
|
||||||
propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.size
|
propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.size
|
||||||
@@ -26475,6 +26480,11 @@ PrefabInstance:
|
|||||||
propertyPath: m_AnchoredPosition.y
|
propertyPath: m_AnchoredPosition.y
|
||||||
value: 383
|
value: 383
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6241634227694161650, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
|
type: 3}
|
||||||
|
propertyPath: m_AnchoredPosition.y
|
||||||
|
value: -6.95
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 6365562694919996555, guid: 38ca8b4bc26702b40a70a342950990ee,
|
- target: {fileID: 6365562694919996555, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
type: 3}
|
type: 3}
|
||||||
propertyPath: m_Layer
|
propertyPath: m_Layer
|
||||||
@@ -26485,6 +26495,21 @@ PrefabInstance:
|
|||||||
propertyPath: m_Layer
|
propertyPath: m_Layer
|
||||||
value: 5
|
value: 5
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6486847052756301725, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
|
type: 3}
|
||||||
|
propertyPath: m_LocalPosition.z
|
||||||
|
value: -1.3131022
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6486847052756301725, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
|
type: 3}
|
||||||
|
propertyPath: m_AnchoredPosition.x
|
||||||
|
value: 15.71
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6486847052756301725, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
|
type: 3}
|
||||||
|
propertyPath: m_AnchoredPosition.y
|
||||||
|
value: -5.210007
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 6492528895270899910, guid: 38ca8b4bc26702b40a70a342950990ee,
|
- target: {fileID: 6492528895270899910, guid: 38ca8b4bc26702b40a70a342950990ee,
|
||||||
type: 3}
|
type: 3}
|
||||||
propertyPath: m_Layer
|
propertyPath: m_Layer
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ namespace Beyond
|
|||||||
public UnityEvent<EnemySpawner> m_onReceivedDamage; // Invoked when an enemy from THIS spawner receives damage
|
public UnityEvent<EnemySpawner> m_onReceivedDamage; // Invoked when an enemy from THIS spawner receives damage
|
||||||
|
|
||||||
private SaveData m_spawnData;
|
private SaveData m_spawnData;
|
||||||
private List<vControlAI> m_activeEnemies = new List<vControlAI>();
|
private List<vControlAI> m_activeEnemies = new List<vControlAI>(); // Initialized here
|
||||||
private bool _initialWaveCheckPassedThisActivation = false; // Flag to control CheckSpawn behavior per activation cycle
|
private bool _initialWaveCheckPassedThisActivation = false;
|
||||||
|
|
||||||
const float RAY_Y_OFFSET = 30f; // How high above candidate point to start raycast
|
const float RAY_Y_OFFSET = 30f; // How high above candidate point to start raycast
|
||||||
const float GROUND_Y_OFFSET = .1f; // How high above ground to place enemy pivot
|
const float GROUND_Y_OFFSET = .1f; // How high above ground to place enemy pivot
|
||||||
@@ -52,20 +52,41 @@ namespace Beyond
|
|||||||
{
|
{
|
||||||
#if ENEMIES_DISABLED && UNITY_EDITOR
|
#if ENEMIES_DISABLED && UNITY_EDITOR
|
||||||
enabled = false;
|
enabled = false;
|
||||||
return;
|
Debug.LogWarning($"EnemySpawner ({name}): Disabled via ENEMIES_DISABLED directive in Awake. Critical members like m_spawnData will not be initialized by Awake if this return is hit.", this);
|
||||||
|
return; // If this path is taken, m_spawnData, m_activeEnemies, etc., below are NOT initialized by Awake.
|
||||||
#endif
|
#endif
|
||||||
base.Awake(); // Important: Call Saver's Awake
|
base.Awake(); // Important: Call Saver's Awake
|
||||||
|
|
||||||
|
// Initialize critical members
|
||||||
m_spawnData = new SaveData
|
m_spawnData = new SaveData
|
||||||
{
|
{
|
||||||
wavesSpawnedCount = 0,
|
wavesSpawnedCount = 0,
|
||||||
waveActive = false
|
waveActive = false
|
||||||
};
|
};
|
||||||
m_activeEnemies = new List<vControlAI>();
|
// m_activeEnemies is already initialized at declaration, but ensuring it if not:
|
||||||
|
if (m_activeEnemies == null) m_activeEnemies = new List<vControlAI>();
|
||||||
_initialWaveCheckPassedThisActivation = false; // Initialize flag
|
_initialWaveCheckPassedThisActivation = false; // Initialize flag
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
|
// Defensive initialization: If Awake() didn't initialize m_spawnData
|
||||||
|
// (e.g., due to ENEMIES_DISABLED causing Awake to return early,
|
||||||
|
// or an exception in base.Awake()), and this component was
|
||||||
|
// subsequently enabled (or was already enabled) causing Start() to run.
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"EnemySpawner ({name}): m_spawnData was null at the beginning of Start(). Initializing now. This might indicate an issue with Awake's execution path (e.g., ENEMIES_DISABLED directive was met and component re-enabled, or an exception in base.Awake()).", this);
|
||||||
|
m_spawnData = new SaveData { wavesSpawnedCount = 0, waveActive = false };
|
||||||
|
|
||||||
|
// Ensure other members typically initialized in Awake are also safe
|
||||||
|
if (m_activeEnemies == null)
|
||||||
|
{
|
||||||
|
m_activeEnemies = new List<vControlAI>();
|
||||||
|
}
|
||||||
|
_initialWaveCheckPassedThisActivation = false;
|
||||||
|
}
|
||||||
|
|
||||||
base.Start(); // Important: Call Saver's Start
|
base.Start(); // Important: Call Saver's Start
|
||||||
|
|
||||||
// Set up m_distanceFrom
|
// Set up m_distanceFrom
|
||||||
@@ -77,7 +98,7 @@ namespace Beyond
|
|||||||
{
|
{
|
||||||
Debug.LogError($"EnemySpawner ({name}): m_distanceFrom could not be set (Player.Instance and Camera.main are null). Disabling spawner.", this);
|
Debug.LogError($"EnemySpawner ({name}): m_distanceFrom could not be set (Player.Instance and Camera.main are null). Disabling spawner.", this);
|
||||||
enabled = false;
|
enabled = false;
|
||||||
return;
|
// If disabled here, InvokeRepeating below won't be set up.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to player respawn events
|
// Subscribe to player respawn events
|
||||||
@@ -94,8 +115,15 @@ namespace Beyond
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start checking for spawn conditions
|
// Start checking for spawn conditions only if the component is still enabled and active
|
||||||
InvokeRepeating("CheckSpawn", m_spawnCheckInterval + (Random.value * m_spawnCheckInterval), m_spawnCheckInterval);
|
if (enabled && gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
InvokeRepeating("CheckSpawn", m_spawnCheckInterval + (Random.value * m_spawnCheckInterval), m_spawnCheckInterval);
|
||||||
|
}
|
||||||
|
else if (!enabled)
|
||||||
|
{
|
||||||
|
Debug.Log($"EnemySpawner ({name}): Spawner is disabled at the end of Start(). CheckSpawn will not be invoked repeatedly.", this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDestroy()
|
void OnDestroy()
|
||||||
@@ -111,15 +139,19 @@ namespace Beyond
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clean up listeners from active enemies
|
// Clean up listeners from active enemies
|
||||||
foreach (var ai in m_activeEnemies)
|
// Ensure m_activeEnemies is not null before iterating
|
||||||
|
if (m_activeEnemies != null)
|
||||||
{
|
{
|
||||||
if (ai != null)
|
foreach (var ai in m_activeEnemies)
|
||||||
{
|
{
|
||||||
ai.onDead.RemoveListener(OnEnemyDead);
|
if (ai != null)
|
||||||
ai.onReceiveDamage.RemoveListener(OnEnemyReceiveDamage);
|
{
|
||||||
|
ai.onDead.RemoveListener(OnEnemyDead);
|
||||||
|
ai.onReceiveDamage.RemoveListener(OnEnemyReceiveDamage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
m_activeEnemies.Clear();
|
||||||
}
|
}
|
||||||
m_activeEnemies.Clear();
|
|
||||||
|
|
||||||
CancelInvoke("CheckSpawn"); // Ensure InvokeRepeating is stopped
|
CancelInvoke("CheckSpawn"); // Ensure InvokeRepeating is stopped
|
||||||
}
|
}
|
||||||
@@ -129,54 +161,71 @@ namespace Beyond
|
|||||||
{
|
{
|
||||||
if (!m_respawnOnPlayersDeath) return;
|
if (!m_respawnOnPlayersDeath) return;
|
||||||
|
|
||||||
|
// Ensure m_spawnData is not null
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_spawnData is null in OnPlayerRespawned. Cannot proceed with respawn logic.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_activeEnemies == null) m_activeEnemies = new List<vControlAI>(); // Should be initialized, but defensive
|
||||||
|
|
||||||
|
|
||||||
// If a wave was previously active and its enemies are now gone, mark the wave as inactive.
|
// If a wave was previously active and its enemies are now gone, mark the wave as inactive.
|
||||||
if (m_spawnData.waveActive && m_activeEnemies.Count == 0)
|
if (m_spawnData.waveActive && m_activeEnemies.Count == 0)
|
||||||
{
|
{
|
||||||
m_spawnData.waveActive = false;
|
m_spawnData.waveActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the flag to allow CheckSpawn (or direct spawn here) to work for this new "life"
|
|
||||||
_initialWaveCheckPassedThisActivation = false;
|
_initialWaveCheckPassedThisActivation = false;
|
||||||
|
|
||||||
// Attempt to spawn a new wave if conditions are met
|
|
||||||
if (!m_spawnData.waveActive && CanSpawnMoreWaves())
|
if (!m_spawnData.waveActive && CanSpawnMoreWaves())
|
||||||
{
|
{
|
||||||
if (IsPlayerInRange())
|
if (IsPlayerInRange())
|
||||||
{
|
{
|
||||||
SpawnWave();
|
SpawnWave();
|
||||||
if (m_spawnData.waveActive) // If wave was successfully spawned by this call
|
if (m_spawnData.waveActive)
|
||||||
{
|
{
|
||||||
_initialWaveCheckPassedThisActivation = true; // Mark as done for this cycle
|
_initialWaveCheckPassedThisActivation = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else Debug.Log($"Player respawned, but spawner {name} is out of range. No wave spawned immediately by OnPlayerRespawned.");
|
|
||||||
}
|
}
|
||||||
// else if (m_spawnData.waveActive) Debug.Log($"Player respawned, but spawner {name} still has an active wave.");
|
|
||||||
// else if (!CanSpawnMoreWaves()) Debug.Log($"Player respawned, but spawner {name} has reached its wave limit.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string RecordData()
|
public override string RecordData()
|
||||||
{
|
{
|
||||||
|
// Ensure m_spawnData is not null before serializing
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"EnemySpawner ({name}): Attempting to record data, but m_spawnData is null. Returning empty string.", this);
|
||||||
|
// Optionally, initialize it here to a default state if that makes sense for your save system logic
|
||||||
|
// m_spawnData = new SaveData { wavesSpawnedCount = 0, waveActive = false };
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
return SaveSystem.Serialize(m_spawnData);
|
return SaveSystem.Serialize(m_spawnData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ApplyData(string s)
|
public override void ApplyData(string s)
|
||||||
{
|
{
|
||||||
|
// Ensure m_spawnData is initialized before trying to apply data,
|
||||||
|
// though Awake or Start should have done this.
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
m_spawnData = new SaveData { wavesSpawnedCount = 0, waveActive = false };
|
||||||
|
Debug.LogWarning($"EnemySpawner ({name}): m_spawnData was null when ApplyData was called. Initialized to default.", this);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(s)) return; // No data to apply
|
||||||
|
|
||||||
var data = SaveSystem.Deserialize<SaveData>(s);
|
var data = SaveSystem.Deserialize<SaveData>(s);
|
||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
m_spawnData = data;
|
m_spawnData = data;
|
||||||
// If loaded save indicates a wave was active but no enemies are present (they weren't saved/restored),
|
if (m_activeEnemies == null) m_activeEnemies = new List<vControlAI>(); // Defensive
|
||||||
// mark wave as inactive.
|
|
||||||
if (m_spawnData.waveActive && m_activeEnemies.Count == 0) // m_activeEnemies is runtime only
|
if (m_spawnData.waveActive && m_activeEnemies.Count == 0)
|
||||||
{
|
{
|
||||||
m_spawnData.waveActive = false;
|
m_spawnData.waveActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the state of _initialWaveCheckPassedThisActivation based on loaded data
|
|
||||||
// If a wave was active, or any waves had been spawned, assume the "initial check" for the
|
|
||||||
// current game session (before this load) effectively passed.
|
|
||||||
// A Reset or PlayerRespawn will clear this flag if needed for a new cycle.
|
|
||||||
if (m_spawnData.waveActive || m_spawnData.wavesSpawnedCount > 0) {
|
if (m_spawnData.waveActive || m_spawnData.wavesSpawnedCount > 0) {
|
||||||
_initialWaveCheckPassedThisActivation = true;
|
_initialWaveCheckPassedThisActivation = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -187,6 +236,7 @@ namespace Beyond
|
|||||||
|
|
||||||
public bool IsAnyEnemyAlive()
|
public bool IsAnyEnemyAlive()
|
||||||
{
|
{
|
||||||
|
if (m_activeEnemies == null) return false;
|
||||||
return m_activeEnemies.Count > 0;
|
return m_activeEnemies.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +256,8 @@ namespace Beyond
|
|||||||
{
|
{
|
||||||
return m_spawnData.wavesSpawnedCount;
|
return m_spawnData.wavesSpawnedCount;
|
||||||
}
|
}
|
||||||
return 0; // Should ideally not happen if Awake ran
|
Debug.LogWarning($"EnemySpawner ({name}): GetWavesSpawnedCount called but m_spawnData is null. Returning 0.", this);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SpawnWave()
|
public void SpawnWave()
|
||||||
@@ -217,16 +268,21 @@ namespace Beyond
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure m_spawnData is not null
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_spawnData is null in SpawnWave. Cannot proceed.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_activeEnemies == null) m_activeEnemies = new List<vControlAI>(); // Defensive
|
||||||
|
|
||||||
if (!CanSpawnMoreWaves())
|
if (!CanSpawnMoreWaves())
|
||||||
{
|
{
|
||||||
// This check is also in CheckSpawn/OnPlayerRespawned, but good for direct calls too
|
|
||||||
// Debug.Log($"EnemySpawner ({name}): Max wave respawn limit reached or cannot spawn more waves.", this);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_spawnData.waveActive)
|
if (m_spawnData.waveActive)
|
||||||
{
|
{
|
||||||
// Debug.Log($"EnemySpawner ({name}): Wave already active, not spawning another.", this);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,20 +343,31 @@ namespace Beyond
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No enemies were spawned in this attempt, so the wave isn't truly "active".
|
|
||||||
// And we shouldn't count it towards wavesSpawnedCount if nothing came out.
|
|
||||||
m_spawnData.waveActive = false;
|
m_spawnData.waveActive = false;
|
||||||
Debug.LogWarning($"EnemySpawner ({name}): Attempted to spawn a wave, but no enemies were successfully placed.", this);
|
Debug.LogWarning($"EnemySpawner ({name}): Attempted to spawn a wave, but no enemies were successfully placed.", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEnemyReceiveDamage(vDamage damageData) // Assuming vDamage from Invector
|
private void OnEnemyReceiveDamage(vDamage damageData)
|
||||||
{
|
{
|
||||||
m_onReceivedDamage?.Invoke(this);
|
m_onReceivedDamage?.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEnemyDead(GameObject deadEnemyObject)
|
private void OnEnemyDead(GameObject deadEnemyObject)
|
||||||
{
|
{
|
||||||
|
// Ensure m_spawnData and m_activeEnemies are not null
|
||||||
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_spawnData is null in OnEnemyDead.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_activeEnemies == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_activeEnemies is null in OnEnemyDead.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
vControlAI deadAI = deadEnemyObject.GetComponent<vControlAI>();
|
vControlAI deadAI = deadEnemyObject.GetComponent<vControlAI>();
|
||||||
if (deadAI != null && m_activeEnemies.Contains(deadAI))
|
if (deadAI != null && m_activeEnemies.Contains(deadAI))
|
||||||
{
|
{
|
||||||
@@ -312,22 +379,19 @@ namespace Beyond
|
|||||||
|
|
||||||
if (m_activeEnemies.Count == 0 && m_spawnData.waveActive)
|
if (m_activeEnemies.Count == 0 && m_spawnData.waveActive)
|
||||||
{
|
{
|
||||||
m_spawnData.waveActive = false; // Mark wave as inactive
|
m_spawnData.waveActive = false;
|
||||||
// DO NOT call CheckSpawn() here to prevent immediate respawn.
|
|
||||||
// DO NOT reset _initialWaveCheckPassedThisActivation here. That's for new cycles (player respawn/reset).
|
|
||||||
Debug.Log($"EnemySpawner ({name}): All enemies in wave died. Wave inactive. Waiting for player respawn or manual reset.", this);
|
Debug.Log($"EnemySpawner ({name}): All enemies in wave died. Wave inactive. Waiting for player respawn or manual reset.", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPlayerInRange()
|
private bool IsPlayerInRange()
|
||||||
{
|
{
|
||||||
if (m_distanceFrom == null) // Should be set in Start, but as a fallback
|
if (m_distanceFrom == null)
|
||||||
{
|
{
|
||||||
if (Player.Instance != null) m_distanceFrom = Player.Instance.transform;
|
if (Player.Instance != null) m_distanceFrom = Player.Instance.transform;
|
||||||
else if (Camera.main != null) m_distanceFrom = Camera.main.transform;
|
else if (Camera.main != null) m_distanceFrom = Camera.main.transform;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Debug.LogWarning($"EnemySpawner ({name}): Cannot check range, m_distanceFrom is null.", this);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,46 +402,62 @@ namespace Beyond
|
|||||||
{
|
{
|
||||||
if (!enabled || !gameObject.activeInHierarchy || m_prefab == null) return;
|
if (!enabled || !gameObject.activeInHierarchy || m_prefab == null) return;
|
||||||
|
|
||||||
// If the "initial" spawn for this activation cycle has already happened (or attempted),
|
// Critical Safety check: If m_spawnData is somehow still null here
|
||||||
// CheckSpawn should not try again until the cycle is reset (by player death or ResetSpawner).
|
if (m_spawnData == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_spawnData is unexpectedly null in CheckSpawn! This indicates a severe initialization problem. Cancelling spawn checks for this spawner to prevent further errors.", this);
|
||||||
|
CancelInvoke("CheckSpawn"); // Stop trying to check
|
||||||
|
enabled = false; // Disable the component to be safe
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ensure m_activeEnemies is not null (should be guaranteed by declaration/Awake/Start)
|
||||||
|
if (m_activeEnemies == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"EnemySpawner ({name}): m_activeEnemies is unexpectedly null in CheckSpawn! Reinitializing. This is unusual.", this);
|
||||||
|
m_activeEnemies = new List<vControlAI>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_initialWaveCheckPassedThisActivation)
|
if (_initialWaveCheckPassedThisActivation)
|
||||||
{
|
{
|
||||||
// If a wave is active, great, CheckSpawn's job for this cycle is done.
|
|
||||||
// If no wave is active BUT wavesSpawnedCount > 0, it means the wave spawned by this cycle died.
|
|
||||||
// In this case, CheckSpawn also shouldn't immediately respawn.
|
|
||||||
// It waits for OnPlayerRespawned or ResetSpawner to clear the flag.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to spawn if: no wave active, can spawn more, player in range, AND initial check hasn't passed.
|
// This is the line (or one like it) that likely caused the original NRE if m_spawnData was null
|
||||||
if (!m_spawnData.waveActive && CanSpawnMoreWaves() && IsPlayerInRange())
|
if (!m_spawnData.waveActive && CanSpawnMoreWaves() && IsPlayerInRange())
|
||||||
{
|
{
|
||||||
SpawnWave();
|
SpawnWave(); // SpawnWave itself has null checks for m_spawnData now
|
||||||
if (m_spawnData.waveActive) // If wave was successfully spawned
|
// Check m_spawnData again as SpawnWave might have failed to initialize it if an error occurred there (though unlikely with current SpawnWave structure)
|
||||||
|
if (m_spawnData != null && m_spawnData.waveActive)
|
||||||
{
|
{
|
||||||
_initialWaveCheckPassedThisActivation = true; // Mark as done for this cycle
|
_initialWaveCheckPassedThisActivation = true;
|
||||||
}
|
}
|
||||||
// If SpawnWave failed to actually spawn anyone (e.g. no valid spots),
|
|
||||||
// waveActive will be false, and _initialWaveCheckPassedThisActivation will remain false,
|
|
||||||
// allowing CheckSpawn to try again on the next interval.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetSpawner()
|
public void ResetSpawner()
|
||||||
{
|
{
|
||||||
CancelInvoke("CheckSpawn"); // Stop existing repeating call
|
CancelInvoke("CheckSpawn");
|
||||||
|
|
||||||
// Destroy active enemies
|
// Destroy active enemies
|
||||||
for (int i = m_activeEnemies.Count - 1; i >= 0; i--)
|
if (m_activeEnemies != null)
|
||||||
{
|
{
|
||||||
if (m_activeEnemies[i] != null)
|
for (int i = m_activeEnemies.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
m_activeEnemies[i].onDead.RemoveListener(OnEnemyDead);
|
if (m_activeEnemies[i] != null)
|
||||||
m_activeEnemies[i].onReceiveDamage.RemoveListener(OnEnemyReceiveDamage);
|
{
|
||||||
Destroy(m_activeEnemies[i].gameObject);
|
m_activeEnemies[i].onDead.RemoveListener(OnEnemyDead);
|
||||||
|
m_activeEnemies[i].onReceiveDamage.RemoveListener(OnEnemyReceiveDamage);
|
||||||
|
Destroy(m_activeEnemies[i].gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
m_activeEnemies.Clear();
|
||||||
}
|
}
|
||||||
m_activeEnemies.Clear();
|
else
|
||||||
|
{
|
||||||
|
m_activeEnemies = new List<vControlAI>(); // Ensure it's not null for future use
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Reset spawn data
|
// Reset spawn data
|
||||||
if (m_spawnData != null)
|
if (m_spawnData != null)
|
||||||
@@ -385,17 +465,16 @@ namespace Beyond
|
|||||||
m_spawnData.wavesSpawnedCount = 0;
|
m_spawnData.wavesSpawnedCount = 0;
|
||||||
m_spawnData.waveActive = false;
|
m_spawnData.waveActive = false;
|
||||||
}
|
}
|
||||||
else // Should not happen if Awake ran
|
else
|
||||||
{
|
{
|
||||||
m_spawnData = new SaveData { wavesSpawnedCount = 0, waveActive = false };
|
m_spawnData = new SaveData { wavesSpawnedCount = 0, waveActive = false };
|
||||||
|
Debug.LogWarning($"EnemySpawner ({name}): m_spawnData was null during ResetSpawner. Initialized to default.", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initialWaveCheckPassedThisActivation = false; // Reset the flag for a new cycle
|
_initialWaveCheckPassedThisActivation = false;
|
||||||
|
|
||||||
// Restart InvokeRepeating if spawner is active
|
|
||||||
if (enabled && gameObject.activeInHierarchy)
|
if (enabled && gameObject.activeInHierarchy)
|
||||||
{
|
{
|
||||||
// Re-initialize m_distanceFrom
|
|
||||||
if (Player.Instance != null) m_distanceFrom = Player.Instance.transform;
|
if (Player.Instance != null) m_distanceFrom = Player.Instance.transform;
|
||||||
else if (Camera.main != null) m_distanceFrom = Camera.main.transform;
|
else if (Camera.main != null) m_distanceFrom = Camera.main.transform;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user