# Unity: 'Quaternion.RotateTowards' animation is too fast (instantaneous)

I have a 'block-on-a-hinge' scenario where I just want it to smoothly rotate into the position of a stair step. I messed with this for a day and finally verified that my start and end angles are correct. The problem now is that the rotation animation is instantaneous instead of smooth over time. How can I adjust this so the rotation takes about 2 seconds? Plugging this into the 'maxDegreesDelta' argument felt intuitive but didn't do anything at all:

endAngle * Time.deltaTime / 2.0f


See the Documentation

video footage

The block itself is parented to this empty (hinge location):

The parent of all the hinges is just 10 blocks in a row rotated by $$\-\arctan{\left(\frac{4}{3}\right)}\$$ (so that is the angle to be undone):

Here is the script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpinningStep : MonoBehaviour
{
[SerializeField]
GameObject triggerObj;
bool disabled = false;
Quaternion startAngle;
Quaternion endAngle;
// Start is called before the first frame update
void Start()
{
startAngle = transform.localRotation; // correct
Debug.Log(startAngle.eulerAngles);
endAngle = startAngle * Quaternion.Euler(Mathf.Rad2Deg * Mathf.Atan(4.0f / 3.0f),0,0);
Debug.Log(endAngle.eulerAngles); // FINALLY correct!
}

// Update is called once per frame
void Update()
{
disabled = GameObject.Find(triggerObj.name).GetComponentInChildren<ItemBox>().disabled;

if (disabled)
{
gameObject.transform.rotation = Quaternion.RotateTowards(startAngle, endAngle, Time.deltaTime);
}
}
}


EPILOGUE - Corrections:

This script, based off of feedback and answer from DMGregory, eliminates initialization errors for the triggerObj and gives smooth time animation:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpinningStep : MonoBehaviour
{
[SerializeField]
ItemBox triggerObj;
bool disabled = false;
Quaternion startAngle;
Quaternion endAngle;
// Start is called before the first frame update

float rotationSpeed;

void Start()
{
float travelDegrees = Mathf.Rad2Deg * Mathf.Atan(4.0f / 3.0f);
startAngle = transform.rotation;
endAngle = startAngle * Quaternion.Euler(travelDegrees, 0, 0);

// Speed in degrees per second.
rotationSpeed = travelDegrees / 2.0f;
}

void Update()
{
if (triggerObj.disabled)
{
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
endAngle,
rotationSpeed * Time.deltaTime  // Our third argument is a degree limit.
);
}
}


I knew that how I was getting the trigger was garbage and wanted to ask, because I had trouble making it work, but I wanted to keep this question focused. Very silly oversight not using the name of the script as the type. Thanks!

• Your example at the top seems to use endAngle to represent an angle value, while your code sample at the bottom uses endAngle to represent a quaternion. So it's not clear how you've tried applying the angle speed adjustment to your code, since we can't just copy-paste between the two examples using the same name to refer to different things. Can you show us your attempt to adjust the speed of rotation in the context of your code sample? – DMGregory Jul 21 '19 at 15:28

It looks like you're mixing up & combining two different ways to rotate an object: Slerp and RotateTowards. Your code has elements of each, but uses neither correctly.

We could implement this with a Slerp, or Spherical Linear Interpolation (where you blend from a start orientation to an end orientation using a progress variable):

float rotationProgress = 0f;
float rotationDuration = 2f;

// These aren't angles, they're orientations, so let's name them to match their real role.
Quaternion startOrientation;
Quaternion endOrientation;

// Don't search for this by name every frame, just cache it.
ItemBox itemBox;

// ... (Skipping Start() for now)

void Update() {
if (itemBox.disabled)
{
rotationProgress = Mathf.Clamp01(rotationProgress + Time.deltaTime/rotationDuration);
gameObject.transform.rotation = Quaternion.Slerp(
startOrientation, // Out first argument is the start orientation.
endOrientation,
rotationProgress  // Our third argument is a progress fraction.
);
}
}


Or we could use RotateTowards (where you rotate from wherever you happen to be, using your current orientation, an end orientation, and a max rotation increment):

float rotationSpeed;

void Start() {
float travelDegrees = Mathf.Rad2Deg *  * Mathf.Atan(4.0f / 3.0f);
startOrientation = transform.rotation;
endOrientation = startOrientation * Quaternion.Euler(travelDegrees, 0, 0);

// Speed in degrees per second.
rotationSpeed = travelDegrees / rotationDuration;

// Try to replace this GameObject.Find with a different way to get your target object.
// Searching for objects by name is error-prone and scales poorly.
itemBox =  GameObject.Find(triggerObj.name).GetComponentInChildren<ItemBox>();
}

void Update() {
if (itemBox.disabled) {
transform.rotation = Quaternion.RotateTowards(
transform.rotation,             // We don't use startOrientation in this version.
endOrientation,
rotationSpeed * Time.deltaTime  // Our third argument is a degree limit.
);
}
}

• I used RotateTowards. I also realize that the reason I was getting initialization errors for triggerObj is that I set the type to GameObject when I should have set the type to the name of the script attached to it, which is ItemBox. Works beautifully now! – hatinacat2000 Jul 21 '19 at 15:58