Smoothly tilting an object from left to right?

Hey people…

I’m trying to do something that should be simple, but I’m horrible at math and really need some help here.

Basically, I am trying to make flat board that can be tilted left/right forward/backward. I want the tilt amount to be fixed, so that if you press LEFT the board will tilt left say 30 degrees and then straightens out. The same goes for the other directions. I’m trying to make a simple puzzle game where you slide a block around a grid to match up colors and crap.

What I need is a routine that will simply rotate the plane in a given direction SMOOTHLY, so that when it nears 30 degrees it decelerates and then as it moves from 30 degrees back to 0 it accelerates / decelerates as appropriate.

I’ve worked w/ various game scripting systems before and they always use a simple 360 degree rotation system. I’m now facing ‘euler angles’ and ‘quaternions’ or whatever. I have no clue what they do or why I need to use them over simply saying “tilt until you reach 30 degrees”.

Could somebody give me a dead simple example of how I would accomplish a tilt from FLAT to 30 degrees LEFT and back to FLAT with no jerkiness (i.e. smoothing at each change in direction)

Thanks!

	// Smoothly tilts a transform towards a target rotation.
	var smooth = 2.0;
	var tiltAngle = 30.0;
	function Update () {
		var tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
		var tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
		var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);
		// Damper towards the target rotation
		transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);
	}

I also added this snippet as a code example to the docs now.

Thanks - but I forgot to specify - I want this tilt sequence to be triggered with ONE TOUCH of the LEFT key. I don’t want the player to have to hold down the key to make this happen. So, TAP left, plane tilts to 30 degrees and then returns to 0. All as one sequence.

I am sure you’ll be able to figure that out with the snippet i posted. You just have to modify the tiltAroundZ and tiltAroundX variables accordingly.

Unfortunately, While I do understand that I can simply change the ‘tiltAround’ variables to get the thing to turn, I cannot follow what is going on exactly.

The important thing for me is to understand exactlyt what is going on here. I don’t know what the whole ‘Quaternion.Euler’ and ‘Slerp’ stuff is doing at all. I want to understand this script fully before I just plug it in.

As I said, I want a routine that will tilt my box a specified number degrees.

Basically, I want to be able to press Left (ONCE) and have the box tilt 30 degrees left (smoothly)and then (smoothly) return back to 0 degrees.

I can tell that this script is close to what I want, but as I said, I need to understand exactly what it is doing before I can move on to the rest of the stuff…

Thanks!

 // Smoothly tilts a transform towards a target rotation. 
   var smooth = 2.0; 
   var tiltAngle = 30.0; 
   function Update () { 
      var tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle; 
      var tiltAroundX = Input.GetAxis("Vertical") * tiltAngle; 
      var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ); 
      // Damper towards the target rotation 
      transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth); 
   }
      var tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle; 
      var tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;

These two lines determine the target tilt angle around the z and x axis.
In your case you want to change those two values, based on some key being pressed. So you probably have some logic to determine the tilt which you want to get to.

      var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);

From those two values we create a rotation, by specifying it as euler angles. This is essentially the same as entering the values in the transform inspector:

      transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);

Now we could just set the transform.rotation to target. However that willl lead to abrupt changes in the rotation which we want to smooth out. Thus we use the slerp function to smoothly interpolate towards the target rotation.

RIght, I think I am running into (yet another) problem I run into w/ the whole ‘Euler Angles’ thing. In the games I’ve scripted, I’m used to being able to check against rotations using simple ‘Float’ numbers - So I could simply check the objects rotation (like if it is at 45 degrees) and then perform operations based on that. I don’t see how I can do that with Euler angles.

The thing is, I need to execute this function as ONE function - not ‘as long as you press the key’ or whatever. And I want the board to move from 0-45 degrees in a given rotation all AT ONCE after I press a key a single time.

I’m having trouble figuring out the best way to do this. I’m not even concerned about smoothing things out at this point, I just want to figure out the most dead-simple implementation of this (from an understanding / illustrative point of view) and work up from there.

Basically

Detect LEFT ARROW press
Tilt Board From 0-45 degrees over the course of 3 seconds or whatever (this sequence cannot be interrupted)
Return to 0 Degrees
Wait for next ARROW press

Basically something like this:

var myAngle = Vector3.Angle(prevRotationVector, transform.rotation.eulerAngles);

if (myAngle == 45.0)
{
  //yep
}

http://unity3d.com/Documentation/ScriptReference/Vector3.Angle.html

One word, coroutine.

http://unity3d.com/Documentation/ScriptReference/index.Coroutines_26_Yield.html

HTH,
-Jeremy

What about adding a hinge joint with springs? Maybe it’ll work. I know that there’s a limit that you can type on the inspector, once reach it’ll stop and use the spring to bring it back to original position.

Ray

Actually that’s not such a good idea, what with floating point imprecision. It’s quite likely that myAngle will never equal 45.0. Generally a better idea to stick with < or > in this case, or use Mathf.Approximately.

To answer the question, you have a nice logical series of events, so it’s just a matter of coding it out. That is, check for input (better to use an axis then checking for a specific key though), if there has been input then have a routine that rotates right or left until the target rotation over a period of time, and then reverses back to 0. Among other possibilities, you could have a boolean variable called inProgress, and have this:

private var inProgress = false;

function Update () {
	if (!inProgress) {
		//check input here, set inProgress to true if there is
	}
	else {
		//do left/right rotation here, then set inProgress to false when done
	}
}

The rotation code can be the same for left or right, just have a variable (let’s say “rotateBy”) set to positive or negative depending on left or right input.

myInput = Input.GetAxis("Horizontal");
if (myInput != 0) {
	inProgress = true;
	if (myInput < 0) {
		rotateBy = -1;
	}
	else {
		rotateBy = 1;
	}
}

The rotation function would then have

myRotation += rotateBy * rotateSpeed * Time.deltaTime;

with of course

var rotateSpeed = .01;

at the top so it’s exposed to the inspector, so you can tweak that number and get the rotation as fast or as slow as you want.

Or at least that’s probably what I’d do.

–Eric

You guys are awesome (as usual here on the forums) - I’m wondering, so the following is NOT safe? (it seems to work OK)

		if ( Vector3.Angle(transform.rotation * Vector3.up, Vector3.up) < 45 )
		{
			transform.Rotate(Vector3.right * Time.deltaTime*(speed/30));	

		}

Executing this every frame update seems to tilt my platform right to 45 degrees, but there DOES seem to be some weirdness with returning it to 0 - it never quite returns to 0 exactly. I’m also confused as to why the LOCAL ROTATION for my object in the inspector window is represented by a long float number (it shows up as 3.542245e-08 after I return the board to 0 degrees using the same method as above) - Why doesn’t it just say 0? Or 5 when it is at 5 degrees?
[/code]

Nope, seems fine to me.

That’s floating point for you. :slight_smile: In other words, it’s normal and expected because of the way decimal floating point is computed on binary systems. 3.542245e-08 is also known as .00000003542245 which is really really close to 0…so close it doesn’t make any difference in practice, but it’s also why it’s not a good idea to make direct comparisons with floating point (the == thing). Less than or greater than is fine and will always work.

–Eric

I think I have implemented it similar to the way you guys are recommending. Although, I’m not sure how to do these events using coroutines.

Anyway, so, in the main FixedUpdate I am doing the basic check for the key press:

function FixedUpdate ()
{
	
	getControlInput();
	
	if (DIRECTION == RIGHT)
	{
		tiltRight();
	}
	
}

Then I’m simply executing the function (every frame) once the flag DIRECTION = RIGHT. This is where I’m wondering if I can simply make a sequence of events (coroutines?) to make the board tilt right then straight…

function tiltRight () 
{ 

	if (STRAIGHTEN == FALSE)
	{	
		if ( Vector3.Angle(transform.rotation * Vector3.up, Vector3.up) < 5 )
		{
			transform.Rotate(Vector3.right * Time.deltaTime*(speed/30));
		}
		
		else
		{
			STRAIGHTEN = TRUE;
		}
	}
	
	else
	{
		if ( Vector3.Angle(transform.rotation * Vector3.up, Vector3.up) != 0 )
		{
			transform.Rotate(Vector3.left * Time.deltaTime*(speed/30));	
		}
		
		else
		{
			STRAIGHTEN = FALSE;
			DIRECTION = NONE;
		}
	}
}

Oh, also to be honest - I’m not even sure what this does:

myAngle = Vector3.Angle(transform.rotation * Vector3.up, Vector3.up)

How is it that this returns a simple value in degrees? And why aren’t rotations represented in degrees in the Unity editor itself? It seems odd as every other level / game editor I have used made it pretty straightforward. I.e. setting rotation X to 90 degrees will rotate the object 90 degrees in X.

Vector3.Angle() returns the angle, in degrees, between two vectors.

The editor does work in degrees: if you set the Y rotation of a transform to 90, you’ll see the blue arrow pointing where the red arrow was. (You’ve rotated the transform 90 degrees around the Y axis.)

Right, that seems to be the case, except as soon as I enter 90 into the Y rotation box in the inspector it turns into “0.9999383” - why is that?