Reputation: 182
In unity, I have to calculate the angle of a spawning particle system according to the location where a different object hit it.
During this calculation I identify if the hit occurs on the bottom half of the impacted entity. This evaluation fails, and produces a different result.
I debugged this (using VS2017 and latest Unity 2018 release) and found that when I perform a 'watch' on the relevant expression, it evaluates to true
while when running the code itself it is evaluated to false
.
Initially when facing the issue, I managed to work-around it by altering the expression inside of the condition, but now it doesn't change it.
I have tried to pull out the expression to it's own value, but it displays inconsistency regarding the result. In the debugger it would, on rare occasions, evaluate correctly, while all other times (including not using the debugger) evaluate incorrectly.
SpriteRenderer Renderer = GetComponent<SpriteRenderer>();
Bounds Bounds = Renderer.bounds;
Vector3 Center = Bounds.center;
Vector3 HalfSize = Bounds.extents;
if (HitPosition.y != int.MinValue)
{
if (HitPosition.y < Center.y + HalfSize.y && HitPosition.y > Center.y - HalfSize.y)
{
HitPosition.y = Center.y + (HalfSize.y * (Center.y < 0 ? 1 : -1));
}
}
else
{
HitPosition.y = Random.Range(Center.y - (HalfSize.y * 0.2f), Center.y + (HalfSize.y * 0.9f));
HitPosition.x += (HitPosition.x < Center.x ? -1 : 1) * HitPosition.x / 100;
}
bool HitOnRight = HitPosition.x >= Center.x + HalfSize.x;
bool HitOnLeft = HitPosition.x <= Center.x - HalfSize.x;
float Angle = 0f;
if (Center.y - HalfSize.y - HitPosition.y >= 0) // <--- Issue
{
Angle = HitOnLeft ? 135f : (HitOnRight ? -135f : 180f);
}
else
{
Angle = HitOnLeft ? 90f : (HitOnRight ? -90f : 0f);
}
Since this is a sort-of generic method, that is used accord multiple entities in the scene I expect it to evaluate differently across various
In the cases in which the evaluation issue occurs, I expect it to evaluate to true
, in all other occurrences it should (and does) evaluates to false
.
Upvotes: 0
Views: 79
Reputation: 182
Following @McAden and another fellow who removed his answer (or had it removed), the issue was indeed the calculation itself.
I'll explain the reason for the issue, in the case that anyone in the future will come upon this.
Unity, when printing output using the Debug.LogXXX
functions, will round all values to 2 decimal points.
I tried to find offical documentation, but as it appears it is a part of C# itself.
float
(for those who come from a Java background) is identified as a Single
type object, and when printed are printed using the ToString()
method which narrows the result to 2 decimal places.
This can cause confusion when performing a standard Debug.LogFormat
print, because it will display 2 decimal places, and in cases such as mine, the difference will be so small that the result will be rounded to 0.
After deeper debugging and further diving into the actual value of the difference, I arrived to the point where the values I got were:
Center Y Value: 5.114532
Half Y Value: 0.64
Hit Y Value: 4.474533
Difference Result: -0.0000001192
This is a less than perfect example, because you can see that it will result in -0.000001 difference there were many instances until now that resulted in what appeared to be an absolute 0.
In cases where a loss of precision is likely to affect the result of a comparison, you can use the following techniques instead of calling the Equals or CompareTo method:
Call the Math.Round method to ensure that both values have the same precision. The following example modifies a previous example to use this approach so that two fractional values are equivalent.
using System; public class Example { public static void Main() { float value1 = .3333333f; float value2 = 1.0f/3; int precision = 7; value1 = (float) Math.Round(value1, precision); value2 = (float) Math.Round(value2, precision); Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); } } // The example displays the following output: // 0.3333333 = 0.3333333: True
IsApproximatelyEqual
;
Test for approximate equality instead of equality. This technique requires that you define either an absolute amount by which the two values can differ but still be equal, or that you define a relative amount by which the smaller value can diverge from the larger value.
Eventually, the root cause of this issue was that I had ignored the deeper precision values represented by the variables and thus found myself not understanding why the comparison is incorrect.
Thank you for your help, hope no one runs into this type of silly issue :)
Upvotes: 1
Reputation: 146
Operator precedence. Try some parentheses.
if ((Center.y - HalfSize.y - HitPosition.y) >= 0) // <--- Issue
{
Angle = HitOnLeft ? 135f : (HitOnRight ? -135f : 180f);
}
else
{
Angle = HitOnLeft ? 90f : (HitOnRight ? -90f : 0f);
}
Upvotes: 0