ReferenceEquals(..., null) returns incorrect result in 6000.0.22f1

As stated in the title ReferenceEquals(Object, null) returns incorrect result when Object is a BlackboardVariable that has not been set. I suspect this causes NavigateToTargetAction to throw NullReferenceException instead of failing when provided with an unset target:

The only way to consistently get correct result is with IsUnityNull().

For reference check this code snippet:

using System;
using Unity.Behavior;
using Unity.VisualScripting;
using UnityEngine;

[Serializable, Unity.Properties.GeneratePropertyBag]
[Condition(name: "IsNull", story: "Test if [Object] or [Generic] is null", category: "Variable Conditions", id: "57fc890530d60f1d655f8d894f2abcbd")]
public partial class IsNullCondition : Condition
{
	[SerializeReference] public BlackboardVariable<GameObject> Object;
	[SerializeReference] public BlackboardVariable Generic;

	public override bool IsTrue()
	{
		// Object?.Value.ToString() == "null" returns True
		Debug.Log(string.Format("Object?.Value.ToString() == \"null\" returns {0}",	Object?.Value.ToString() == "null"));
		// Object?.Value.IsUnityNull() returns True
		Debug.Log(string.Format("Object?.Value.IsUnityNull() returns {0}",	Object.Value.IsUnityNull()));
		// Object?.Value == null returns True
		Debug.Log(string.Format("Object?.Value == null returns {0}", Object?.Value == null));
		// ReferenceEquals(Object?.Value, null) returns False
		Debug.Log(string.Format("ReferenceEquals(Object?.Value, null) returns {0}", ReferenceEquals(Object?.Value, null)));

		// Generic?.ObjectValue.ToString() == "null" returns True
		Debug.Log(string.Format("Generic?.ObjectValue.ToString() == \"null\" returns {0}", Generic?.ObjectValue.ToString() == "null"));
		// Generic?.ObjectValue.IsUnityNull() returns True
		Debug.Log(string.Format("Generic?.ObjectValue.IsUnityNull() returns {0}", Generic.ObjectValue.IsUnityNull()));
		// Generic?.ObjectValue == null returns False
		Debug.Log(string.Format("Generic?.ObjectValue == null returns {0}", Generic?.ObjectValue == null));
		// Generic?.ObjectValue is null returns False
		Debug.Log(string.Format("Generic?.ObjectValue is null returns {0}", Generic?.ObjectValue is null));
		// ReferenceEquals(Generic?.ObjectValue, null) returns False
		Debug.Log(string.Format("ReferenceEquals(Generic?.ObjectValue, null) returns {0}", ReferenceEquals(Generic?.ObjectValue, null)));
		return Object.Value.IsUnityNull() || Generic.ObjectValue.IsUnityNull();
	}
}

Hi @dZh0 ,

Thank you for the information. A bit of wisdom that was shared with me by @CianNoonan is that ReferenceEquals is unsafe when comparing vs a UnityEngine.Object. UnityEngine.Object actually has a wrapper over it and in this case you’re checking against the wrapper that Unity is making which is definitely not null. It’s a bit confusing, but in here you’re better off using ==.

Another bit of useful information: We’re making sure BlackboardVariable is never null, so you only need to check against its value :slight_smile:

And I just realised you’re pointing out our own code is implementing this wrong :woman_facepalming: It’ll be fixed! Thank you.

Fixed for 1.0.4 in NavigateToLocation, NavigateToTarget and Patrol nodes! :slight_smile: Thank you.

You are welcome.

I’m looking for a job BTW :stuck_out_tongue_winking_eye:

Hehe, if only we had headcount right now! :smiley: