Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Problem with rotating object by 90 degrees

Discussion in 'Scripting' started by TiZzzz, Jul 26, 2022.

  1. TiZzzz

    TiZzzz

    Joined:
    Nov 27, 2020
    Posts:
    3
    Hi everyone,
    Im rotating an object by 90 degrees in four direction and my script is working properly only in the case where the parent, the object AND the child have 0 rotations in all axis, but now i need to have a "base" rotation of the object that is not equal to 0 in all axis and i have multiple problems by rotating the object himself/ a parent or the child.
    I don't really know where to go from here, just to be clear my final goal is to rotate the object and see clearly(from the same perspective) the same number of faces of the cube.
    This is what i want to obtain

    ezgif.com-gif-maker.gif

    Here's the code im using:

    Code (CSharp):
    1. //This is what _value is
    2. case Direction.left: _inputValue = new Vector2(-1.00f, 0);
    3. case Direction.down: _inputValue = new Vector2(0, 1.00f);
    4.  
    5. //This calculate target rotation (rotation angle is 90)
    6. Vector3 rotationAxis = Vector3.Cross(_value, Vector3.forward);
    7. _targetRot = _cubeT.rotation * Quaternion.Euler(_cubeT.InverseTransformVector(rotationAxis) * rotationAngle);
    8.  
    9. //and this to apply the rotation in time
    10. _cubeT.rotation = Quaternion.Slerp(_cubeT.rotation, _targetRot, _currentTime / completeRotationTime);
    I hope this is the correct section of the forum.
    Thanks in advance
     
  2. kieemelicia

    kieemelicia

    Joined:
    Jul 25, 2022
    Posts:
    1
    Hi Tiz,

    with "base rotation" you mean that your starting position is similar to this, right?


    I'm asking this because I've a similar problem and an help would be much appreciated.

    I was thinking of creating a sort of parent/lodger object with the "base rotation" and inside it I have my visible object that won't be affected by this "base rotation". It will be relatively position to X=0, Y=0, Z=0 so that by turning only one of this coordinated by 90° it should work.

    Please let me know if you found out a way to solve the problem.
     

    Attached Files:

  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    rotation parameter affects the final (absolute) rotation of the object. The proper word is 'global' but I'm trying to convey the meaning of it. With hierarchical transformations everything is either 'global' or 'local' where local transformations are kept locally and global (true or final) transformation are computed on the fly.

    In your example you want to manipulate localRotation, not (global)rotation. Please try to learn more about the transform hierarchy (which is essentially beyond Unity, and more of a mathematical convention used by computer graphics since forever), otherwise you're in a world of pain.

    Obviously there is one special case where manipulating (global)rotation gives a proper behavior, and that's when every parent object is at 0, which is what you're getting. But consider that when you're changing (global)rotation, you're essentially asking Unity to (inversely) take all parents into account, so that it can properly adjust the localRotation, which is the actual parameter of the system.

    Maybe I can try to describe this in a simpler example with translation.
    Imagine you had objects Parent and Object where Parent sits at (0, 0) and Object sits at (6, 3).
    If you set Object's (global)position to (12, 3) its localPosition is in turn set to (12, 3) and everything seems fine.
    However if Parent was at (-1, 0), setting Object's (global)position to (12, 3) would set its localPosition to (13, 3) instead.
    Hopefully you can see why this is the case. You can also check this out because Transform component only addresses the local space.

    If I wanted to affect the local space of the Object, while disregarding the cumulative parent biases, I would simply set the localPosition directly, which is the fastest and the preferred way of doing this. Sometimes, however you really want to compensate against the hierarchy chain, and this is why we have (global)position, as a convenience, so you don't have to compute transformations on your own, but this has to be done anyway.

    All in all, all global properties such as position, rotation, and lossyScale (there is a mathematical reason why it is named liked so) are basically the global space counterparts which are only there for convenience, but you should be aware of the computational overhead that comes with them (especially with long hierarchy chains). You should be thinking in terms of localPosition, localRotation, and localScale which are the actual data in the transformation model.

    Behind the scenes, all Transform components are in fact 4x4 matrices with some GUI attached to them, but they do keep a set of redundant copies of human-readable parameters (called TRS for translation/rotation/scale). This is done to allow a human to easily access the coordinates, angles of rotation and scale factors, while keeping the numerically stable outcomes by recreating the matrices every time a change occurs. Keep in mind though that rotations aren't actually resolved through these angles and that under the hub everything boils down to matrix rotation, which is more akin to quaternions and is not human readable nor intuitive for the uninitiated.

    In my mind, the real issue why beginners constantly stumble on these basic aspects of computer 3D graphics, stems from the Unity's decision to appeal to everyone and appear as simple as possible (this is reflected in the names they've chosen for the properties), instead of expecting the developers to thoroughly understand the system, which is frankly inevitable.
     
  4. TiZzzz

    TiZzzz

    Joined:
    Nov 27, 2020
    Posts:
    3
    Your reasoning makes perfect sense, and I have thought and worked about it in acting on the local rotation but, unfortunately, without decent results.
    Maybe I need to take a different reference axis?
    The fact is that I am already transforming cross product into local space using InverseTransformVector but this doesn't seem to help when I try to apply it to local rotation.
    Thanks for the response
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,947
    Are you just flopping a cube around as if it was on a table?

    This was how I did it a few years ago. See enclosed package.
     

    Attached Files:

    TiZzzz likes this.
  6. TiZzzz

    TiZzzz

    Joined:
    Nov 27, 2020
    Posts:
    3
    That's cool, but the cube is stationary and floating in space like the example i provided in post #1
     
  7. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Transform.Rotate may be useful?
     
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Your issue likely has to do with Vector3.forward. You want the cube to rotate in an unbiased way, yet you refer to a cross with an axis that is valid only in two dimensions. The third one is ambiguous. You should check out the magnitude of your cross and if it's 0 (or near-zero), switch to another axis. I believe this will solve your problem, because cross will become invalid once you align your cube in such a way that _value is collinear to forward.

    Here's an example of computing a perpendicular vector in 3D, which introduces a similar problem.
    Code (csharp):
    1. static public Vector2 Perp(this Vector2 vec)
    2.   => new Vector2(-vec.y, vec.x).normalized; // just to illustrate how simple this is in 2D
    3.  
    4. static public Vector3 Perp(this Vector3 vec, float sqrEpsilon = kEPSILON_V3_SQR) {
    5.   var perp = vec.Cross(Vector3.forward);
    6.   if(!perp.IsZeroLength(sqrEpsilon)) return perp.normalized;
    7.   return vec.Cross(Vector3.right).normalized;
    8. }
    isZeroLength is implemented as
    Code (csharp):
    1. static public bool IsZeroLength(this Vector3 vec3, float sqrEpsilon = kEPSILON_V3_SQR)
    2.   => Vector3.Dot(vec3, vec3) < sqrEpsilon;
    where the epsilon constant is defined as
    public const float kEPSILON_V3_SQR = 3E-8f;


    (btw, the purpose of that dot product is to quickly compute a squared magnitude, and thus eliminate the need for square roots)

    edit:
    I'm haphazardly copy pasting and changing things on the fly, but still had to look twice -- that dot does not have be safeguarded with abs for this particular example because multiplying a vector with itself always returns a positive. so it's safe to assume the result is a valid squared magnitude. just fyi
     
    Last edited: Aug 4, 2022
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Another solution would be not to use Vector3.forward, but transform.forward instead. Instead of taking an absolute forward, you take the local forward of the cube, which will adapt to its rotation.

    (transform.forward is essentially the compact form of transform.localRotation * Vector3.forward.)

    edit:
    from this example alone you can tell where the paradox lies.
    I don't have time to analyze your code more thoroughly, I'm really only glancing, but your axis should probably be a properly defined local axis (or even global depending on what you want), i.e you don't need a cross to obtain it.