- Home /
Unable to fully rotate GameObject on tap
Hi guys,
I've got this bit of code in a script named TapRotationLogic.cs attached to a wall:
// Called when tapped
void OnTap () {
Debug.Log("Tapped to rotate");
Quaternion newRot = transform.rotation;
newRot.y += Mathf.Lerp(0.0f, 45.0f, 2.0f);
transform.rotation = newRot;
}
The above method is called from TBTap.cs, which is set to call the OnTap method. However, even though OnTap is getting called (I can tell from the debugging console), the wall is only rotating in a choppy manner a few degrees, back and forth. And that's if it even bothers to rotate at all, despite the relevant function firing perfectly fine.
The wall should rotate exactly 45 degrees relative to the y axis on every tap, and it should rotate smoothly.
What am I doing wrong? Please help.
MachCUBED
First off, if you read the documentation, you'll see that the maximum value the Lerp function wants is a 1. Secondly, unless you're lerping over time then you're going to get one result, whatever t is equal to.
Answer by Wolfram · Feb 02, 2013 at 02:07 AM
You don't mess with the components of Quaternions directly. They are sufficiently unrelated to angles. Never set or query something like transform.rotation.y directly, it won't help you.
Read up on how to use a Quaternion in the docs, or what it is on Wikipedia, if you're interested.
Convert to and from a Quaternion using convenience functions:
Vector3 angles=myquaternion.eulerAngles;
angles.y+=45;
myquaternion=Quaternion.Euler(angles);
EDIT: or, in case of transform.rotation, use transform.eulerAngles instead, for both query and assignment.
Ha! I was just about to come on here and correct myself...leave it to you to beat me to it! ;)
I modified the code to not modify the Quaternion components directly and came up with this:
IEnumerator RotateGameObject (float speed)
{
Debug.Log("Tapped to rotate");
Quaternion myQuaternion = transform.rotation;
Vector3 angles = myQuaternion.eulerAngles;
while (transform.rotation.y < myQuaternion.y)
{
angles.y = $$anonymous$$athf.Lerp(angles.y, 45, speed);
myQuaternion = Quaternion.Euler(angles);
transform.rotation = myQuaternion;
yield return 0;
}
}
Now the GameObject won't rotate at all.
Just set the eulerAngles of your transform to angles ins$$anonymous$$d of creating a Quaternion.
while (transform.rotation.y < myQuaternion.y)
{
angles.y = $$anonymous$$athf.Lerp(angles.y, 45, speed);
transform.eulerAngles = angles;
yield return 0;
}
You are still accessing the quaternion:
while (transform.rotation.y < myQuaternion.y)
Also note that using Lerp with a constant/small term such as "speed", you are creating an accelerated motion, a "spring" effect, where you start fast, and the rotation become slower the closer you get to the target value (similar to an "ease out"). For smooth constant speed, use Lerp as it's original intended, and as @vonni demonstrated (although all Lerp examples in the docs unfortunately are very bad examples, since there is no time control whatsoever.
Vector3 angles=transform.eulerAngles;
float startTime=Time.time;
float startAngle=angles.y;
float endAngle=startAngle+45; // -45 for left turn
while(duration>=Time.time-startTime){
angles.y=$$anonymous$$athf.LerpAngle(startAngle,endAngle,(Time.time-startTime)/duration);
transform.eulerAngles=angles;
yield return null;
}
Also, for angles, always use LerpAngle, to avoid wraparound problems at 360 degrees.
Another mistake is your code will always rotate towards 45 degrees absolute, not by 45 degrees. I fixed that, too. Browse through Unity Answers, there are tons of questions and solutions on how to rotate an object from A to B. Also consider using the free iTween, which can do ease in, ease out, and many other things.
Wolfram has solved it! The final code is:
IEnumerator RotateGameObject (float duration)
{
Debug.Log("Tapped to rotate");
Vector3 angles = transform.eulerAngles;
float startTime = Time.time;
float startAngle = angles.y;
float endAngle = startAngle + 45; // -45 for left turn
while(duration >= Time.time - startTime)
{
angles.y = $$anonymous$$athf.LerpAngle(startAngle,endAngle,(Time.time - startTime) / duration);
transform.eulerAngles = angles;
yield return null;
}
}
Thanks everyone!
Answer by Vonni · Feb 01, 2013 at 11:53 PM
function OnTap only executes once. You only rotate it what it would on 1 game frame.
and as iwaldrop said, the "t" of Mathf.Lerp (which is 2.0f in your case) controls the PROGRESS of the lerp.
So what you want to do is to setup a coroutine (look it up). And then run from 0 - 1 in t, where 0 is the start and the value one being the targeted rotation.
like shown in the docs:
// Fades from minimum to maximum in one second
var minimum = 10.0;
var maximum = 20.0;
function Update () {
transform.position = Vector3(Mathf.Lerp(minimum, maximum, Time.time), 0, 0);
}
notice that this works because it is inside update(which executes 1 time per game frame. And because of Time.time which will make this lerp finish after 1 second.
But you want it to happen OnTap, so call a COROUTINE function from there.
I tried implementing a coroutine, so that the code looks like this:
public class TapRotationLogic : $$anonymous$$onoBehaviour {
// Use this for initialization
void Start () {
}
// Called when tapped
void OnTap () {
StartCoroutine("RotateGameObject", Time.time);
}
IEnumerator RotateGameObject (float timeInterval)
{
Debug.Log("Tapped to rotate");
Quaternion newRot = transform.rotation;
newRot.y += $$anonymous$$athf.Lerp(0.0f, 45.0f, timeInterval);
transform.rotation = newRot;
yield return new WaitForFixedUpdate();
}
}
But I still have the same problem. Bummer.
try...
IEnumerator RotateGameObject (float speed)
{
Debug.Log("Tapped to rotate");
Quaternion targetRotation = transform.rotation;
targetRotation.y += 45;
while (rotation.y < targetRotation.y)
{
transform.rotation = $$anonymous$$athf.Lerp(transform.rotation, targetRotation, speed);
yield return 0;
}
}
The above code had a compile error, and my efforts to fix the errors resulted in the following code:
IEnumerator RotateGameObject (float speed)
{
Debug.Log("Tapped to rotate");
Quaternion targetRotation = transform.rotation;
targetRotation.y += 45;
Quaternion newRot = transform.rotation;
while (transform.rotation.y < targetRotation.y)
{
newRot.y = $$anonymous$$athf.Lerp(transform.rotation.y, targetRotation.y, speed);
transform.rotation = newRot;
yield return 0;
}
}
And I still have the same problem. Bummer.
hehe, dont use Time.time! By the time you proably tap the screen the value of Time.time is over 1, so it will never rotate smoothly. Time.time just keep going up and up and is the time spendt in game.You also have nothing in your coroutine that makes it keep going for more than 1 frame. You need a while statement or something to keep it executing the coroutine until t = 1; That will work, gl.
Your answer
Follow this Question
Related Questions
How to compute the change in vector when it's referential vector changes? 0 Answers
Workaround for Quaternion.eulerAngles 360 Degree Limit? 1 Answer
Applying force with respect to angle with the ground? 1 Answer
Rotate object based on hit normal + matching camera direction 0 Answers
Turning Vectors.... 2 Answers