- Copied and modified from a CHiMERiC tutorial (http://chimeric.beyondunreal.com/tutorials/rotators.html .)
- Includes Jeremy T's info (link down) about loss of roll when converting to a vector.
Rotators are one of the Built-In Struct. But what the hell are they? Think angles. But instead of 360 degrees, a full circle is 65536 RUUs (Rotational Unreal Units). Every Actor has a Movement -> Rotation variable that determines (gasp) what direction the actor is pointing. Here's exactly what a rotator is:
struct Rotator { var() config int Pitch, Yaw, Roll; };
Rotators have 3 parts:
- Pitch (think up/down... like nodding your head "yes")
- Yaw (think left/right... like shaking your head "no")
- Roll (tilt your head to one side, so you're still looking the same direction, but one ear is pointing up and the other down).
You can modify the angles just by changing each of the values, but you can not modify an actor's rotation directly, you must use the SetRotation(newRotation) function, as shown below.
function ChangeRotation() { local Rotator newRot; // This will be our new Rotation newRot = Rotation; // Set newRot to our current Rotation newRot.Pitch += 32768; // Since 65536 = 0 = 360, half of that equals 180, right? newRot.Yaw -= 16384; // And half of that equals 90 and so on... SetRotation(newRot); // After we've done our tweaking go ahead and set the new rotation }
So what did we just do? We made something pitch 32768 RUU (180 degrees), and then turn 16384 RUU (90 degrees). Make sense? To get a vector of length one that points in the direction of the rotator:
local rotator myRot; local vector myVec; ... myVec = vector(myRot); ...
Always remember: the "roll" angle would rotate a vector about its own axis. But a vector is a mathematical line, if you do that it doesn't change at all. Therefore, myVec in the example above doesn't have any roll information. There's no way to figure out the roll component of myRot by looking at myVec. See also Typecasting.
To get a rotator that corresponds to a vector, type:
... myRot = rotator(myVec); ...
Since vectors don't have roll information, the "roll" of myRot will be zero, but the other parts will be correct.
Say you wanted myRot to point 90 degrees to the left...lets add another line:
... myRot.Yaw += 16384; // Now myRot points 90 degrees to the left of before ...
Easy stuff no? Just remember 65536 = 360 degrees, and to use SetRotation, and you'll do good. Rotators are useful for using angle based math, in many cases can be used in place of vector math to make some tasks much simpler. Thankfully Unreal already handles converting back and forth...
Musicalglass: OK, that will make it so when you open your map, it will be rotated in a new static position. Now how do you introduce the element of time into the equation? Like have your object start at zero and rotate to the new settings over a period of say, 10 seconds?
I know there are several different interpolation functions available as well, like linear and smooth. Could somebody please add a couple of simple equations to this page which show you how to do this? Thanks
Foxpaw: I think this page is more about rotators themselves, not about actor rotation but I suppose some iterpolation stuff could go here. You should be able to accomplish what you want by setting bRotateToDesired true, then setting a desiredrotation and a rotationrate, as well as a physics type that supports this type of rotation. Then the object will start rotating toward the angle in desiredrotation at the rate specified in rotationrate.
Contents
Functions
The following Global Function apply to rotators.
- GetAxes (rotator A, out vector X, out vector Y, out vector Z) [static]
- Assigns the axis vectors of A to X, Y and Z, in terms of Absolute Rotation, where X points forward, Y points right and Z points upwards. The UnrealEngine2 implementation of GetAxes gives invalid results when applied to the rotator 0,0,0. In this case three zero vectors will be returned.
- GetUnAxes (rotator A, out vector X, out vector Y, out vector Z) [static]
- Assigns the axis vectors of Rot(0,0,0) to X, Y and Z, in terms of A's Local Rotation. The same result as GetAxes(A,X,Y,Z) followed by Invert(X,Y,Z). If you think of X, Y and Z as 3x3 matrix, GetUnAxes returns the Wikipedia:transposed matrix relative to what GetAxes would return with the same rotator.
- rotator RotRand (optional bool bRoll) [static]
- Returns a random rotator. If bRoll is False the Roll component of the rotator is 0.
- rotator OrthoRotation (vector X, vector Y, vector Z) [static]
- Returns a rotator from three Wikipedia:orthogonal vectors.
- rotator Normalize (rotator Rot) [static]
- Returns the corresponding rotator with components between -32768 and 32767. (See A note on logging rotators below to find out why
log(Normalize(someRotator));
seems to disagree with this description.)
Operators
See Operators for the operators than can be used with a Rotator.
A note on logging rotators
When rotators are typecasted to string, e.g. when logging them, their Pitch/Yaw/Roll values are cramped into the range of 0 to 65535 by reducing them to 16bit unsigned integers. This does not mean the rotator has changed, just that e.g. log() is not giving you their true value! To get the true value write your own rotator to string function.
Example:
static final function string RotatorToString(rotator rot) { return "(" $ rot.Pitch $ "," $ rot.Yaw $ "," $ rot.Roll $ ")"; }
This example function will return a string representation of the rotator that is similar to the built-in typecast, but uses the full 32bit integer precision of the rotator's components.
Rotator Replication
Before a rotator is replicated, its component values are compressed into the range of a byte through the function ((value >> 8) & 0xFF). This has the advantage that a rotator only takes up 3 bytes of bandwidth for its value, but also limits the precision to 256 possible values per component. That's why a player's view rotation is generally not replicated as a rotator. The reduced Yaw and Pitch precision would hurt aiming accuracy.
Related Topics
(there's a table of URU to degrees on both those pages)
Discussion
Parallax: I just wanted to take this oppurtunity to mention that in 16 bit maths, 65535 (as an unsigned 16 bit number) and -1 (as a signed 16 bit number) both have the binary representation 0xFFFF. This means that if you add -1 to 1 (65535 to 1) you get zero (65535 + 1 = 65536 -> 0). In hex this looks like (0xFFFF + 0x0001 = 0x10000, then the leading 1 is truncated).
Furthermore, this means that as long as the numbers are constanly mapped to the values 0 to 65535 (as they seem to be) then -1 and 65535 act like the same number. If it helps you visualise it, if you turn around 359 degrees, you have effectively turned 1 degree in the opposite direction.
Of course, this whole argument no longer holds if you aren't talking about numbers that map from 0..65535. Fortunately in the case of rotators we are, whereas in the case of vectors or other representations of movement rather than rotation there is no such of logical wrap-around.
I would be highly suprised if Rotators are not normalised upon network replication, as transmitting three 16 bit numbers when that is all the information that is needed is much more logical than transmitting three 32 bit numbers. It is not really relevant wheither these numbers are signed or unsigned in the case of rotators for the reasons I just explained.
Bear in mind that when you subtract 1 from 50 (say) the computer (internally in the microprocessor) just calculates -1 (65535) and then adds it to 50, getting a result of 49 (after the inevitable overflow).
Foxpaw: One minor point of contention: UnrealScript rotators are a struct composed of three variables of type "int." An "int" appears to be represented natively with a 32 bit signed integer, not a 16 bit unsigned integer.
However, the part about replication may be true, as it would save some bandwidth.
Xian: EPIC tried to compress the rotation in the case of players (Controller for UE2 and PlayerPawn for UE1) by putting the yaw and pitch in an integer. The roll could easily be compressed in a byte. Though I doubt replicating a struct made up of an int and a byte would save too much; Still, depending on what you need it for (i.e. the ViewRotation doesn't require a roll), a replicated int should be better than a whole 3-int struct.
in case you wanted the code:
function CompressExample (rotator Rot, out int CompRot, out byte CompRoll) { CompRot = (((32767 & (Rot.Pitch / 2)) * 32768) + (32767 & (Rot.Yaw / 2))); CompRoll = ((Rot.Roll >> 8) & 255); } function DeCompressExample (out rotator Rot, int CompRot, byte CompRoll) { Rot.Pitch = (CompRot / 32768); Rot.Yaw = (2 * (CompRot - (32768 * Rot.Pitch))); Rot.Pitch *= 2; Rot.Roll = ((CompRoll << 8) | CompRoll); }
Wormbo: Rotators are already compressed for replication. For each component only 1 or 9 bits are replicated. The first bit signals, whether the component is zero and if it isn't, a byte with the value follows. That byte is calculated as Roll in the CompressExample function above. The (De)CompressExample functions are very handy though, if full precision is required for a rotation direction.
Random150: Rotators cause an interesting problem when you want to position a one vector relative to another. When the first vector is rotated, this means that the second requires rotation. Here is an example on how to do this:
local vector Loc; local rotator Rot; local float diff; diff = 256; //The distance you need between the two Loc = Location; //Loction of the Orginal Vector Rot = Rotation; //Rotation of the Orginal Rotator //This is the movement of the X/Y plane. You will have to change // sin/cos/tan to your required axis of movement Loc.X += diff * cos(Rot.Yaw * (pi / 32768)); Loc.Y += diff * sin(Rot.Yaw * (pi / 32768)); Loc.Z += diff * sin(Rot.Pitch * (pi / 32768));
computergod666: What exactly happens when you subtract two rotators? Does it simply subtract their components from each other, or is there something more complex involved?
Wormbo: The four arithmetic operators +, -, * and / are all applied component-wise to rotators and vectors. No special "magic" involved.