Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Struggling to set initial position in an array of Vector3...

Discussion in 'Scripting' started by NeonCharles, Mar 3, 2021.

  1. NeonCharles

    NeonCharles

    Joined:
    Jun 21, 2019
    Posts:
    4
    I've got a system of levels that has prefabs separated by tags. The dirt platform in each level is added to an array for that level so that I can loop through each object and apply certain transforms and rotations to it.

    The problem is I've run into a situation where I need to store the initial position of each floor dirt. So I've created an array of Vector3's to store each position. Since I don't know exactly how many floor dirts objects there will be, I don't know the size to initialize it to, so I've left it mostly blank.

    But right now I'm getting ```NullReferenceException: Object reference not set to an instance of an object``` when I try and set the position of the dirt at that index.

    ... it's really hard to explain, so please take a look at the code I've got and let me know if you know the source of my problem, or better yet an easier solution. I think at this point I've got myself all turned around, and this is my very first game, so I'm still coding inefficiently (obviously as you will see).

    edit: Forgot to include, at the moment my GameManager is initializing level 3 first for testing, which is why I'm looking at lvl3 more closely. I've also tried initializing a Vector3 object and setting it to that first as well... still not working.

    Code (CSharp):
    1. private GameObject[] _firstFloorDirt;
    2.     private Vector3[] _firstFloorDirt_InitialPosition;
    3.     private GameObject[] _secondFloorDirt;
    4.     private Vector3[] _secondFloorDirt_InitialPosition;
    5.     private GameObject[] _thirdFloorDirt;
    6.     private Vector3[] _thirdFloorDirt_InitialPosition;
    7.  
    8.     private GameObject[][] _allFloors;
    9.  
    10.     // Start is called before the first frame update
    11.     void Start()
    12.     {
    13.         _firstFloorDirt = GameObject.FindGameObjectsWithTag("Level1_FloorDirt");
    14.         _secondFloorDirt = GameObject.FindGameObjectsWithTag("Level2_FloorDirt");
    15.         _thirdFloorDirt = GameObject.FindGameObjectsWithTag("Level3_FloorDirt");
    16.         _allFloors = new GameObject[][] { _firstFloorDirt, _secondFloorDirt, _thirdFloorDirt };
    17.  
    18.         Debug.Log("first: " + _thirdFloorDirt[0]);
    19.         Debug.Log("flrs 3: " + _allFloors[2]);
    20.         GetInitialPositions(_allFloors);
    21.         Debug.Log("first in thrd: " + _thirdFloorDirt_InitialPosition[0]);
    22. }
    23.  
    24.     private void GetInitialPositions(GameObject[][] allFlrs)
    25.     {
    26.         //foreach (GameObject[] x in allFlrs)
    27.         int x;
    28.         for (x = 0; x < allFlrs.Length && allFlrs[x] != null; x++)
    29.         {
    30.             int index = 0;
    31.             foreach (GameObject i in allFlrs[x])
    32.             {
    33.                 if (allFlrs[x] == _firstFloorDirt && _firstFloorDirt != null)
    34.                 {
    35.                     _firstFloorDirt_InitialPosition[index] = i.transform.localPosition;
    36.                     index++;
    37.                 }
    38.                 if (allFlrs[x] == _secondFloorDirt && _secondFloorDirt != null)
    39.                 {
    40.                     _secondFloorDirt_InitialPosition[index] = i.transform.localPosition;
    41.                     index++;
    42.                 }
    43.                 if (allFlrs[x] == _thirdFloorDirt && _thirdFloorDirt != null)
    44.                 {
    45.                     _thirdFloorDirt_InitialPosition[index] = new Vector3();
    46.                     _thirdFloorDirt_InitialPosition[index] = i.transform.localPosition;
    47.                     index++;
    48.                 }
    49.             }
    50.         }
    51.     }
     
  2. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    If you have an unknown amount why not use a list? (or any other collection you can easily add and remove elements from)
     
  3. NeonCharles

    NeonCharles

    Joined:
    Jun 21, 2019
    Posts:
    4
    Would the list deal with the null reference error? I could try converting to a list
     
  4. olejuer

    olejuer

    Joined:
    Dec 1, 2014
    Posts:
    210
    Hey,

    The variables _firstFloorDirt_InitialPosition are never assigned. You only declare them as fields, but you need to assign them a value before you access them. The first time you do access them, you actually know the size they should have. Its allFlrs[x].Length . So you could initialize them with
    _firstFloorDirt_InitialPosition = new Vector3[allFlrs[x].Length]

    This is just a solution to the exact problem you are currently having. There are more issues with your code, though. I am not quite sure of what you are trying to achieve here. But here are a few general hints:
    - NullReferenceException means you are trying to access some variable that has no value assigned to it. The error should also give you the line where this happens in your code.
    - You are iterating with a foreach and then increment an index you maintain manually. I would do either or, not both. Nobody guarantees you that your index will later match the object that foreach gave you.
    - A few null checks you do are weird and seem to be there just for the sake of avoiding exceptions. In case something really is null, does your code behave the way you want it to? Why should it be null? Try to solve the underlying problem instead.
    - Consider using Components instead of Tags. You could have a script


    Code (CSharp):
    1. public class FloorDirt: MonoBehaviour
    2. {
    3.   public Vector3 InitialPosition {get; private set;}
    4.  
    5.   private void Awake()
    6.   {
    7.     InitialPosition = transform.position;
    8.   }
    9. }
    And attach it to all your dirts. They would then memorize their initial positions on their own and make it accessible for other scripts.

    You could also have a script

    Code (CSharp):
    1. public class Floor : MonoBehaviour
    2. {
    3.   [SerializeField] private FloorDirt[] _floorDirts;
    4.   public FloorDirt[] FloorDirts => _floorDirts;
    5. }
    you could assign it to a "Floor" GameObject in your scene and assign all the Dirts to its list in the editor. You could also use "FindObjectsOfType<FloorDirt>()", but I recommend you manually assign them. The Floor script could have a method that goes through all the Dirt objects and move them back to their InitialPosition whenever you need to do that.

    I hope this makes sense to you and I haven't overwhelmed you
     
  5. NeonCharles

    NeonCharles

    Joined:
    Jun 21, 2019
    Posts:
    4
    @olejuer thank you for the detailed response! That helps immensely. You've certainly given me a lot to consider but I want to try implementing the separate script to memorize initial positions first.

    Essentially different dirts on different levels behave separately - some rotate, some move left and right, some up and down, etc. So having the initial position of all of these is going to be helpful. However some are null on some levels where that type of floor dirt is never used. I.e. - on level 1, I don't have any objects with tag Level3_FloorDirt, so in that situation those would be null. But later on, on level 5 say, I have all 3 floor dirt types so they need to be accessed simultaneously. Or at least that was the logic behind using tags.

    I was under the impression that GetComponent or whatnot, used to access components, is used for any object defined by that script. So since my Floor script is on all three dirt types, it would just get everything.
     
  6. olejuer

    olejuer

    Joined:
    Dec 1, 2014
    Posts:
    210
    Hmmm, there is a difference between the object not existing and it being null. When you use "FindGameObjectsWithTag" or "FindObjectsOfType<>" they would give you an empty array. Not null and not an array with nulls in it, but one that has 0 length.

    I think, in your case, you can either create special components such as

    Code (CSharp):
    1. public class RotatingDirt : MonoBehaviour
    2. {
    3.   [SerializeField] private float _rotationSpeed;
    4.   [SerializeField] private Vector3 _rotationAxis;
    5.  
    6.   private void Update()
    7.   {
    8.     transform.Rotate(_rotationAxis, _rotationSpeed * Time.deltaTime);
    9.   }
    10. }
    And your Dirt Object could have a "Dirt" component for remembering the StartPosition (all dirts need to do that after all) and a "RotatingDirt" component when it is the kind that rotates. You can then search for whatever you want to find. Or keep a list somewhere.
     
  7. NeonCharles

    NeonCharles

    Joined:
    Jun 21, 2019
    Posts:
    4
    @olejuer that's a good idea. I think I will end up doing that. Thank you so much for your help.
     
  8. olejuer

    olejuer

    Joined:
    Dec 1, 2014
    Posts:
    210
    Sure, you are welcome. Have fun coding!
     
    NeonCharles likes this.