- Home /
When the player dies and I reload the scene the enemies spawn script stops working.
This is my first game and I'm trying to reset the game after player death, i already made the ui and all the functions but for some reason after the player dies and reloads the scene the enemy manager stops respawning my enemy prefab after I kill it once. The error message appears when I try to instantiate the new enemy. The error message is. "MissingReferenceException: The object of type 'EnemyManager' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. EnemyManager.spawnNewEnemy () (at Assets/EnemyManager.cs:35) target.g__Die|26_0 () (at Assets/target.cs:116) target.TakeDamage (System.Single amount) (at Assets/target.cs:103) gun.Shoot () (at Assets/gun.cs:59) gun.Update () (at Assets/gun.cs:34)"
target.cs
public class target : MonoBehaviour
{
public float health = 30f;
public NavMeshAgent agent;
public Transform player;
public LayerMask whatIsGround, whatIsPlayer;
public HealthbarEnemy healthbar;
public delegate void EnemyKilled();
public static event EnemyKilled OnEnemyKilled;
//Patroling
public Vector3 walkPoint;
bool walkPointset;
public float walkPointRange;
//Attacking
public float timeBetweenAttacks;
public float agentDamage = 50f;
bool alreadyAttacked;
//State
public float sightRange, attackRange;
public bool playerInSightRange, PlayerInAttackRange;
private void Awake()
{
player = GameObject.Find("First Person Player").transform;
agent = GetComponent<NavMeshAgent>();
}
public void Patroling()
{
if (!walkPointset)
{
SearchWalkPoint();
}
if (walkPointset)
{
agent.SetDestination(walkPoint);
}
Vector3 distaneTowalkPoint = transform.position - walkPoint;
if (distaneTowalkPoint.magnitude < 1f)
{
walkPointset = false;
}
}
private void SearchWalkPoint()
{
float randomZ = Random.Range(-walkPointRange, walkPointRange);
float randomX = Random.Range(-walkPointRange, walkPointRange);
walkPoint = new Vector3(transform.position.x + randomX, transform.position.y,
transform.position.z + randomZ);
if (Physics.Raycast(walkPoint, -transform.up, 2f, whatIsGround))
{
walkPointset = true;
}
}
public void ChasePlayer()
{
agent.SetDestination(player.position);
}
public void AttackPlayer()
{
agent.SetDestination(transform.position);
transform.LookAt(player);
if (!alreadyAttacked)
{
//attack code here
player.GetComponent<playerDamage>().damagePlayer(agentDamage);
alreadyAttacked = true;
Invoke(nameof(ResetAttack), timeBetweenAttacks);
}
}
private void ResetAttack()
{
alreadyAttacked = false;
}
public void TakeDamage (float amount)
{
health -= amount;
healthbar.SetHealth(health);
if(health <= 0f)
{
Die();
}
void Die()
{
Debug.Log("this needs to be excuted");
if (gameObject != null)
{
Debug.Log("this needs to be excuted");
Destroy(gameObject);
Debug.Log("this was excuted successfully");
}
Debug.Log("this needs to be excuted");
OnEnemyKilled();
Debug.Log("this was excuted successfully");
}
}
private void Update()
{
player = GameObject.Find("First Person Player").transform;
agent = GetComponent<NavMeshAgent>();
//Check for sight and attack range
playerInSightRange = Physics.CheckSphere(transform.position, sightRange, whatIsPlayer);
PlayerInAttackRange = Physics.CheckSphere(transform.position, attackRange, whatIsPlayer);
if(!playerInSightRange && !PlayerInAttackRange)
{
Patroling();
}
if (health < 30f)
{
ChasePlayer();
}
if (playerInSightRange && !PlayerInAttackRange)
{
ChasePlayer();
}
if (playerInSightRange && PlayerInAttackRange)
{
AttackPlayer();
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, sightRange);
}
}
gun.cs
public class gun : MonoBehaviour
{
public float damage = 10f;
public float range = 100f;
public float fireRate = 15f;
public float impactForce = 3000f;
public AudioSource soundSource;
public AudioClip gunschot;
public Camera fpsCam;
public ParticleSystem muzzleFlash;
public GameObject impactEffectBlood;
public GameObject impactEffectGround;
public pausmenu pausmenu;
private float nextTimeToFire = 0f;
void Start()
{
soundSource = GetComponent<AudioSource>();
impactEffectBlood.SetActive(true);
impactEffectGround.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1") && Time.time >= nextTimeToFire && !pausmenu.GameIsPaused)
{
nextTimeToFire = Time.time + 1f / fireRate;
Shoot();
}
}
void playgunsound ()
{
soundSource.clip = gunschot;
soundSource.PlayOneShot(soundSource.clip);
}
void Shoot()
{
playgunsound();
muzzleFlash.Play();
RaycastHit hit;
if(Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
target target = hit.transform.GetComponent<target>();
if(target != null)
{
target.TakeDamage(damage);
}
if (hit.rigidbody != null)
{
hit.rigidbody.AddForce(-hit.normal * impactForce);
}
if (hit.transform.name == "Enemy")
{
GameObject impactGO = Instantiate(impactEffectBlood, hit.point,
Quaternion.LookRotation(hit.normal));
Destroy(impactGO, 2f);
}
if (hit.transform.tag == "ground")
{
GameObject impactGO = Instantiate(impactEffectGround, hit.point,
Quaternion.LookRotation(hit.normal));
Destroy(impactGO, 2f);
}
}
}
}
EnemyManager.cs
public class EnemyManager : MonoBehaviour
{
public Transform spawnpoint;
public GameObject EnemyPrefab;
// Start is called before the first frame update
void Start()
{
Debug.Log("this needs to be excuted");
spawnNewEnemy();
Debug.Log("this was excuted successfully");
}
void OnEnable()
{
Debug.Log("this needs to be excuted");
target.OnEnemyKilled += spawnNewEnemy;
Debug.Log("this was excuted successfully");
}
// Update is called once per frame
void Update()
{
}
void spawnNewEnemy()
{
Debug.Log("this needs to be excuted");
if (transform != null)
{
Debug.Log("this needs to be excuted");
Debug.Log(transform);
Instantiate(EnemyPrefab, spawnpoint.transform.position, Quaternion.Euler(Vector3.zero));
Debug.Log("this was excuted successfully");
} else
{
Debug.Log("ENEMYMANAGER.TRANSFORM = NULL");
}
}
}
Answer by Ghosthunter_007 · Oct 05, 2021 at 07:59 AM
@simiel7 Thanks I will definitely read up on that. But i never call Destroy() on the enemy manager object. I can show you more of my code.
this is all the code that's involved with the scene load. (Plus the code from before of course)
public class GameOver : MonoBehaviour
{
public void PlayAgain()
{
Debug.Log("this needs to be excuted");
SceneManager.LoadScene(1, LoadSceneMode.Single);
Debug.Log("this was excuted successfully");
}
public void BackToMain()
{
Debug.Log("this needs to be excuted");
SceneManager.LoadScene(0, LoadSceneMode.Single);
Debug.Log("this was excuted successfully");
}
public void QuitGame()
{
Debug.Log("quit");
Application.Quit();
}
}
public class playerDamage : MonoBehaviour
{
public float healthplayer = 100f;
public float currentHealth;
public Healthbar healthbar;
public GameObject GameOverUI;
public pausmenu pausmenu;
void Start()
{
currentHealth = healthplayer;
healthbar.SetMaxHealth(healthplayer);
}
public void damagePlayer(float amount)
{
currentHealth = healthplayer -= amount;
healthbar.SetHealth(currentHealth);
if (currentHealth > 0)
{
Debug.Log("you took damage, your health is now:" + currentHealth);
}
if (currentHealth <= 0f)
{
Die();
}
void Die()
{
Debug.Log("you took health has been depleted you are dead");
Cursor.lockState = CursorLockMode.Confined;
GameOverUI.SetActive(true);
Time.timeScale = 0f;
}
}
}
I've also tried to fix the problem by adding some lines that refer to both the spawnpoint object and the enemy prefab in the start and spawn new enemy functions but that made the game spawn one extra enemy for every time I run the game over sequence. Other than that I haven't changed anything.
You have this line of code in the EnemyManager:
void OnEnable()
{
Debug.Log("this needs to be excuted");
target.OnEnemyKilled += spawnNewEnemy;
Debug.Log("this was excuted successfully");
}
Could you try to add target.OnEnemyKilled -= spawnNewEnemy; into the OnDisable() method, for example:
void OnDisable()
{
Debug.Log("this needs to be excuted");
target.OnEnemyKilled -= spawnNewEnemy;
Debug.Log("this was excuted successfully");
}
Either way if it not fix this issue, you should always remove the delegation reference or event listeners if you add them, because it can cause issues later, also it will cause memory leak. So a best practice is if you use xyz.AddListener(...) or xyz += something, then you should think about the de-registration of them, like in the OnDestroy() or OnDisable() or where it is fit into your logic.
Maybe you have luck with it and this is your problem too. And try to revert back the additional code you added as a temporary fix.
Under the hood, I think when the scene loads, it will leave the previous registration as is (because you did not de-registered it) and your logic will just add a new one with the target.OnEnemyKilled += spawnNewEnemy;, but technically, your first version is not exists anymore. So if you remove the first one as I mentioned it can solve this problem.
And when you call OnEnemyKilled(); in the target class, it will iterate through all the registered delegation and the first one is null, because the scene was unloaded and that specific gameobject was destroyed automatically. This is why you need to remove it and the OnDisable() or OnDestory() will be called automatically when the scene unloaded, it will technically delete everything (if you use the SceneManager with LoadSceneMode.Single).
Answer by simiel7 · Oct 04, 2021 at 04:39 PM
Hi!
As I can see, you left out some part of your code, but in general this error means what it says. You destroyed the EnemyManager at some point with the Destroy(...) method and cannot find the reference anymore.
Another possibility, you mentioned that the player dies and the Scene reloads. Maybe here you implemented something which will destory the EnemyManager or not recreate it automatically and when the game restarts itself, the EnemyManager not yet created and this is the result of it.
I cannot point out the exact problem, because as I said it seems to me that this is not the full code where the restarting process happens or the player dies (or I missed them entirely, sorry for that).
But either way, this error appears if you or the system destroyed the gameobject and the class cannot be found anymore. In the latter case, you should check out the Unity's lifecycle process (https://docs.unity3d.com/2021.1/Documentation/Manual/ExecutionOrder.html) and get your head around it, because during resetting the scene improperly can lead to this problem, where Unity already destroyed your object (properly), but the starting process you wrote is flawed and will not recreate the object you need.