如何使用动态索引更新对象数组?

Posted

技术标签:

【中文标题】如何使用动态索引更新对象数组?【英文标题】:How to update an array of object with a dynamic index? 【发布时间】:2020-07-20 15:13:21 【问题描述】:

开始

ex "数组调整大小丢失的类或索引"

我正在构建一个对象来保存我的汽车的值。

基本逻辑是: 对于每个变速箱 > 保存每个悬架 > 保存每个车轮。

脚本逻辑:

  public class VehicleController : MonoBehaviour
  
      public gearbox[] gearboxes = new gearbox[0]; //999 not change effect

      [System.Serializable] 
      public class gearbox
      
        public Transform gearBoxTarget;

        public List<Transform> assets = new List<Transform>();

        public gearbox(Transform gearBoxTarget, List<Transform> assets  )
        
            this.gearBoxTarget = gearBoxTarget;
            this.assets = assets;
        
      

      void Awake()
      
        /// count all gBox into 3d model
        List<Transform> boxes = new List<Transform>();
        Transform[] elems = transform.GetComponentsInChildren<Transform>();
        int Index = 0;
        for (int c = 0; c < elems.Length; c++)
        
          if (elems[c].name == "gearbox")
          
            Index++;
            boxes.Add(elems[c].transform);
          
        

        /// set array length (1 for gBox finded)
        System.Array.Resize(ref gearboxes, Index);

        /// for all gearboxes finded (or boxes.Length... It's equal)
        for (int box = 0; box < gearboxes.Length; box++)
        
          // get suspansions and wheels
          Transform[] inBox = boxes[box].GetComponentsInChildren<Transform>();
          List <Transform> Items = new List <Transform>();
          for (int e = 0; e < inBox.Length; e++)
          
              var el = inBox[e];
              if (el.parent.name == "gearbox" || el.name == "wheel") Debug.Log(e+" => "+el); Items.Add(el); elseDebug.Log(e);
              if(e==inBox.Length)  Debug.Log("finder end"); 
          

          /// add elements into the gearbox object
          Debug.Log(gearboxes[box]); // NULL!
          gearboxes[box].gearBoxTarget = boxes[box]; // NULL!
          gearboxes[box].assets.AddRange(Items); // NULL!
        
      
  

我不明白为什么数组的长度发生了变化,尽管检查器已更新,但脚本似乎不再看到变速箱。

无论您尝试放入什么,变速箱...都是无效的


测试 02

新的简化测试。

我尝试在 Awake 中初始化列表。

它还会创建第二个列表,用于处理元素。

未能直接实例化对象我尝试将所有内容从新列表转移到旧列表。

  public class VehicleController : MonoBehaviour
  

    public gearbox[] gearboxes;

    [System.Serializable]
    public class gearbox
    

      public Transform boxtarget; 

        public gearbox( Transform boxtarget  )
        
        this.boxtarget = boxtarget;
        
    
    

    void Awake()
    

        // count all gBox and set it on inspector and add to list
        Transform[] elems = transform.GetComponentsInChildren<Transform>();
        List<Transform> targets = new List<Transform>();
        for (int c = 0; c < elems.Length; c++)
        
          if (elems[c].name == "gearbox")
          
            targets.Add(elems[c]);
          
        

        int Counter = targets.Count;

        // System.Array.Resize(ref gearboxes, Index);
        gearbox[] gboxes = new gearbox[Counter];
        gearboxes = new gearbox[Counter];

        for (int i = 0; i < Counter; ++i)
         

          Debug.Log(targets[i]); //ok exist
          gboxes[i].boxtarget = targets[i];
        

        System.Array.Copy(gboxes, 0, gearboxes , 0, Counter);
         

    

  

又一次失败。 在检查器列表中更新为 3 个元素但为空并且调试写入 null。

齿轮箱(UnityEngine.Transform)(目标确定)

NullReferenceException:对象引用未设置为 object VehicleControllerManager.VehicleController.Awake()(在


测试 03

另一种方法...

现在的尝试是将一个类添加到列表中并检索拥有该类的对象。

这样我应该绕过这样一个事实,即单声道直接指向变速箱但将其视为附加对象。

public class VehicleController : MonoBehaviour

    [SerializeField]
    public List<gearbox> gearboxes = new List<gearbox>();

    [System.Serializable] 
    public class gearbox
    
      public class box
      
        public Transform boxTarget;
        public box(Transform boxTarget)
        
          this.boxTarget = boxTarget;
        
      
    

    void Awake()
    


      /// count all gBox into 3d model
      List<Transform> boxes = new List<Transform>();
      Transform[] elms = transform.GetComponentsInChildren<Transform>();
      int Counted = 0;
      for (int c = 0; c < elms.Length; c++)
      
        if (elms[c].name == "gearbox")
         Counted++; boxes.Add(elms[c].transform); 
      

      Debug.Log(boxes.Count); // yes, it's 3

      /// add class on link (1 for all gBox finded)
      for (int box = 0; box < boxes.Count; box++)
      
        gearboxes.Add( new VehicleController.gearbox()); // ok, added it
      

      /// for all gearboxes in list
      for (int i = 0; i < gearboxes.Count; i++)
      
        /// into the gearboxes get object and set values of box
        //gearboxes[i].gearbox.box.boxTarget = boxes[i]; WTF? I can't navigate into the class!??
      

    

结果:检查器中出现 3 个元素,但它们内部似乎没有任何内容,或者无论如何..."gearboxes[i].gearbox.box.boxTarget = boxes[i];"我无法导航到类...

是否有可能恢复盒子并设置它们的内部值?


测试 03.b

根据各种建议和示例,我找到了一个“半解决方案”

这不是确定的,因为我不在检查器中显示内容,而只显示插入的类。

但是,如果我循环...数据存在并返回!

public class VehicleController : MonoBehaviour

    [SerializeField]        public List<gearbox> gearboxes = new List<gearbox>();
    [System.Serializable]   public class gearbox
                            
                              public _box box = new _box();
                              [System.Serializable]
                              public class _box
                              
                                public Transform boxTarget  get; set; 
                              
                            
  

    void Start()
    


      /// count all gBox into 3d model
      List<Transform> boxes = new List<Transform>();
      Transform[] elms = transform.GetComponentsInChildren<Transform>();
      int Counted = 0;
      for (int c = 0; c < elms.Length; c++)
      
        if (elms[c].name == "gearbox")
         Counted++; boxes.Add(elms[c].transform); 
      

      Debug.Log(boxes.Count);

      /// add class on link (1 for all gBox finded)
      for (int box = 0; box < boxes.Count; box++)
      
        gearboxes.Add( new VehicleController.gearbox());
      

      // for all gearboxes in list
      for (int i = 0; i < gearboxes.Count; i++)
      
        /// add elements into the gearbox object
        gearboxes[i].box.boxTarget = boxes[i];
      

      for (int i = 0; i < gearboxes.Count; i++)
      
        Debug.Log("++ "+gearboxes[i].box.boxTarget);
      

    


测试 03.c - 解决方案

正如建议的那样,它是“实例化对象”,但是 这是直接实例化到列表的构造

如果没有这个,最大的问题是要理解“如果它是空的,就不可能实例化对象”,否则你会发现著名的“空错误”。

所以......让我们给它一个空! :D,这就是解决方案!

public class VehicleController : MonoBehaviour

    [SerializeField]        public List<boxvalues> gearboxes = new List<boxvalues>();
    [System.Serializable]   public class boxvalues
                            
                              public Transform boxTarget;
                              public boxvalues(Transform boxTarget)
                              
                                this.boxTarget = boxTarget;
                              
                              // [SerializeField] public Transform boxTarget  get; set;  // This is a safer system but does not expose variables.
                            
  

    void Awake()
    


      /// count all gBox into 3d model
      List<Transform> boxes = new List<Transform>();                       // prepare a list of gameObject.
      Transform[] elms = transform.GetComponentsInChildren<Transform>();   // find all child into main gameObject.
      int Counted = 0;                                                     // Counter of gameObject
      for (int c = 0; c < elms.Length; c++)                                // Loop all finded gameObject into main
      
        if (elms[c].name == "gearbox")                                     // if is my gameObject...
         Counted++; boxes.Add(elms[c].transform);                        // add it to list and count it...
      

      Debug.Log(boxes.Count);                                              // yes, It's 3 gameObject.

      /// add class on link (1 for all gBox finded)
      for (int box = 0; box < boxes.Count; box++) 
      
        gearboxes.Add( new VehicleController.boxvalues(null));            // now for all gameObject init a new data container... empty (yooo! new list of data!)
      

      // for all gearboxes in list
      for (int i = 0; i < gearboxes.Count; i++)                           // now for all data container... put a new values. Win!
      
        /// add elements into the box object
        gearboxes[i].boxTarget = boxes[i];
      

      // test return the values
      for (int i = 0; i < gearboxes.Count; i++)
      
        Debug.Log("++ "+gearboxes[i].boxTarget);
      

    

衷心感谢那些耐心给我一个有用的方向的人!

我希望这个方案对其他人有教育意义。

【问题讨论】:

如果gearbox 是一个类,那么它的默认值为null - 如果您希望它不为空,则需要创建该类的实例 如果gearboxclass,那么您有一个对gearbox 实例的引用数组。这种引用的默认值为null @Alberto 您不能在 c# 中调整数组的大小,只能用不同大小的新数组替换它。您或许应该考虑使用 List,因为这将提供添加/删除项目的能力。 @Alberto Array.Resize 不会调整数组的大小,它会以请求的大小创建一个新数组,并将旧数组中的元素复制到其中。这是一个技术点,但很重要,因为数组不再是同一个对象。 @Alberto,我认为问题出在 UnholySheep 解释的范围内,您正在创建一个大小正确的数组,但所有条目均为空,因为您从未将它们设置为任何内容。 【参考方案1】:

你需要创建类的实例:

public class VehicleController : MonoBehaviour
  
      public gearbox[] gearboxes = null;

      public VehicleController()
      
        gearboxes = new gearbox[0];
      

      [System.Serializable] 
      public class gearbox
      
        public Transform gearBoxTarget;

        public List<Transform> assets = new List<Transform>();

        public gearbox(Transform gearBoxTarget, List<Transform> assets  )
        
            this.gearBoxTarget = gearBoxTarget;
            this.assets = assets;
        
      

      void Awake()
      
        /// count all gBox into 3d model
        List<Transform> boxes = new List<Transform>();
        Transform[] elems = transform.GetComponentsInChildren<Transform>();
        int Index = 0;
        for (int c = 0; c < elems.Length; c++)
        
          if (elems[c].name == "gearbox")
          
            Index++;
            boxes.Add(elems[c].transform);
          
        

        /// set array length (1 for gBox finded)
        System.Array.Resize(ref gearboxes, Index);

        /// for all gearboxes finded (or boxes.Length... It's equal)
        for (int box = 0; box < gearboxes.Length; box++)
        
          // get suspansions and wheels
          Transform[] inBox = boxes[box].GetComponentsInChildren<Transform>();
          List <Transform> Items = new List <Transform>();
          for (int e = 0; e < inBox.Length; e++)
          
              var el = inBox[e];
              if (el.parent.name == "gearbox" || el.name == "wheel") Debug.Log(e+" => "+el); Items.Add(el); elseDebug.Log(e);
              if(e==inBox.Length)  Debug.Log("finder end"); 
          

          /// add elements into the gearbox object
          Debug.Log(gearboxes[box]);
          gearboxes[box].gearBoxTarget = boxes[box];
          gearboxes[box].assets.AddRange(Items);
        
      
  

我猜你没加的函数应该是这样的:

public class Transform
    

        public string name  get; set; 

        public Transform parent  get; set; 

        public Transform[] GetComponentsInChildren<T>()
        
            return (new List<Transform>() 
                new Transform()  name = "gearbox" ,
                new Transform()  parent = new Transform()  name = "gearbox" , name = "wheel" 
            ).ToArray();
        
    

假设是这种情况,逻辑相同,你必须初始化变量,因为你初始化了类,否则它总是为空。

public class VehicleController
    
        private gearbox[] gearboxes;
        private readonly Transform transform;

        public VehicleController(Transform _transform)
        
            gearboxes = new gearbox[0];
            transform = _transform;
        

        [System.Serializable]
        public class gearbox
        
            public Transform gearBoxTarget;

            public List<Transform> assets = new List<Transform>();

            public gearbox(Transform gearBoxTarget, List<Transform> assets)
            
                this.gearBoxTarget = gearBoxTarget;
                this.assets = assets;
            
        

        void Awake()
        
            /// count all gBox into 3d model
            List<Transform> boxes = new List<Transform>();
            Transform[] elems = transform.GetComponentsInChildren<Transform>();
            int Index = 0;
            for (int c = 0; c < elems.Length; c++)
            
                if (elems[c].name == "gearbox")
                
                    Index++;
                    boxes.Add(elems[c].transform);
                
            

            /// set array length (1 for gBox finded)
            System.Array.Resize(ref gearboxes, Index);

            /// for all gearboxes finded (or boxes.Length... It's equal)
            for (int box = 0; box < gearboxes.Length; box++)
            
                // get suspansions and wheels
                Transform[] inBox = boxes[box].GetComponentsInChildren<Transform>();
                List<Transform> Items = new List<Transform>();
                for (int e = 0; e < inBox.Length; e++)
                
                    var el = inBox[e];
                    if (el.parent.name == "gearbox" || el.name == "wheel")  Debug.Log(e + " => " + el); Items.Add(el);  else  Debug.Log(e); 
                    if (e == inBox.Length)  Debug.Log("finder end"); 
                

                /// add elements into the gearbox object
                Debug.Log(gearboxes[box]);
                gearboxes[box].gearBoxTarget = boxes[box];
                gearboxes[box].assets.AddRange(Items);
            
        
    

【讨论】:

不...它不起作用。一直说变速箱是空的 函数“transform.GetComponentsInChildren()”有什么作用? 恢复容器中的对象。然后他们扫描名称,如果它被称为对象,那么我保存并计算它。到每个对象 3d 将对应相对对象 c # 无论如何等等......我发布了测试 3,我研究了你推荐的内容并回答了(我没有注意到你更新了答案,我也看到了 get / set)跨度> 真的很感谢你,没有你的例子我就无法理解和采取正确的方式。

以上是关于如何使用动态索引更新对象数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何在多维数组/网格c ++中创建List

firebase 按索引从字段数组中删除对象

ngrx 更新数组内的对象

Vue.set 向响应式对象中添加响应式属性,及设置数组元素触发视图更新

链表06-开发可用链表(根据索引取得数据)

如何在反应中从对象映射动态设置 sessionStorage