簡體   English   中英

如何修復這些 Unity 錯誤?

[英]How to fix these Unity errors?

我最近在我的大多數腳本中為列表切換了一些 arrays,因為它們更易於使用。 但問題是帶來了一些新的錯誤。 他們是:

  1. InvalidOperationException:集合已修改; 枚舉操作可能無法執行。
  2. ArgumentOutOfRangeException:索引超出范圍。 必須是非負數且小於集合的大小。

根據錯誤,問題出在 Gorean Turret 的第 96 行和 POW_Camp 的第 37 行。 其他幾個腳本也有這個問題,但如果我弄清楚這兩個有什么問題,我應該能夠弄清楚如何修復 rest。

我搜索了如何處理這些錯誤並找到了幾個解決方案。 我將 foreach 循環更改為 for (var i=0;) 樣式循環。 這應該有效,但不幸的是沒有。

我的問題是如何修復這些錯誤。 我如何需要更改我的腳本以確保沒有錯誤。 作為旁注,如果您發現可以縮短的內容,請告訴我。 我有一種感覺,代碼也比它需要的長。

這是 POW_Camp 腳本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class POW_Camp : MonoBehaviour
{
    public GameObject AmmunitionsDump;
    public GameObject Explosion;
    public GameObject Fences;
    public List<GameObject> Prisoners;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (AmmunitionsDump.GetComponent<Health>().CurrentHealth<=0)
        {
            AmmunitionsDump.SetActive(false);
            Explosion.SetActive(true);
        }
        //If the ammunitiondump is destroyed then destroy fences
        if (AmmunitionsDump.activeSelf == false)
        {
            Destroy(Fences);
            //Activate POWs
            for (var i=0;i<Prisoners.Count; i++)
            {
                if (Prisoners[i] == null)
                {
                    Prisoners.Remove(Prisoners[i]);
                }
                if (Prisoners[i] != null)
                {
                    Prisoners[i].GetComponent<PlayerData>().Captured = false;
                }
            }
        }
    }
}

這是 GoreanTurret 腳本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GoreanTurret : MonoBehaviour
{
    public GameObject Projectile;
    public GameObject FiringPoint;
    public GameObject GunTower;

    public GameObject HealthBar;
    public List<GameObject> TargetsWithinRange = new List<GameObject>();
    public float FiringRange = 100;
    public GameObject CurrentTarget;
    public bool TargetLocked;

    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("Shoot", 1, 0.5f);
        HealthBar.GetComponent<Slider>().maxValue = gameObject.GetComponent<Health>().MaxHealth;
        HealthBar.GetComponent<Slider>().value = gameObject.GetComponent<Health>().CurrentHealth;
    }

    // Update is called once per frame
    void Update()
    {
        FindTargetsWithinRange(FiringRange, TargetsWithinRange);
        TargetManager(TargetsWithinRange, FiringRange);
        HealthBar.GetComponent<Slider>().value = gameObject.GetComponent<Health>().CurrentHealth;
        //Look at Target if Target is Locked
        if (TargetLocked)
        {
            GunTower.transform.LookAt(CurrentTarget.transform);
        }
        // If target is destroyed then set targetLocked to false and target to null;
        if (CurrentTarget != null)
        {
            if (CurrentTarget.GetComponent<Health>().CurrentHealth <= 0)
            {
                TargetLocked = false;
                CurrentTarget = null;
            }
        }
        // If one of the targets in the target list is destroyed then remove that
        foreach (GameObject possibleTarget in TargetsWithinRange)
        {
            if (possibleTarget == null || possibleTarget.GetComponent<Health>().CurrentHealth<=0 || possibleTarget.GetComponent<Health>()==null)
            {
                TargetsWithinRange.Remove(possibleTarget);
                
            }
        }
        if (CurrentTarget == null)
        {
            TargetLocked = false;
            CurrentTarget = null;

        }
        // If target is lost and there are still targets, then switch target
        if (TargetLocked == false && (CurrentTarget == null || CurrentTarget.GetComponent<Health>().CurrentHealth<0) && TargetsWithinRange.Count>0)
        {
            if (TargetsWithinRange[0] != null)
            {
                CurrentTarget = TargetsWithinRange[0];
                TargetLocked = true;
            }
        }
    }



    void FindTargetsWithinRange(float range, List<GameObject> TargetList)
    {
        Collider[] colliders = Physics.OverlapSphere(gameObject.transform.position, range);
        foreach (Collider collider in colliders)
        {
            if (collider.gameObject.GetComponent<PlayerData>())
            {
                if (collider.gameObject.GetComponent<PlayerData>().Captured == false && TargetList.Contains(collider.gameObject) == false)
                {
                    TargetList.Add(collider.gameObject);
                }
            }
        }
    }
    void TargetManager(List<GameObject> TargetList, float MaxRange)
    {
        for (var i = 0; i < TargetList.Count; i++)
        {
            //If the Target is null or inactive then remove that target or if it has no health
            if (TargetList[i] == null || TargetList[i].activeSelf == false || TargetList[i].GetComponent<Health>().CurrentHealth <= 0)
            {
                TargetList.Remove(TargetList[i]);
                if (TargetList[i] == CurrentTarget)
                {
                    TargetLocked = false;
                }
            }
            //If the target is too far
            if (Vector3.Distance(gameObject.transform.position, TargetList[i].transform.position) > MaxRange)
            {
                TargetList.Remove(TargetList[i]);
                if (TargetList[i] == CurrentTarget)
                {
                    CurrentTarget = null;
                    TargetLocked = false;
                }
            }
        }

        //If there is no target and the TargetLocked is false then Set a new target
        if (CurrentTarget == null && TargetLocked == false && TargetList.Count > 0)
        {
            CurrentTarget = TargetList[0];
            TargetLocked = true;
        }

        // If target is destroyed then set targetLocked to false and target to null;
        if (CurrentTarget != null && CurrentTarget.activeSelf == true && CurrentTarget.GetComponent<Health>().CurrentHealth > 0)
        {
            if (CurrentTarget.GetComponent<Health>().CurrentHealth <= 0)
            {
                TargetLocked = false;
                CurrentTarget = null;
            }
        }
    }

    public void Shoot()
    {
        if (TargetLocked == true)
        {
            Instantiate(Projectile, FiringPoint.transform.position, FiringPoint.transform.rotation);
        }
    }
}

在您的兩個類中,您都有循環,您在迭代它們時從 collections 中刪除元素。

第一個導致ArgumentOutOfRangeException因為在刪除一定數量的元素后,您將迭代直到達到一個太高的索引,因為您的列表/數組現在更小了!

另一個例外是不言自明的:您在使用foreach迭代集合時更改集合。

您可以使用Linq 解決這兩種情況, Where為了過濾掉null條目(需要using System.Linq;在文件頂部)

// in general use the bool operator of UnityEngine.Object
// never do check for "== null" !
Prisoners = Prisoners.Where(p =>  p).ToList();
// This basically equals doing something like
//var p = new List<GameObject>();
//foreach(var prisoner in Prisoners)
//{
//    if(prisoner) p.Add(prisoner);
//}
//Prisoners = p;

foreach (var prisoner in Prisoners.Count)
{
    prisoner.GetComponent<PlayerData>().Captured = false;
}

完全相同的事情可以在你的第二個腳本中完成

// so first again we use the bool operator to check if the element target "existed
// then directly use TryGetComponent instead of GetComponent twice
// your order to check also made little sense ;) you should first Check 
// the existence of the component before accessing a value
TargetsWithinRange = TargetsWithinRange.Where(target => target && target.TryGetComponent<Health>(out var health) && health.CurrentHealth > 0).ToList();

或者,如果集合本身保持相同的引用很重要,您可以使用RemoveAll代替,例如

Prisoners.RemoveAll(prisoner => ! prisoner);

並相應地在您的其他腳本中

TargetsWithinRange.RemoveAll(target => !target || !target.TryGetComponent<Health>(out var h) || h.CurrentHealth <= 0);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM