Search Unity

Compiler gives me LITERARY non-sense errors

Discussion in 'Scripting' started by Eliotz, May 30, 2016.

  1. Eliotz

    Eliotz

    Joined:
    Jul 18, 2015
    Posts:
    81
    So I recently finished working on my GunController script which basically takes the selected weapon into hands and non used weapons into their holsters and everything worked completely fine until now. So when I reopened my project and pressed play, I instantly got these NullReferenceExeption errors for some unknown reason.

    So I started digging a bit deeper and found that WeaponSupplier() function successfully filled the weapons array(see the console), but for some reason the activeWeapon never got assigned in the Start() function.

    Later I found out that Start() function never actually called itself so I called it from WeaponSupplier() function since I know that function definitely works.
    And this is where the non-sense part happens - compiler gave me a "NullReferenceException" error for line 27 which is the Debug.Log line which says in the console that the indeed holds an object called Pistol.

    Wuuuut??#@!

    I'm like super confused right now and I'm hoping that some of you smarter people can help me out.

    Here is the script and my console:

    GunController.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GunController : MonoBehaviour {
    5.  
    6.     public Transform hands;
    7.     public Transform[] holsters = new Transform[4];
    8.  
    9.     public WeaponStats[] weapons = new WeaponStats[4];
    10.  
    11.     WeaponStats ActiveWeapon;
    12.     int weaponOrder = 0;
    13.  
    14.     Weapon[] weaponsInUse = new Weapon[4];
    15.     Weapon gunInHands;
    16.  
    17.     float fireRate;
    18.     float damage;
    19.     float velocity;
    20.     float accuracy;
    21.     float range;
    22.  
    23.     //Sets the starting weapon
    24.     void Start()
    25.     {
    26.         ActiveWeapon = weapons[0];
    27.         Debug.Log("Start() Funciton - " + weapons[0].weapon.id);
    28.         EquipWeapon();
    29.     }
    30.  
    31.     //Assigns weapons to their slots
    32.     public void WeaponSupplier(WeaponStats _primaryStats, WeaponStats _secondaryStats, WeaponStats _tirtiaryStats, WeaponStats _quaternaryStats)
    33.     {
    34.         weapons[0] = _primaryStats;
    35.         weapons[1] = _secondaryStats;
    36.         weapons[2] = _tirtiaryStats;
    37.         weapons[3] = _quaternaryStats;
    38.         Debug.Log("WeaponSupplier() Funciton - " + weapons[0].weapon.id);
    39.         Start();
    40.     }
    41.     //Equips the weapon
    42.     void EquipWeapon ()
    43.     {
    44.  
    45.         for (int i = 0; i < weapons.Length; i++) {
    46.             if (weaponsInUse[i] != null)
    47.             {
    48.                 Destroy(weaponsInUse[i].gameObject);
    49.             }
    50.             if (gunInHands != null)
    51.             {
    52.                 Destroy(gunInHands.gameObject);
    53.             }
    54.             if (i != weaponOrder) {
    55.                 weaponsInUse[i] = Instantiate(weapons[i].weapon, holsters[i].position, holsters[i].rotation) as Weapon;
    56.                 weaponsInUse[i].transform.parent = holsters[i];
    57.             }
    58.             if (ActiveWeapon != null)
    59.             {
    60.                 gunInHands = Instantiate(ActiveWeapon.weapon, hands.position, hands.rotation) as Weapon;
    61.                 gunInHands.transform.parent = hands;
    62.                 WeaponProperties(ActiveWeapon);
    63.             }
    64.         }
    65.     }
    66.     void WeaponProperties(WeaponStats weaponStats)
    67.     {
    68.         fireRate = weaponStats.fireRate;
    69.         damage = weaponStats.damage;
    70.         velocity = weaponStats.velocity;
    71.         accuracy = weaponStats.accuracy;
    72.         range = weaponStats.range;
    73.     }
    74.     void Update()
    75.     {
    76.         switch (Input.inputString)
    77.         {
    78.             case "1":
    79.                 weaponOrder = 0;
    80.                 ActiveWeapon = weapons[weaponOrder];
    81.                 EquipWeapon();
    82.                 break;
    83.             case "2":
    84.                 weaponOrder = 1;
    85.                 ActiveWeapon = weapons[weaponOrder];
    86.                 EquipWeapon();
    87.                 break;
    88.             case "3":
    89.                 weaponOrder = 2;
    90.                 ActiveWeapon = weapons[weaponOrder];
    91.                 EquipWeapon();
    92.                 break;
    93.             case "4":
    94.                 weaponOrder = 3;
    95.                 ActiveWeapon = weapons[weaponOrder];
    96.                 EquipWeapon();
    97.                 break;
    98.             case "e":
    99.                 weaponOrder++;
    100.                 if(weaponOrder > weapons.Length - 1)
    101.                 {
    102.                     weaponOrder = 0;
    103.                 }
    104.                 ActiveWeapon = weapons[weaponOrder];
    105.                 EquipWeapon();
    106.                 break;
    107.             case "q":
    108.                 weaponOrder--;
    109.                 if (weaponOrder < 0)
    110.                 {
    111.                     weaponOrder = 3;
    112.                 }
    113.                 ActiveWeapon = weapons[weaponOrder];
    114.                 break;
    115.         }
    116.         //Sets the fire mode for the weapon
    117.         switch (ActiveWeapon.weapon.type)
    118.         {
    119.             case "Pistol":
    120.                 if (Input.GetMouseButtonDown(0))
    121.                 {
    122.                     Fire();
    123.                 }
    124.                 break;
    125.             case "Rifle":
    126.                 if (Input.GetMouseButton(0))
    127.                 {
    128.                     Fire();
    129.                 }
    130.                 break;
    131.         }
    132.     }
    133.     void Fire()
    134.     {
    135.         gunInHands.Fire(fireRate, damage, velocity);
    136.     }
    137. }
    2016-05-31_0039.png 2016-05-31_0039.png
     
  2. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    It sounds to me like you didn't specify an execution order for your scripts (Edit -> Project Settings -> Script Execution Order). Have the GunController script execute after the 'Default Time'. With this solution, you can remove the call to Start in the WeaponSupplier function.

    Originally you were lucky GunController's Start was called after the script that calls the WeaponSupplier function, but the order can change anytime you open the project unless you specify it.

    The other option is to rename the Start function to something Unity doesn't call automatically, and continue calling it in the WeaponSupplier function.
     
  3. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    It makes perfect sense to me... You're populating your array in the WeaponsSupplier function, but you're trying to access array element 0 in your Start function. You may think it's not being called, but it is... it's being called before your WeaponSupplier function which is why you get the null reference exception. Weapons[0] is null.
     
    Ryiah likes this.
  4. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Looking at your post and code again.. I think I know why you think it wasn't executing. You thought that because ActiveWeapon was null, it wasn't working. You're initializing your array with 4 slots, so they will all be null... when Start executes, it sets ActiveWeapon to null... When you call it again manually, it sets it to the correct weapon because you're calling it after your WeaponsSupplier method fills your array. So, now that you're trying to access a property on the method with Debug.Log, it is throwing an exception because it's null. If you call WeaponsSupplier from Start() before you set ActiveWeapon, it will work fine every time assuming that _primaryStats is not null.

    You could also abstract away some of that functionality. Create your own class that implements IList, or inherits List<T>. Override the Add, AddRange, Remove, Empty methods. Call it a RollingList or something... and add Next and Previous methods on it so it could keep track of it's current index and automatically roll to 0 (or last index) when you call those methods.

    Here's a RollingCollection<T> (a quick implementation). It implements ICollection<T> and uses a backing list to store the values. It adds extra Next, Previous and SetIndex methods as well as a CurrentIndex (read only) property. You could reuse it in your weapon system and other places:

    Code (csharp):
    1.  
    2.     public class RollingCollection<T> : ICollection<T>
    3.     {
    4.         private readonly List<T> _backingList = new List<T>();
    5.         private int _index = 0;
    6.  
    7.         public T this[int index]
    8.         {
    9.             get { return _backingList[index]; }
    10.             set { _backingList[index] = value; }
    11.         }
    12.  
    13.         public int CurrentIndex {  get { return _index; } }
    14.  
    15.         public int Count
    16.         {
    17.             get { return _backingList.Count; }
    18.         }
    19.  
    20.         public bool IsReadOnly
    21.         {
    22.             get { return false; }
    23.         }
    24.  
    25.         public void Add(T item)
    26.         {
    27.             _backingList.Add(item);
    28.         }
    29.  
    30.         public void Clear()
    31.         {
    32.             _backingList.Clear();
    33.             _index = 0;
    34.         }
    35.  
    36.         public bool Contains(T item)
    37.         {
    38.             return _backingList.Contains(item);
    39.         }
    40.  
    41.         public void CopyTo(T[] array, int arrayIndex)
    42.         {
    43.             _backingList.CopyTo(array, arrayIndex);
    44.         }
    45.  
    46.         public IEnumerator<T> GetEnumerator()
    47.         {
    48.             return _backingList.GetEnumerator();
    49.         }
    50.  
    51.         public bool Remove(T item)
    52.         {
    53.             var result = _backingList.Remove(item);
    54.             if (_index >= _backingList.Count)
    55.                 _index = _index--;
    56.  
    57.             return result;
    58.         }
    59.  
    60.         IEnumerator IEnumerable.GetEnumerator()
    61.         {
    62.             return _backingList.GetEnumerator();
    63.         }
    64.  
    65.         #region Navigation Methods
    66.  
    67.         public T Current()
    68.         {
    69.             return _backingList[_index];
    70.         }
    71.  
    72.         public T Next()
    73.         {
    74.             _index++;
    75.  
    76.             if (_index >= _backingList.Count)
    77.                 _index = 0;
    78.  
    79.             return _backingList[_index];
    80.         }
    81.  
    82.         public T Previous()
    83.         {
    84.             _index--;
    85.  
    86.             if (_index < 0)
    87.                 _index = _backingList.Count - 1;
    88.  
    89.             return _backingList[_index];
    90.         }
    91.  
    92.         public void SetIndex(int newIndex)
    93.         {
    94.             if(newIndex < 0 || newIndex >= _backingList.Count)
    95.                 throw new IndexOutOfRangeException("New index must be greater than or equal to 0 and less than the size of the collection.");
    96.  
    97.             _index = newIndex;
    98.         }
    99.  
    100.         #endregion
    101.     }
    102.  
    103.  
    EDIT: Modified so Previous and Next return the next or previous item instead of index... added this[int index] to access items by their index exclusively, and added Current which will return the item at the "current" rolling index.
     
    Last edited: May 31, 2016
    Ryiah likes this.
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Blasphemy. The compiler only speaks truth!

    I've never thought to comment on the literary value of the compiler output. Most of the time the compiler simply say "you messed up". There is no emotion, no passion, no story arc, no poetry. Sometimes there is a little bit of mystery, but in general the literary value of compiler output sucks.

    To answer your question, Start is firing before you call WeaponSupplier. Which is exactly what you would expect.

    To fix simply call WeaponSupplier in Start (or Awake) before you try and assign a weapon. Or move the functionality in Start off into some other function you can call after you call WeaponSupplier. Either way calling Start manually is a bad idea.
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    This isn't a compiler error, by the way, regardless of literary quality. It's a run-time error. You only get compiler errors when your code is, well, compiling, and you can't enter play mode until you have valid code that can be run by Unity. Even if the code doesn't actually do what you want.

    --Eric
     
  7. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    Compilers do get actual bugs filed against them (a truth best kept hidden from programmers).
    I think the OP meant, literally - then again you might take literary literally.
     
    Kiwasi likes this.
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I considered this. But the context doesn't make sense. ;)
     
    eelstork likes this.
  9. Eliotz

    Eliotz

    Joined:
    Jul 18, 2015
    Posts:
    81
    Hi everyone! Daniel's solution worked for me and I made WeaponLibary.cs execute before GunController.cs using the "Script Execution Order" and I got rid of Start() function and I'm not sure why I had it in the first place anyway so thanks for pointing that out!

    WeaponLibary is a script that gives all the WeaponStats to the WeaponSuppler() function, that's also why I can't call the WeaponSupplier function from GunController.cs (as some of you suggested), because it asks for you to have all the WeaponStats variables which are only being generated by WeaponLibary.

    WeaponStats is a class that holds the Weapon variable and couple floats (which are the actual stats) and Weapon is basically a GameObject that has the Weapon.cs script attached.

    So sorry for the confusion about how my scripts work! I never got to try out Dustin's cool script (It's way too advanced for me so I barley understand what It says XD), but thanks for taking your time to help me out with all this, I really appreciate it.

    I also tweaked the scripts so they can handle all sorts of situations and If you are interested I can post the scripts for you.

    Big Thanks to all of you and your replies, man I learned a lot from this thread!
     
    Dustin-Horne likes this.
  10. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    :) It's basically what you have, only abstracted... here's your script rewritten to use it..disclaimer, I just rewrote here in the forums so I may have missed some bits... and I'm not sure what "weaponsInUse" would be used for... but you can see that it's greatly simplified. All of the code that was dealing with tracking of the current weapon index, etc, has been delegated to the RollingCollection class. So now you just call Previous(), Next(), Current().

    Code (csharp):
    1.  
    2. usingUnityEngine;
    3. usingSystem.Collections;
    4.  
    5. publicclassGunController:MonoBehaviour{
    6.  
    7.    public Transform hands;
    8.    public Transform[] holsters = new Transform[4];
    9.  
    10.    public RollectionCollection<WeaponStats> weapons = new RollingCollection<WeaponStats>();
    11.  
    12.     Weapon[] weaponsInUse = newWeapon[4];
    13.  
    14.    float fireRate;
    15.    float damage;
    16.    float velocity;
    17.    float accuracy;
    18.    float range;
    19.  
    20.    //Assigns weapons to their slots
    21.    publicvoidWeaponSupplier(WeaponStats _primaryStats, WeaponStats _secondaryStats, WeaponStats _tirtiaryStats, WeaponStats _quaternaryStats)
    22.    {
    23.         weapons.Add(_primaryStats);
    24.         weapons.Add(_secondaryStats;
    25.         weapons.Add(_tirtiaryStats);
    26.         weapons.Add(_quaternaryStats);
    27.    }
    28.    //Equips the weapon
    29.    void EquipWeapon()
    30.    {
    31.    
    32.        Instantiate(weapons.Current().weapon, holsters[weapons.CurrentIndex].position, holsters[weapons.CurrentIndex].rotation) as Weapon;
    33.    }
    34.    void WeaponProperties(WeaponStats weaponStats)
    35.    {
    36.         fireRate = weaponStats.fireRate;
    37.         damage = weaponStats.damage;
    38.         velocity = weaponStats.velocity;
    39.         accuracy = weaponStats.accuracy;
    40.         range = weaponStats.range;
    41.    }
    42.  
    43.    void Update()
    44.    {
    45.        switch(Input.inputString)
    46.        {
    47.            case"1":
    48.                 Destroy(weapons.Current().gameObject);
    49.                 weapons.SetIndex(0);
    50.                 EquipWeapon();
    51.                break;
    52.            case"2":
    53.                 Destroy(weapons.Current().gameObject);
    54.                 weapons.SetIndex(1);
    55.                 EquipWeapon();
    56.            case"3":
    57.  
    58.                 Destroy(weapons.Current().gameObject);
    59.                 weapons.SetIndex(2);
    60.                 EquipWeapon();
    61.            case"4":
    62.                 Destroy(weapons.Current().gameObject);
    63.                 weapons.SetIndex(3);
    64.                 EquipWeapon();
    65.            case"e":
    66.                 Destroy(weapons.Current().gameObject);
    67.                 weapons.Next();
    68.                 EquipWeapon();
    69.                 break;
    70.            case"q":
    71.                 Destroy(weapons.Current().gameObject);
    72.                 weapons.Previous();
    73.                 EquipWeapon();
    74.                 break;
    75.        }
    76.        //Sets the fire mode for the weapon
    77.        switch(weapons.Current().weapon.type)
    78.        {
    79.            case"Pistol":
    80.                if(Input.GetMouseButtonDown(0))
    81.                {
    82.                     Fire();
    83.                }
    84.                break;
    85.            case"Rifle":
    86.                if(Input.GetMouseButton(0))
    87.                {
    88.                     Fire();
    89.                }
    90.                break;
    91.        }
    92.    }
    93.    void Fire()
    94.    {
    95.         weapons.Current().Fire(fireRate, damage,velocity);
    96.    }
    97. }
    98.  
     
  11. Eliotz

    Eliotz

    Joined:
    Jul 18, 2015
    Posts:
    81
    Oh cool! Now I see it makes sense to implement RollingCollection, it definitely makes the script look way more understandable and easier to read.

    Also sorry for the confusing name weaponsInUse (I should probably name it something like: weaponsInRest) since they are not being used, but they are the leftover weapons that are not in hands and they have their "resting positions" like on your back for example... you know like when the character wants to grab a pistol, then he will put the rifle on his back and then take the pistol on hands. That's also another task that EquipWeapon() function does and that should need to be added back into the "simplified" version of the script.

    Here is the code I had when I posted the previous reply:

    I did also change weapons to a list, because the 3rd and 4th weapon will be only extras, bet 1st and 2nd weapons are mandatory and I needed my code to be able to deal with those situations.

    GunController.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class GunController : MonoBehaviour {
    6.  
    7.     public Transform hands;
    8.     public Transform[] holsters = new Transform[4];
    9.  
    10.     public List<WeaponStats> weapons = new List<WeaponStats>();
    11.  
    12.     WeaponStats ActiveWeapon;
    13.     int weaponOrder = 0;
    14.  
    15.     Weapon[] weaponsInRest = new Weapon[4];
    16.     Weapon gunInHands;
    17.  
    18.     float fireRate;
    19.     float damage;
    20.     float velocity;
    21.     float accuracy;
    22.     float range;
    23.  
    24.     //Sets the starting weapon
    25.     void Start()
    26.     {
    27.         Debug.Log("Start() Funciton - " + weapons[0].weapon.id);
    28.         EquipWeapon();
    29.     }
    30.  
    31.     //Assigns weapons to their slots
    32.     public void WeaponSupplier(WeaponStats _primaryStats, WeaponStats _secondaryStats, WeaponStats _tirtiaryStats, WeaponStats _quaternaryStats)
    33.     {
    34.         weapons.Add(_primaryStats);
    35.         weapons.Add(_secondaryStats);
    36.         if (_tirtiaryStats != null)
    37.         {
    38.             weapons.Add(_tirtiaryStats);
    39.         }
    40.         if (_quaternaryStats != null)
    41.         {
    42.             weapons.Add(_quaternaryStats);
    43.         }
    44.         ActiveWeapon = weapons[0];
    45.     }
    46.     //Equips the weapon
    47.     void EquipWeapon ()
    48.     {
    49.         for (int i = 0; i < weapons.Count; i++) {
    50.             if (weaponsInRest[i] != null)
    51.             {
    52.                 Destroy(weaponsInRest[i].gameObject);
    53.             }
    54.             if (gunInHands != null)
    55.             {
    56.                 Destroy(gunInHands.gameObject);
    57.             }
    58.             if (i != weaponOrder && weapons[i] != null) {
    59.                 weaponsInRest[i] = Instantiate(weapons[i].weapon, holsters[i].position, holsters[i].rotation) as Weapon;
    60.                 weaponsInRest[i].transform.parent = holsters[i];
    61.             }
    62.             if (ActiveWeapon != null)
    63.             {
    64.                 gunInHands = Instantiate(ActiveWeapon.weapon, hands.position, hands.rotation) as Weapon;
    65.                 gunInHands.transform.parent = hands;
    66.             }
    67.         }
    68.     }
    69.     void Update()
    70.     {
    71.         switch (Input.inputString)
    72.         {
    73.             case "1":
    74.                 weaponOrder = 0;
    75.                 ActiveWeapon = weapons[weaponOrder];
    76.                 EquipWeapon();
    77.                 break;
    78.             case "2":
    79.                 weaponOrder = 1;
    80.                 ActiveWeapon = weapons[weaponOrder];
    81.                 EquipWeapon();
    82.                 break;
    83.             case "3":
    84.                 if (weapons.Count >= 3)
    85.                 {
    86.                     weaponOrder = 2;
    87.                     ActiveWeapon = weapons[weaponOrder];
    88.                     EquipWeapon();
    89.                 }
    90.                 break;
    91.             case "4":
    92.                 if (weapons.Count == 4)
    93.                 {
    94.                     weaponOrder = 3;
    95.                     ActiveWeapon = weapons[weaponOrder];
    96.                     EquipWeapon();
    97.                 }
    98.                 break;
    99.             case "e":
    100.                 weaponOrder++;
    101.                 if(weaponOrder > weapons.Count - 1)
    102.                 {
    103.                     weaponOrder = 0;
    104.                 }
    105.                 ActiveWeapon = weapons[weaponOrder];
    106.                 EquipWeapon();
    107.                 break;
    108.             case "q":
    109.                 weaponOrder--;
    110.                 if (weaponOrder < 0)
    111.                 {
    112.                     weaponOrder = weapons.Count -1;
    113.                     Debug.Log(weaponOrder);
    114.                 }
    115.                 ActiveWeapon = weapons[weaponOrder];
    116.                 EquipWeapon();
    117.                 break;
    118.         }
    119.         //Sets the fire mode for the weapon
    120.         if (ActiveWeapon != null) {
    121.             switch (ActiveWeapon.weapon.type)
    122.             {
    123.                 case "Pistol":
    124.                     if (Input.GetMouseButtonDown(0))
    125.                     {
    126.                         Fire();
    127.                     }
    128.                     break;
    129.                 case "Rifle":
    130.                     if (Input.GetMouseButton(0))
    131.                     {
    132.                         Fire();
    133.                     }
    134.                     break;
    135.             }
    136.         }
    137.     }
    138.     void Fire()
    139.     {
    140.         gunInHands.Fire(ActiveWeapon.fireRate, ActiveWeapon.damage, ActiveWeapon.velocity);
    141.     }
    142. }
    I will also try learning c# properties so I can hopefully understand your RollingCollection script and attempt to implement it.

    Thanks for your time and most importantly...... Happy Coding! :D
     
    Dustin-Horne likes this.