Detecting Property Changes

My little rant generated some questions that it may be easier to clear up here than 140 characters at a time.

Sometimes you want to execute some functionality when changing the value of a property. The key word there is “changing”. In order to know if the property changed you must compare the new value to the current value. It looks something like this:

// some field in your class
int value;  

void SetValue(int newValue)
{
  if (value != newValue)
  {
    value = newValue;
    // do some stuff that you only want to do if the value changes
  }
}

Works great. I mean, why do extra work if some other code is setting a value to the same value?

But does it work with floating point? I mean, we’re never supposed to do equality comparisons with floating point right? But in this pattern that’s not what we’re really doing is it? We’re just concerned that the value changed. How we arrived at the value being passed in isn’t relevant. This method just cares that the value is different from the previous value.

And this was the source of my frustration in my previous post. I wanted to know that the value had changed, not that it was close. And in my mind at least, using an equality operator is the proper way to do that.

Here is the code behind Unity’s equality operator. Inequality is the same with the opposite sign.

// UnityEngine.Vector3
public static bool operator ==(Vector3 lhs, Vector3 rhs)
{
  return Vector3.SqrMagnitude(lhs - rhs) < 9.99999944E-11f;
}

That works great. It isn’t a bug. It is valuable and even necessary functionality when you’re dealing with floating point and vectors and such. I just believe it doesn’t belong in the operator that’s testing for equality, because close is not the same thing as equals. But maybe that’s just me.

Here is some of the code for the Equals method.

// UnityEngine.Vector3
public override bool Equals(object other)
{
  Vector3 vector = (Vector3)other;
  return this.x.Equals(vector.x) && this.y.Equals(vector.y) && this.z.Equals(vector.z);
}

That is not close, that is equal. And that is what I expect from my equality operators.

This has nothing to do with dealing with floating point rounding errors or the wisdom of comparing calculated floating point values or anything like that. It has to do with expected behavior. But perhaps my expectations are unreasonable and that makes me a clown.

Should Unity change it? Probably not at this point. Who knows what it would break.

And that’s all I have to say about that.

Unity Vector Comparison Rant

I recently ran into an interesting Unity feature that I didn’t know about previously. I kept having an agent get stuck in the same place for no apparent reason. I’ve spent two days trying to track down the problem and finally ran across this in the least expected place – a function written almost on day 1 of this project, over a year and a half ago.

Surprising Vector Comparison

 

I’m trying to set my x position to 32. Since I’m generating an event when the position changes I only want to change it if the value being passed is actually different. Looking at the various values in the debugger the position is indeed different. Comparing the previous and new x values directly indeed shows that they are not equal, but when comparing the vectors themselves they come out as equal!

I went to answers.unity3d.com to ask a question about it, but did due diligence first and searched for a similar problem and found a reference to vector equality operator documentation. The key verbiage is:

This will also return true for vectors that are really close to being equal.

All I can say is wtf?

Now, I understand why they did this – floating point rounding and precision and all that. But to put that functionality in an actual equality operator, which is supposed to be testing for equality, is not the proper way to handle that. It is surprising and unexpected behavior. Adding an Approximately method would have been a much better choice, like they did for floats in Mathf.Approximately.

need my vector value to change to the integer values. I expect the equality operator to actually check for equality. The fact that it does not cost me two days trying to find a bug that should not have existed.

End of rant. On the plus side, I now have a nice and robust code tracing system for more easily tracking down this sort of thing in the future. Silver linings and all that.