简体   繁体   中英

Attack only targeted enemies Unity

I have a script which is used to control targeted enemies and another to control player attacks, the targeting script seems to work fine on its own until the first target is killed, so when I click Tab it targets my enemies in distance order but when my first target dies it cannot target another and I get the message:

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.

Also I am unable to attack anything except the first player whether it is targeted or not. I tried switching my playerattacks so it worked with a list just like my targeting script however I get all the same errors.

My targetting script is:

public class Targetting : MonoBehaviour {

public List<Transform> targets;
public Transform selectedTarget;
public string targetTag = "Enemy";
private Transform myTransform;

//Use this for initialization
void Start () {
    targets = new List<Transform>();
    selectedTarget = null;
    myTransform = transform;        
    AddAllEnemies();
}

public void AddAllEnemies() {
    GameObject[] go = GameObject.FindGameObjectsWithTag(targetTag);     
    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;    
        }

        selectedTarget = targets[index];
    }
}

//Update is called once per frame
void Update () 
{
    if(Input.GetKeyDown(KeyCode.Tab))
    {
        TargetEnemy();
    }
}
}

And my PlayerAttack Script is currently:

public class PlayerAttack : MonoBehaviour {

public enemyHealth eHealth;
public GameObject enemy;
private float MHCD; // MeleeHit CD

private float hitChance;

// Use this for initialization
void Start () {

}

void Update () {
    hitChance = Random.Range (1, 100); 

    if (Input.GetKeyDown (KeyCode.Alpha1) && Time.time - MHCD > 1) { 
        if (hitChance > 0 && hitChance < 90) {
            Attack1 (); 
            MHCD = Time.time; 
        }
    }
}  

void Attack1() {
    float distance = Vector3.Distance (enemy.transform.position, transform.position);

    if (distance < 4) { 
        eHealth.SendMessage ("MeleeHit");
    } else {
        Debug.LogWarning ("You are too far away!"); 
    }
}
}

Basically what I am looking to do is to make both scripts work with eachother so when an enemy is targeted, it'll only deduct damage from the targeted enemy, and when an enemy is killed it is removed from the list 'targets'

I completely agree with Joe's diagnosis that the key problem here is that you are not updating your List<Transform> targets after calling Destroy() on any its members' associated objects. I'm going to propose a slightly different fix to the problem, though.

When you attempt to sort the targets by their distance from the player with the following method:

private void SortTargetsByDistance() {
    targets.Sort(delegate(Transform t1, Transform t2) { 
        return (Vector3.Distance(t1.position, myTransform.position).CompareTo)
            (Vector3.Distance(t2.position, myTransform.position));
    });
}

You're attempting to access the Transform on each potential target for comparison, but aren't taking into account the possibility that the target may have already been destroyed (rendering its Transform nonexistent). A simple fix to this is to remove all null references to Transform objects before trying to sort targets , by adjusting it to the following:

private void SortTargetsByDistance() {
    targets.RemoveAll(target => target == null);
    targets.Sort(delegate(Transform t1, Transform t2) { 
        return (Vector3.Distance(t1.position, myTransform.position).CompareTo)
            (Vector3.Distance(t2.position, myTransform.position));
    });
}

Hope this helps! Let me know if you have any questions.

(Although this is the most minimal change to your code, I do still support the idea of having a manager that keeps track of the enemies, like what Joe suggested. It'll save you some headaches in the long run, if your project keeps growing.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM