- Home /
Why is rotation and unparenting causing discrepancy between visual position and Inspector? Not a local vs global position confusion.
Hi,
In my scene I have two 1x1x1 unity cubes. The first, "cube", is at (0,0,0). The second, "cube1", is at (0,1,0). Both are tagged "Cube" and start in an empty parent object called "cubes". My script parents them in an empty game object positioned at (0,.5f,0), rotates that around the x axis 90 degrees after key input and then unparents them.
The problem starts after odd numbers of 90 degree rotations, even works great. User hits the key, the cubes are parented. The parent rotates 90 degrees. The cubes are unparented. Everything is visually in place and the Inspector has only a very tiny discrepancy. "cube" is at (0,.5,-.5) and "cube1" is at (0,.499,5). As small as the shift from .5 is it seems to makes all the difference. The cubes are parented again, the parent rotates, the cubes are unparented. Again, visually, everything is fine, the cubes appear to be in the expected places and "cube's" position in the Inspector is even as expected (0,1,0), but "cube1's" position in the inspector is (0,-1.192,0) which visually it is definitely not at. Also, when I attempt to adjust the y position, in the Inspector, for "cube1" at this point the number snaps to 0 as if it had been there all the time. After many rotations everything starts to break down.
It always starts with that tiny shift after an unparenting following an odd number of spins. unparenting after an even number of spins is no problem, everything still works great. Also, if I do not unparent the cubes at all everything works great. If this script was all I was trying to do I could just stop unparenting them, but it is a part of a larger script that requires cubes to be changing parents. Changing parents without unparenting first seems to cause the same effect.
Thanks in advance for any help.
using UnityEngine;
using System.Collections;
public class RotTester : MonoBehaviour
{
private GameObject[] cubes;
private GameObject pivot;
private GameObject endRotation;
private bool rotate;
private float t;
private float c;
void Start ()
{
rotate = false;
t = 0;
endRotation = new GameObject ("endRotation");
pivot = new GameObject ("pivot");
pivot.transform.position = new Vector3 (.5f, .5f, 0);
cubes = GameObject.FindGameObjectsWithTag ("Cube");
}
void Update ()
{
if (Input.GetKeyDown(KeyCode.Q))
{
foreach (GameObject cube in cubes)
{
cube.transform.parent = pivot.transform;
}
endRotation.transform.Rotate (Vector3.right, 90, Space.World);
rotate = true;
}
if (rotate == true)
{
pivot.transform.rotation = Quaternion.RotateTowards (pivot.transform.rotation, endRotation.transform.rotation, Time.deltaTime * 400);
if (t < 1)
{
t += Time.deltaTime;
} else
{
foreach(GameObject cube in cubes)
{
cube.transform.parent = null;
}
t = 0;
rotate = false;
}
}
}
}
Always format your code with 101010 button or your questions will be rejected. This has been done for you this time. ;)
Diagrams and videos really help in these situations. I'm also not sure exactly what the problem is - phrases like "breaks down" don't communicate anything meaningful. In what way does this problem manifest besides observing incorrect values in the inspector?
I don't have a clear picture of what you're communicating, but you appear to be confident that the problem has something to do with the floating point imprecision you mention.
$$anonymous$$aking some major assumptions about your game's nature, you could try a hack-y fix: At whatever moment is most convenient, such as immediately before the problem would otherwise manifest, snap the position components to the nearest "good" increment.
value = $$anonymous$$athf.Round(value / 0.5f) * 0.5f;
Is the number (if you click and scroll in the space) really maybe -1.192xxxxx e-10 ?
Answer by DiegoSLTS · Aug 01, 2015 at 03:22 AM
If you make the Inspector window wider you'll see that "-1.192" is not the whole value on that field, it probably is something like "-1.192E-20", which means... a really small number. Something similar goes for the 0.499 value, it probably is 0.499999... In practice is the same as 0.5.
Unity does some math when you do rotations, translations and parenting/unparenting (usually a product of a vector and a matrix), and those operations produce some errors inherent of the limited precision of floats.
If you really need those values to be exactly "0.5" or "0" you have to assign them manually (in the inspector or through a script), you can't expect Unity to assign rounded values.
Yep, i thinks that's the case ^^. Btw, just to illustrate what "-1.192E-20 actually looks like:
-0.00000000000000000001192
If you don't know that the scientific notation is you might want to look it up ^^.
Thank you everyone for the responses.
AlwaysSunny, I will remember that about the 101010, thanks. What I meant by "break down" was that the more the rotations and unparenting the greater the floating point imprecision becomes until eventually the cubes move into each other.
Owen Reynolds and DiegoSLTS, You are absolutely right, that is the case. I am familiar with scientific notation, but I did not realize that was happening here, thanks.
Great to know its something I'll have to assign the values another way.
Unfortunately, I don't think this is the cause of the problem I'm having in my larger script anymore. I'll post a new question about it in detail, but to sum it up I am making a rubik's cube type of thing, 3x3x3, using a similar code to the one in this post. The columns all work great, but when I add rotation to the rows the parents are no longer collecting all 9 cubes after odd numbers of rotations. When I noticed the issue I asked about in this post I'd hoped it might indicate something else I didn't understand that would be the key, but its not looking that way. The method of collection I'm using follows:
foreach (GameObject cube in cubes)
{
if (cube.transform.position.x == 2)
{
cube.transform.parent = rightPivot.transform;
}
}
Some of the cubes not being collected aren't having the floating point imprecision issue.
Thanks again! Any ideas about this one I'd love to hear.
Answer by Baste · Aug 02, 2015 at 12:13 PM
Don't rely on delta movements/rotations when you need exact possitions.
As the other commenters have pointed out, what's happening is that you get really small errors due to floating point rounding errors. These are inevitable - floats are implemented as numbers on the form (a * (2 ^ b)), so exact representations of most numbers are not possible.
So whenever you rotate something X degrees, there's a good chance that you're not rotating it exactly X degrees, but something very close to it. The small error isn't a problem when you only rotate once, but when you get many of those errors, the problem grows.
The solution is to not use delta rotations - ie. not add rotations together. So if you have rotated 90 degrees, and want to rotate 90 more degrees, don't add 90 to your rotation, but set it directly to 180.
If you're implementing a Rubiks Cube, each of the eight rotation bases has exactly 4 possible rotations. You can find those on start, store them, and then rotate towards the next one whenever the player rotates one of them. In addition, you could store the relative possition the cubes should have, and move the current child-cubes to those locations after a rotation, to compensate for rounding errors.
Thanks Baste!
That was very helpful. $$anonymous$$y cube is working great now.
Your answer
Follow this Question
Related Questions
Unable to set Rotation 1 Answer
Converting www.text or string to rotation or position 2 Answers
Calculating a new position based on where another object is facing 1 Answer
Add an offset to the position of an object in front of it 1 Answer
How can I identify a non-uniform scaled mesh, and fix it? 1 Answer