Null Reference Exception

It’s typically a bad idea to call the delegate without checking first that it isn’t null, e.g. whether it has any subscribers.

The steps I would take here to diagnose the problem would be as follows;

  • comment out your line of code within the if statement
  • use Debog.Log in two separate statements to output priorityHit.value and layerHit

Assuming you see values other than null when you do this you can rule those out.

At this point I would then wrap the delegate call in a validation check;

if (Input.GetMouseButton (0))
{
    if(notifyMouseClickObservers)
    {
        notifyMouseClickObservers(priorityHit.Value, layerHit);
    } 
}

Let us know how you get on.

1 Like

Thanks for such a speedy reply, Rob.

I tried the techniques you recommended and found both priorityHit.Value and layerHit did NOT return null. My delegate was indeed null.

I checked again that I was properly subscribed in PlayerMovement.Start with the following line of code:

cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick;

(head scratching begins again …)

On a whim, I moved that whole block of code to PlayerMovement.Awake and that fixed the problem for me.

Now I need to figure out how CameraRaycaster.Update ran before PlayerMovement.Start.

I might add that I’m using Unity 2018.2

Hi,

You are more than welcome.

Your issue is going to be the order of events. Before you made your change, a call was being made to your delegate before the subscribers had subscribed. Whilst all of the Start methods are called before say, Update, you cannot guarantee that they will be executed in your preferred order unless you implement the Script Execution Order and effectively weight them. Where-ever possible I would avoid using the Script Execution Order as it provides a fantastic way of hiding what is going on.

You could also consider the OnEnable and OnDisable methods for use with delegates, as really you should be unsubscribing from them too. :slight_smile:

Because the Awake method is called before Start, this is why after you made your change your code doesn’t error, as all of the subscribers are no in place before a call is made to the delegate.

Hope this helps :slight_smile:


See also;

1 Like

Once again, big thanks. Now I can get back to the course. The references you listed are sure to be most helpful.

1 Like

You are more than welcome :slight_smile:

1 Like

I am confused as to what “whole block” of code you moved to Awake to fix this issue.

I moved:

cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick;

to the Awake method but I still get a nullReference exception.

I don’t understand why the teacher’s code works, but my identical code does not.

I tried to place the if statement: if(notifyMouseClickObservers)

inside the if (Input.GetMouseButton (0)), but I get an error saying that “notifyMouseClickObservers” is not a bool type.

Hi,

to the Awake method but I still get a nullReference exception.

Perhaps post your script(s) and indicate the specific line number of the error.

I tried to place the if statement: if(notifyMouseClickObservers)

inside the if (Input.GetMouseButton (0)), but I get an error saying that “notifyMouseClickObservers” is not a bool type.

It isn’t, but what you can do is an evaluation, e.g.

if(notifyMouseClickObservers != null)
{
    // do stuff
}

Hope this helps :slight_smile:

Uhm, it is interesting. When I click on the ground the ProcessMouseClick called 6 times each click.

Okay! I unstained what you meant now, sorry. I must have missed the “!” before the = sign.

I tried fixing the problem for 2 hours but to no avail…

I am still getting null references at:

if (Input.GetMouseButton (0))
{
      notifyMouseClickObservers(priorityHit.Value, layerHit);
}

When I used Debug.Log for the outputs of priorityHit.value and layerHit within the != null statement, I never got any logs in the console. So I Only have null.

My PlayerMovement script is as follows:

using System;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
using UnityEngine.AI;

[RequireComponent(typeof (NavMeshAgent))]
[RequireComponent(typeof (AICharacterControl))]
[RequireComponent(typeof (ThirdPersonCharacter))] 
public class PlayerMovement : MonoBehaviour {

    AICharacterControl aiCharacterControl = null;
    ThirdPersonCharacter thirdPersonCharacter = null;  
    CameraRaycaster cameraRaycaster = null;
    Vector3 currentDestination;  

    private bool isInDirectMode = false;  

	void Start()
    {
        cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
        thirdPersonCharacter = GetComponent<ThirdPersonCharacter>();
        currentDestination = transform.position;  
        aiCharacterControl = GetComponent<AICharacterControl>();
        cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick; 
    }

    void ProcessMouseClick(RaycastHit raycastHit, int layerHit)
    {
        print("Click");
    }

    //TODO Make this get called again
    void ProcessDirectMovement()
    {
        Debug.Log("In direct movement mode");
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        // calculate camera relative direction to move:
        // calculate move direction to pass to character
        Vector3 cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
        Vector3 movement = v * cameraForward + h * Camera.main.transform.right;
     
        thirdPersonCharacter.Move(movement, false, false);
    }
}

Should I post my CameraRaycaster or CursorAffordance script as well?

Hi,

cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick;

Put the above lines of code into the Awake method instead of Start and re-run.

private void Awake()
{
    cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
    cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick; 
}

Let me know how you get on :slight_smile:


See also;

Moved it. Unfortunately I still get:

NullReferenceException: Object reference not set to an instance of an object
CameraRaycaster.Update () (at Assets/Camera and UI/CameraRaycaster.cs:81)

Can you post those scripts also please :slight_smile:

If that refers to this bit of code;

if (Input.GetMouseButton (0))
{
      notifyMouseClickObservers(priorityHit.Value, layerHit);
}

The delegate should really be being checked for null before being used, e.g. no subscribers are present, like this;

if (Input.GetMouseButton (0))
{
    if(notifyMouseClickObservers != null)
    {
        notifyMouseClickObservers(priorityHit.Value, layerHit);
    }
}

I have the code checked for if it is null.
(Sorry for the bad formatting, I will correct that)

Code for my ProcessMovement:

[RequireComponent(typeof (NavMeshAgent))]
[RequireComponent(typeof (AICharacterControl))]
[RequireComponent(typeof (ThirdPersonCharacter))]  
public class PlayerMovement : MonoBehaviour {

    AICharacterControl aiCharacterControl = null;
    ThirdPersonCharacter thirdPersonCharacter = null;   // A reference to the ThirdPersonCharacter on the object
    CameraRaycaster cameraRaycaster = null;
    Vector3 currentDestination;  // required so we don't start moving in some arbitray direction or start moving, until we actually click.

    private bool isInDirectMode = false;  //TODO Consider making static later if other scripts 
										  //require this information.   

	void Awake()
	{
		cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
        cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick;
	}

	void Start()
    {
        thirdPersonCharacter = GetComponent<ThirdPersonCharacter>();
        currentDestination = transform.position;  
        aiCharacterControl = GetComponent<AICharacterControl>(); 
    }

    void ProcessMouseClick(RaycastHit raycastHit, int layerHit)
    {
        print("Click"); 
    }

    //TODO Make this get called again
    void ProcessDirectMovement()
    {
        Debug.Log("In direct movement mode");
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        // calculate camera relative direction to move:
        // calculate move direction to pass to character
        Vector3 cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
        Vector3 movement = v * cameraForward + h * Camera.main.transform.right;
        thirdPersonCharacter.Move(movement, false, false);

    }
}

The CameraRaycaster script is as follows:

public class CameraRaycaster : MonoBehaviour
{
	// INSPECTOR PROPERTIES RENDERED BY CUSTOM EDITOR SCRIPT
	[SerializeField] int[] layerPriorities;  // int value based, compared to OLD with layer types

    float maxRaycastDepth = 100f; // Hard coded value
	int topPriorityLayerLastFrame = -1; 

    public delegate void OnCursorLayerChange(int newLayer); // declare new delegate type
    public event OnCursorLayerChange notifyLayerChangeObservers; // instantiate an observer set

	public delegate void OnClickPriorityLayer(RaycastHit raycastHit, int layerHit); // declare new delegate type
	public event OnClickPriorityLayer notifyMouseClickObservers; // instantiate an observer set


    void Update()
	{

		// Check if pointer is over an interactable UI element
		if (EventSystem.current.IsPointerOverGameObject ())
		{
			NotifyObserersIfLayerChanged (5);
			return; // Stop looking for other objects
		}

		// Raycast to max depth and store in array named 'raycastHits', every frame as things can move under mouse
		Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
		RaycastHit[] raycastHits = Physics.RaycastAll (ray, maxRaycastDepth);

        RaycastHit? priorityHit = FindTopPriorityHit(raycastHits);
        if (!priorityHit.HasValue) // if hit no priority object
		{
			NotifyObserersIfLayerChanged (0); // broadcast default layer
			return;
		}

		// Notify delegates of layer change
		var layerHit = priorityHit.Value.collider.gameObject.layer;  // Too many Dots!
		NotifyObserersIfLayerChanged(layerHit);
		
        
		// Notify delegates of highest priority game object under mouse when clicked
		if (Input.GetMouseButton (0))
		{
            if (notifyMouseClickObservers != null)
            {
                Debug.Log(priorityHit.Value);
                Debug.Log(layerHit);
                notifyMouseClickObservers(priorityHit.Value, layerHit);
            }
		}
	}

	void NotifyObserersIfLayerChanged(int newLayer)
	{
		if (newLayer != topPriorityLayerLastFrame)
		{
			topPriorityLayerLastFrame = newLayer;
			notifyLayerChangeObservers (newLayer);
		}
	}

	RaycastHit? FindTopPriorityHit (RaycastHit[] raycastHits)
	{
		// Form list of layer numbers hit
		List<int> layersOfHitColliders = new List<int> ();
		foreach (RaycastHit hit in raycastHits)
		{
			layersOfHitColliders.Add (hit.collider.gameObject.layer);
		}

		// Step through layers in order of priority looking for a gameobject with that layer
		foreach (int layer in layerPriorities)
		{
			foreach (RaycastHit hit in raycastHits)
			{
				if (hit.collider.gameObject.layer == layer)
				{
					return hit; // stop looking
				}
			}
		}
		return null; 
	}
}

CursorAffordance Script:

[RequireComponent (typeof(CameraRaycaster))]
public class CursorAffordance : MonoBehaviour {

    [SerializeField] Texture2D walkCursor = null;
    [SerializeField] Texture2D targetCursor = null;
    [SerializeField] Texture2D unknownCursor = null; 

    [SerializeField] Vector2 cursorHotspot = new Vector2(0 , 0);  // The actual clicking point on the cursor

    //TODO solve fight between serialize and const
    [SerializeField] const int walkableLayerNumber = 9;
    [SerializeField] const int enemyLayerNumber = 10;
    [SerializeField] const int deadLayerNumber = 11;

    CameraRaycaster cameraRaycaster;

	void Start() {
        cameraRaycaster = GetComponent<CameraRaycaster>();
        cameraRaycaster.notifyLayerChangeObservers += OnLayerChangeForCursor;  // Registering to oberver list.
	}
	
	// Only called when layer changes.
	void OnLayerChangeForCursor(int newLayer) {
        
        switch (newLayer) // Used to say "switch (cameraRaycaster.currentLayerHit)" before we passed parameters with delegates.
        {
            case walkableLayerNumber:
                Cursor.SetCursor(walkCursor, cursorHotspot, CursorMode.Auto);
                break;
            case enemyLayerNumber:
                Cursor.SetCursor(targetCursor, cursorHotspot, CursorMode.Auto);
                break;
            default:
                //Debug.LogError("Don't know what cursor to show");
                Cursor.SetCursor(unknownCursor, cursorHotspot, CursorMode.Auto);
                return;
        }
        
	}
}

Thank you so much. I know this is a lot.

Can you confirm which line the error is on, in the above it states line 81, but that would be a closing brace at present, so it clearly isn’t that.

Note, if you don’t post full scripts then the line numbers don’t match up when other people check your code, case in point, all of the missing using directives etc.

If it’s easier, share the project with me and I’ll take a look. The forum will allow uploads of up to 10mb, your project is likely to be larger than that you’ll need to use a service such as Google Drive or Dropbox. Please zip the project files and then share the URL and I’ll take a look for you. :slight_smile:

I am sorry. I removed all my comments since I take deep notes during the lectures. The error is on the:

if (Input.GetMouseButton (0))
{
    if (notifyMouseClickObservers != null)
    {
        Debug.Log(priorityHit.Value);
        Debug.Log(layerHit);
        notifyMouseClickObservers(priorityHit.Value, layerHit);
    }
}

When double clicking the error in the console, it takes me to “notifyMouseClickObservers(priorityHit.Value, layerHit);” line.

Ok, can you share the project files with me please.

Sorry, didn’t know how to use GoogleDrive hahaha

https://drive.google.com/drive/folders/10x-oBDWlURUV-K1foDoyV7XjS6fCy9ro?usp=sharing

Hi,

I waited as long as I could but had to get some sleep in the end, grabbing a copy of your project now, will take a look :slight_smile:


Updated Wed Sep 05 2018 13:53

Just open up the project, I wasn’t sure which scene specifically you were running, so I started with Combat Sandbox, no errors. I placed the mouse over an enemy and it changed to a sword icon, I moved the mouse over the grass, it changed to an arrow - seems ok.

I then tried the Level 1 Village scene, and did encounter an error, in fact, I saw three errors;

image

Starting from the top, the first error is caused because this line of code;

cameraRaycaster.notifyMouseClickObservers += ProcessMouseClick;

is trying to act upon members of a variable, cameraRaycaster, which itself is null. The reason cameraRaycaster is null is due to the previous line in the Awake statement;

cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();

Here, you are using GetComponent and specifying the type as CameraRaycaster, but if you look at your Main Camera you’ll note that you don’t have a CameraRaycaster component attach to it, what you do have is a CameraRaycaster_Old component attached. It looks like you have probably renamed it but not changed this line above, and as such the variable is null because no component was found/returned.

The second error message is due to the exact same problem, again, no CameraRaycaster component could be found/was returned, due to the renaming.

And finally, the third error is caused because the delegate has no subscribers, because of the renaming, and the cameraRaycaster variable being null in the other two scripts, nothing actually ever subscribed.

So, to start to resolve this problem I would suggest you either remove the component flagged old if it shouldn’t be attached anymore, and then attach the correct one - or - rename this one accordingly so that your code can find the correct component.

After this, let me know where things are :slight_smile:


Updated Wed Sep 05 2018 14:47

If you add the CameraRaycaster.cs script component, and remove the CameraRaycaster_OLD.cs script component and then run the game you’ll be presented with the following error;

image

If we look at the code in question;

if (EventSystem.current.IsPointerOverGameObject ())
{
    NotifyObserersIfLayerChanged (5);
    return; // Stop looking for other objects
}

Line 54 is the first line, you are trying to acces the EventSystem, if you check your scene you’ll note that you don’t have one of these objects in the Hierarchy.

After adding one, and running the scene again, no errors are presented, instead your Debug.Log messages appear in the console;

image

The next issue is that you can’t really test the cursor affordance because of the position of your player in the scene, so, if you move your player, for now, to somewhere like 5, -100, 210, and then drag an enemy prefab into the scene, somewhere near to the player and run the game, you can now move your mouse over all of the layers you are interested in, the ground, the enemy and the unknown grey world space…


(video best played in full screen mode for clarity)

Okay! Sorry for the long wait! My college classes started ahahah
I’m looking at everything now, and Wow… Thank you. To be honest, I think I got all mixed up with version control at some point. I must have had trouble, reverted to an older build thinking my scripts had been added at that point, and they weren’t and dug myself a deeper trench…

I am going to be So much more careful in checking that all proper scripts are attached for now on.

Now, I am getting “Click” responses when I expect them in both scenes.

However, I am getting similar NullReferenceExceptions when moving my cursor from a walkable layer to an enemy layer. So, anytime I change layer, I get this null reference exception. This time the NullReference is in regard to “notifyLayerChangeObservers”.

I then did as you recommended above:

if (notifyLayerChangeObservers != null)
            {
                notifyLayerChangeObservers (newLayer);
            }

since “It’s typically a bad idea to call the delegate without checking first that it isn’t null, e.g. whether it has any subscribers.” On that change, SUCCESS! Everything works as it should!

But, I am still confused as to why this happens the way it does.

So here is where my understanding of delegates falls flat. I am wondering what the notifyLayerChangeObservers is read as at different times and why it does so.

I changed the code to:

void NotifyObserersIfLayerChanged(int newLayer)
	{
		if (newLayer != topPriorityLayerLastFrame)
		{
			topPriorityLayerLastFrame = newLayer;

            Debug.Log(notifyLayerChangeObservers);
            notifyLayerChangeObservers (newLayer);
		}
	}

On play, the console reads out:

1.)  CameraRaycaster+OnCursorLayerChange
UnityEngine.Debug:Log(Object)

2.) 9
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:68)

3.) Null
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:96)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:74)

4.)  9
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:68)

5.)  NullReferenceException: Object reference not set to an instance of an object
CameraRaycaster.NotifyObserersIfLayerChanged (Int32 newLayer) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster.Update () (at Assets/Camera and UI/CameraRaycaster.cs:74)

6.)  CameraRaycaster+OnCursorLayerChange
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:96)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:74)

7.)  5
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:57)

8.)  Null
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:96)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:74)

9.)  5
UnityEngine.Debug:Log(Object)
CameraRaycaster:NotifyObserersIfLayerChanged(Int32) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster:Update() (at Assets/Camera and UI/CameraRaycaster.cs:57)

10.)  NullReferenceException: Object reference not set to an instance of an object
CameraRaycaster.NotifyObserersIfLayerChanged (Int32 newLayer) (at Assets/Camera and UI/CameraRaycaster.cs:97)
CameraRaycaster.Update () (at Assets/Camera and UI/CameraRaycaster.cs:74)

On start of the game, the cursor is on a walkable layer.

So, on print-out 1 (PO1) the game checks that specific delegate (OnCursorLayerChange), and finds that the layer is valued at 9 (PO2).

At PO3, it reads Null. Why is the delegate reading null if we subscribed OnLayerChange to cameraRaycaster.notifyLayerChangeObservers on Awake? Or is the delegate only NOT null when notifying the observers, then switches back to null immediately after?

PO5 is the error that follows immediately after.

I move my cursor from a walkable layer to an enemy layer and the same thing happens over.

Just to be clear, All Issues Are Solved! I am just hoping that myself and all future confused readers can gain some insight into how this is functioning to cause errors.

Thanks!

Hi,

I’m looking at everything now, and Wow… Thank you.

You are very welcome :slight_smile:

On that change, SUCCESS! Everything works as it should!

Great! :slight_smile:

But, I am still confused as to why this happens the way it does.

I’m fairly certain this comes down to the ordering of your event functions.

I don’t have a copy of your project at this time, but I’m fairly certain not all of delegates were being set up in the Awake methods, and, unless you are using script ordering, you cannot be certain of the execution order of the scripts, e.g. which of their Start methods will be called first. My belief is that you are effectively trying to access something which hasn’t been set up yet.

With regards to your printouts ordering above, are you saying that since making the changes above you are still receiving errors?

It’s obviously hard for me to diagnose from this end with only that. The video I took was of the scene mentioned after correcting the issue and no further errors were displayed. I moved the mouse from walkable, unknown, enemy etc, no errors were displayed.

Happy to take another look if you want to share the entire project files again, including any changes you have made).

Privacy & Terms