- Home /
Enemies Cannot Be Selected After The First One Dies...
Hello,
I am trying to fix a problem I have, when I have an enemy selected and it dies, which means that it is destroyed, then I cannot select the next enemy...
This is the error I get -
MissingReferenceException: The object of type 'GameObject' 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.
PlayerAttack.Attack () (at Assets/Resources/Script/PlayerAttack.cs:30)
PlayerAttack.Update () (at Assets/Resources/Script/PlayerAttack.cs:23)
&
MissingReferenceException: The object of type 'Transform' 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.
UnityEngine.Transform.get_position () (at C:/BuildAgent/work/7535de4ca26c26ac/Runtime/ExportGenerated/Editor/UnityEngineTransform.cs:26)
Targetting.<SortTargetsByDistance>m__0 (UnityEngine.Transform t1, UnityEngine.Transform t2) (at Assets/Resources/Script/Targetting.cs:35)
System.Array.qsort[Transform] (UnityEngine.Transform[] array, Int32 low0, Int32 high0, System.Comparison`1 comparison) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1772)
System.Array.Sort[Transform] (UnityEngine.Transform[] array, Int32 length, System.Comparison`1 comparison) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1699)
Rethrow as InvalidOperationException: Comparison threw an exception.
System.Array.Sort[Transform] (UnityEngine.Transform[] array, Int32 length, System.Comparison`1 comparison) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1702)
System.Collections.Generic.List`1[UnityEngine.Transform].Sort (System.Comparison`1 comparison) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.Collections.Generic/List.cs:579)
Targetting.SortTargetsByDistance () (at Assets/Resources/Script/Targetting.cs:34)
Targetting.TargetEnemy () (at Assets/Resources/Script/Targetting.cs:42)
Targetting.Update () (at Assets/Resources/Script/Targetting.cs:90)
And here is the code -
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Targetting : MonoBehaviour {
public List<Transform> targets;
public Transform selectedTarget;
private Transform myTransform;
void Start () {
targets = new List <Transform>();
selectedTarget = null;
myTransform = transform;
AddAllEnemies();
}
public void AddAllEnemies()
{
GameObject[] go = GameObject.FindGameObjectsWithTag("Enemy");
foreach(GameObject enemy in go)
AddTarget(enemy.transform);
}
public void AddTarget(Transform enemy)
{
targets.Add(enemy);
}
private void SortTargetsByDistance()
{
targets.Sort(delegate(Transform t1, Transform t2){
return (Vector3.Distance(t1.position, myTransform.position)).CompareTo(Vector3.Distance(t2.position, myTransform.position));
});
}
private void TargetEnemy()
{
if(selectedTarget == null)
{
SortTargetsByDistance();
selectedTarget = targets[0];
}
else
{
int index = targets.IndexOf(selectedTarget);
if(index < targets.Count - 1)
{
index++;
}
else
{
index = 0;
}
DeselectTarget();
selectedTarget = targets[index];
}
SelectTarget();
}
private void SelectTarget()
{
selectedTarget.renderer.material.color = Color.red;
selectedTarget.GetComponent<EnemyDeath>().Selected = true;
PlayerAttack pa = (PlayerAttack)GetComponent("PlayerAttack");
pa.target = selectedTarget.gameObject;
}
private void DeselectTarget()
{
selectedTarget.renderer.material.color = Color.white;
selectedTarget.GetComponent<EnemyDeath>().Selected = false;
selectedTarget = null;
}
void Update () {
if(Input.GetKeyDown(KeyCode.Mouse1))
{
TargetEnemy();
}
}
}
I am new to programming, although I am learning, so any help with this would be great.
Thank you
Answer by whebert · Mar 21, 2013 at 03:05 AM
If your Enemy game objects are being destroyed somewhere other than the above script, you will run into this issue. What is happening is that your targets list contains all the Transforms of the Enemies in your scene at startup, but when one of those Enemies gets destroyed, your target list still contains a reference to the no longer existing GameObject's Transform. And you try to access the destroyed Transform's position when you sort the list, which causes havoc.
There are probably several ways to avoid this, depending on what you wish to accomplish (code samples untested).
Don't destroy your Enemy GameObjects. Perhaps just de-activate them and make them invisible? And when you target it, you could check if it is active and skip it if so...
Let the above Targeting.cs script manage when an Enemy is destroyed in the scene. When that needs to happen, you can remove the Transform from your targets list with
targets.Remove(enemyToBeDestroyed.transform);
This way, you keep a clean targets list and won't have the above issue.If you don't want Targeting.cs to manage when to destroy an Enemy, you could have an Enemy "de-register" itself with the Targeting script. On the Enemy's OnDestroy() method, you could do something like:
Targetting targeting = FindObjectOfType(typeof(Targetting)) as Targetting; if(targeting != null) targeting.RemoveTransform(gameObject.transform);
And of course add a method to Targetting,public void RemoveTransform(Transform transform) { targets.Remove(transform);
Thanks for the reply :)
I have followed the 1st suggestion and it works, when the enemy is destroyed it is deactivated and invisible, however now I am having difficulty finding a way to skip the disabled enemy from being targeted...
I haven't tested this code, but something like the following should work in your TargetEnemey() method.
In Unity 3.x
int index = targets.IndexOf(selectedTarget);
do
{
if(++index >= targets.Count)
{
index = 0;
}
}while(targets[index].gameObject.active == false);
In Unity 4
int index = targets.IndexOf(selectedTarget);
do
{
if(++index >= targets.Count)
{
index = 0;
}
}while(targets[index].gameObject.activeInHierarchy == false);
This assumes you have some method of knowing when you are out of Enemies and won't try to target the next one if there are no longer any available, otherwise the above code would just cycle infinitely.
Awesome, glad it worked. Feel free to mark the answer correct if ya like. :D