2

I'm creating a 2D spaceship game in Unity. I have an object titled "Player" with this script attached to it. In the script, I have this class representing the player's ship:

public class Ship : MonoBehaviour 
{
    public List<Weapon> weaponsList;

    void Start()
    {
        weaponsList = new List<Weapon>();
        weaponsList.Add(new Weapon());
        weaponsList.Add(new Weapon());
    }
}

And this class (within the same script) representing a weapon:

public class Weapon
{
    //properties here
}

Now, when I try to reference weaponsList to get List.Count using this code (from a different script), it throws a NullReferenceException, saying Object reference not set to an instance of an object:

Ship ship = GameObject.Find("Player").GetComponent<Ship>();
if (ship.weaponsList.Count >=2)
{
    //do stuff
}

But any other property of ship i try to access works just fine. Can someone help? If you need additional context or code, please let me know and I'll make the necessary edits.

EDIT: The start method is special to Unity and is always called by default when the script initializes.

5 Answers5

2

To avoid this error Add constructor to your class

public class Ship : MonoBehaviour 
{
    public Ship()
    {
         weaponsList = new List<Weapon>();
    }
    public List<Weapon> weaponsList;

    void Start()
    {
        weaponsList = new List<Weapon>();
        weaponsList.Add(new Weapon());
        weaponsList.Add(new Weapon());
    }
}
mohsen
  • 1,763
  • 3
  • 17
  • 55
  • This solved the problem. I made an edit to the question stating that Unity always calls the Start() method when the script initializes before anything else, so I assumed it would work in the same fashion that a constructor would. Thank you for the suggestion. – Tanner Fix-It Smith Jun 08 '16 at 20:40
  • 2
    That solution causes `weaponsList` to be initialized twice. If there was anything done with the `weaponsList` before calling the `Start()` method this would cause odd behaviors. Better remove the second initialization out of the `Start()` method and even better make sure `weaponsList` can only be initialized once and/or only internally within the `Ship` class. – Quality Catalyst Jun 08 '16 at 22:17
2

Your weaponsList is null if Start() isn't called ... or it becomes null at some point. Change the public variable to become a public property to deny external callers to change the internal list:

public class Ship : MonoBehaviour 
{
    public List<Weapon> weaponsList { get; private set; }
    public Ship()
    {
        weaponsList = new List<Weapon>();
    }
    ...
}

This will likely create compiler errors in other parts of your application. These errors are probably the reason why weaponsList becomes null.

In terms of better coding practice some more suggestions to change the property to this:

public IList<IWeapon> Weapons { get; private set; }
  • Change the List to an interface.
  • Change the Weapon to an IWeapon.
  • Use Pascal notation (Weapons, not weapons).
  • Avoid types in names: Weapons, not WeaponsList (that it is a list is obvious)
Quality Catalyst
  • 6,531
  • 8
  • 38
  • 62
1

Ship does not contain weapons list.

You can avoid the exception with

Ship ship = GameObject.Find("Player").GetComponent<Ship>();
if (ship != null && ship.weaponsList != null && ship.weaponsList.Count >=2)
{
    //do stuff
}

¿Is the method Start() been call?

DanielGatti
  • 653
  • 8
  • 21
0

Instead of putting the initialization of the weapons list in the void Start() put it in the constructor of the object. Then when the ship is created, the weapons list will always be initialized with a zero count. A constructor should always put the object in question into a valid state so it can be used. Sometimes, programmers create Init() or Start() methods to defer expensive logic until a method actually needs it, but in this case I would definitely put that initialization in the constructor.

Jon Raynor
  • 3,804
  • 6
  • 29
  • 43
0

The list is constructed when Start() is called. If Start() is not called before the list is accessed, your error will appear. My guess is that you are trying to access the list before Start() is called.

You should consider building a constructor for the Ship class and placing the initialization code there:

public class Ship : MonoBehaviour 
{
    public List<Weapon> weaponsList;

    public Ship()
    {
        weaponsList = new List<Weapon>();
        weaponsList.Add(new Weapon());
        weaponsList.Add(new Weapon());
    }
}

This constructor will be called as soon as objects of the class are created, and you won't have to call a method explicitly to get the object's properties constructed.

JeffFerguson
  • 2,952
  • 19
  • 28