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. Dismiss Notice

Question Problems with Rotation of a GameObject

Discussion in 'Scripting' started by iSleepzZz, May 24, 2023.

  1. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    201
    Okay so I am attempting to make a script in which when calling a method, for example: ShakeEffect() it will simply rotate the game object quickly and slowly bring it back to the original rotation.
    The rotation of the object depends on the player's location.
    The Code
    Code (CSharp):
    1.  
    2. public Transform shakeTransform;
    3. private Vector3 originalRotation;
    4. private Vector3 targetShake;
    5.  
    6. void Awake() {
    7.     if (shakeTransform) {
    8.         originalRotation = shakeTransform.localEulerAngles;
    9.         targetShake = originalRotation;
    10.     }
    11. }
    12.  
    13.  
    14. public void ShakeEffect() {
    15.     Vector3 dir = (shakeTransform.position - Player.localPlayer.transform.position).normalized;
    16.     targetShake = new Vector3(6 * dir.x, 0, 6 * dir.z);
    17. }
    18.  
    19.  
    20. void FixedUpdate() {
    21.     targetShake = Vector3.Slerp(targetShake, originalRotation, 8f * Time.fixedDeltaTime);
    22.     shakeTransform.localRotation = Quaternion.Euler(targetShake);
    23. }
    24.  
    The Issue
    It works perfectly if the object is at 0,0,0 rotation from the start. However, not every object is going to be at a original rotation of 0,0,0. The problem arises whenever I have an object, for example, at a rotation of 90,0,0. Once you do this, you'll notice in the editor that the X and Z rotation actually changes directions as shown below:


    Essentially, now I should be rotating on X->Z and Z->X. However, how can I account for this to make this script work with any rotation the game object starts out with?

    GameObject Structure
    Code (CSharp):
    1. Rock Ore (contains script)
    2.     ShakeTransform (3d model)
    Rock Ore: can be at any starting rotation
    ShakeTransform: starts at 0,0,0 local rotation
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    You shouldn't care about the original rotation. You shouldn't work with eulerAngles either.
    If I understand you correctly, you want to spin an object in multiples of 360. This is guaranteed to return the object to where it was.

    To produce this motion, you need to maintain some time interval. The passage of time is what defines your motion. Then your rotation is a function of time, where you absolutely know your start and end values: 0 and 2*N*Pi radians. You could do this with Slerp, but AngleAxis is much much faster.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class SexySpinner : MonoBehaviour {
    4.  
    5.   const float TAU = 2f * MathF.PI;
    6.  
    7.   [SerializeField] [Range(1, 3)] int _revolutions;
    8.   [SerializeField] float _timeInterval; // in seconds
    9.   [SerializeField] Vector2 _anglesOfAxis; // in degrees
    10.  
    11.   float _time;
    12.   Quaternion _identity;
    13.   Vector3 _axis;
    14.  
    15. #if UNITY_EDITOR
    16.   void OnValidate() {
    17.     _axis = Quaternion.Euler(_anglesOfAxis.x, _anglesOfAxis.y, 0f) * Vector3.up;
    18.   }
    19. #end if
    20.  
    21.   void Awake() {
    22.     enabled = false;
    23.     _axis = Quaternion.Euler(_anglesOfAxis.x, _anglesOfAxis.y, 0f) * Vector3.up;
    24.   }
    25.  
    26.   void OnEnable() {
    27.     _identity = transform.localRotation;
    28.     _time = 0f;
    29.   }
    30.  
    31.   void OnDisable() {
    32.     transform.localRotation = _identity;
    33.   }
    34.  
    35.   void Update() {
    36.     _time += Time.deltaTime;
    37.  
    38.     var t = _time / _timeInterval;
    39.  
    40.     if(t >= 1f) {
    41.       enabled = false;
    42.       return;
    43.     }
    44.  
    45.     var angle = (float)_revolutions * TAU * easeInOutCubic(t);
    46.     transform.localRotation = Quaternion.AngleAxis(angle, _axis) * _identity;
    47.  
    48.     static float easeInOutCubic(float n)
    49.       => n < .5f? 4f * n * n * n : 1f - MathF.Pow(-2f * n + 2f, 3f) / 2f;
    50.   }
    51.  
    52. }

    Once you hit Play, enable the script to see the effect.
    (Haven't tested it, might contain dumb errors.)

    Edit: Added OnValidate so you can play with 'Angles Of Axis' in play mode.
     
    Last edited: May 24, 2023
  3. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    201
    Hi there! Thank you so much for the reply. However, I am not trying to rotate in circular motion like a "360" revolution with N iterations.
    I am simply trying to rotate the object away from the player facing it (so only in rotations of X and Z axis) and then it will simply start lerping back to it's original rotation.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I have a hard time understanding what you want. Can you provide a moving example of this motion?

    I truly cannot imagine this. What does "rotating the object away" mean?
    If you rotate away from your neighbor, that usually means something because you have a face and eyes, and you turn those away. A rock or a tree do not have eyes and can't possibly turn away. What is the purpose of such rotation?
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I mean if the whole point is to be able to turn X to face toward Y and vice versa, to turn X to face away from Y, then you can simply use LookRotation, but instead of setting the rotation to the player you set it to the object.
    Code (csharp):
    1. object.transform.localRotation = LookRotation((object - player).normalized, Vector3.up);
     
  6. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    201
    Sure, here is a clip of what I want to happen:



    This is rotating the rock's rotation away from the player.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Ahhh, haha, well you need to be more specific with your descriptions. So that's why you called it a 'shake'. Yes it is technically a rotation, but no one would ever call this a rotation, it's a slight tilt at best. I had to watch this twice. Ok nvm, as long as we understand each other, great that you had a video.

    I'll do something in the next post.
     
  8. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    201
    Haha yes it is more accurately a "tilt" effect.
    I do not want any rotation on Y axis at all.
    Sorry for my lack of clear communication.
     
  9. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    You could also just create an animation.
     
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Here's something that should work.
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class RockSmash : MonoBehaviour {
    4.  
    5.   [SerializeField] [Min(1E-1f)] float _timeInterval; // in seconds
    6.   [SerializeField] [Range(0f, 90f)] float _tiltAngle; // in degrees
    7.  
    8.   float _time;
    9.   Quaternion _identity;
    10.   Quaternion _yRotation = Quaternion.identity;
    11.  
    12.   void Awake() {
    13.     enabled = false;
    14.   }
    15.  
    16.   void OnEnable() {
    17.     _identity = transform.localRotation;
    18.     _time = 0f;
    19.   }
    20.  
    21.   void OnDisable() {
    22.     transform.localRotation = _identity;
    23.   }
    24.  
    25.   public void Animate(Quaternion yRotation) {
    26.     if(enabled) OnDisable();
    27.     _yRotation = yRotation;
    28.     enabled = true;
    29.   }
    30.  
    31.   public void Animate(float tilt, Quaternion yRotation, float timeInterval) {
    32.     if(enabled) OnDisable();
    33.     _tiltAngle = tilt;
    34.     _yRotation = yRotation;
    35.     _timeInterval = timeInterval;
    36.     enabled = true;
    37.   }
    38.  
    39.   void Update() {
    40.     _time += Time.deltaTime;
    41.  
    42.     var t = _time / _timeInterval;
    43.  
    44.     if(t >= 1f) {
    45.       enabled = false;
    46.       return;
    47.     }
    48.  
    49.     var tiltedRot = Quaternion.AngleAxis(_tiltAngle, computeAxisDir());
    50.     var finalSlerp = Quaternion.Slerp(tiltedRot, Quaternion.identity, easeOut(t));
    51.  
    52.     transform.localRotation = finalSlerp * _identity;
    53.  
    54.     static float easeOut(float x) => 1f - MathF.Pow(1f - x, 5f);
    55.   }
    56.  
    57.   Vector3 computeAxisDir() => Quaternion.AngleAxis(_yRotation, Vector3.up) * Vector3.right;
    58.  
    59. #if UNITY_EDITOR
    60.   void OnDrawGizmos() {
    61.     var p = transform.position;
    62.     var l = computeAxisDir();
    63.     Gizmos.color = Color.cyan;
    64.     Gizmos.DrawLine(p, p + l);
    65.   }
    66. #endif
    67.  
    68. }
    I've also included a gizmo for the axis, so that you can preview whether it's in the right spot (it should be horizontal and where the pivot is).

    However, the trick with this is that you need to supply the proper yRotation for the animation. Because the Y rotation depends on from where the player is hitting the rock. To get this rotation you can do
    Code (csharp):
    1. var yRotation = Quaternion.LookRotation((rock - player).normalized, Vector3.up);
    (If I got this backward, simply swap rock and player; edit: oh and make sure both points have the same y value)

    and then you call this script (which should be attached to rock) like so
    Code (csharp):
    1. GetComponent<RockSmash>().Animate(10f, yRotation, 1f);
    Where 10 is the maximum tilt in degrees, and 1 is the time duration before it springs back.
    It should conserve any kind of prior orientation your rock already had.

    Edit: Added Animate(Quaternion yRotation) when you want to supply only Y rotation but keep the other two arguments as they were (including the ones set in the inspector).
     
    Last edited: May 24, 2023
    kdgalla likes this.