Quaternions
In Unity Rotations are stored as Quaternions. Quaternions work like vectors but their coordinates of x, y, z, and w are interdependent. These are different from the Euler angle rotation that you see in the inspector.
Because quaternion coordinates are interdependent you should never change them individually as you might when placing objects in the world initially. The reason Quaternions are the preferred method for rotating is because they allow for incremental rotation, without being subject to gimbal lock, which sometimes locks objects in 2D rotation when you need 3 dimensions.
There are 4 main quaternion functions used for rotation; AngleAxis, RotateTowards, LookRotation, and FromToRoatation.
To create the world gravity shift we rotate the platforms around the character.
Rotating platforms
Okay so for the rotation and Flipping in the game we need a rotation object that has the platforms as children.
- Create a empty object
- Rename it PlatformRotator
- Move the object to 0,0,0
- In the hierarchy move the platforms and the platform spawner controller that we made earlier into the PlatformRotator to parent it
Great, now for the hard part. It took me several weeks to get this rotation to go a perfect 90 with each flip as well as 180 degrees. In this game we want the user to press a key have the rotation go without use needing to hold down the key or press it a bunch of times. We could make it happen instantly, but for VR and UX purposes we don’t want to do that either. Now many beginner tutorials won’t explain how to do this. The answer is coroutines. Coroutines allow the rotation method to execute multiple times as it gives back control to other parts of the game.
The other difficult part finding which of the quaternion functions to use and how do you rotate spawned objects. Okay, so with that said lets start with the quaternion function we want to use and how to apply it to all my platforms.
Rotation Script
This rotation script will be attached to our PlatformRotator. What we want to do is get user input, see if the platforms are rotating, and if they aren’t, start the rotation.
Lets add a boolean to see if the platforms are rotating and a float to keep the degree of the angle we want to go to.
private bool rotating;
private float angle = 0.0f;
So to get user input we need to write the following inside the Update function.
void Update () {
if(Input.GetKeyUp(KeyCode.Q)){
if(!rotating) {
}
}
if(Input.GetKeyUp(KeyCode.E)){
if(!rotating) {
}
}
if(Input.GetKeyUp("space")){
if(!rotating) {
}
}
}
We also need to calculate the angle of exact rotation. In order to do a smooth rotation we will be adding and subtracting 90 degrees from the current angle, and restarting if it gets back to zero.
float getNextLeftAngle (float oAngle){
oAngle = oAngle + 90;
return oAngle;
}
float getNextFlip (float oAngle)
{
oAngle = oAngle + 180;
return oAngle;
}
float getNextRightAngle (float oAngle)
{
oAngle = oAngle - 90;
return oAngle;
}
Now we use the angles to pass into the coroutines. Every coroutine is an IEnumertor function. Which means we need to import the System.Collections library.
We need two coroutines, one for rotating, one for flipping. The coroutine works by having a while loop that returns yield return null. Yield return null tells the engine that when you reach this method again, start from the while loop.
IEnumerator FlipMe (float nextstep)
{
rotating = true;
float step = 232 * Time.smoothDeltaTime;
Quaternion fromAngle = transform.rotation;
Quaternion newRotation = Quaternion.Euler (new Vector3(0, 0, nextstep));
while (transform.rotation != newRotation) {//the original angle from the input key dot with 90 degree < != 0
transform.rotation = Quaternion.RotateTowards(transform.rotation, newRotation, step);
yield return null;
}
rotating = false;
}
I use the rotate towards quaternion function because we have a goal angle that we get closer to each time we run the coroutine.
We do the same for the Rotate coroutine. I have two methods because the step aka speed of rotation. I wanted the rotation to be slightly faster than flipping because in VR a full 180 degree flip is slightly more jarring, so slower speed is needed.
IEnumerator RotateMe(float nextstep) {
rotating = true;
float step = 500 * Time.smoothDeltaTime;
Quaternion fromAngle = transform.rotation;
Quaternion newRotation = Quaternion.Euler (new Vector3(0, 0, nextstep));
while(transform.rotation != newRotation){//the original angle from the input key dot with 90 degree < != 0
transform.rotation = Quaternion.RotateTowards(transform.rotation, newRotation, step);//newRotation;
yield return null;
}
rotating = false;
}
Now we need to call the coroutine in the Update function:
void Update () {
if(Input.GetKeyUp(KeyCode.Q)){
if(!rotating) {
angle = getNextLeftAngle(angle);
StartCoroutine(RotateMe(angle));
}
}
if(Input.GetKeyUp(KeyCode.E)){
if(!rotating) {
angle = getNextRightAngle(angle);
StartCoroutine(RotateMe(angle));
}
}
if(Input.GetKeyUp("space")){
if(!rotating) {
angle = getNextFlip(angle);
StartCoroutine(FlipMe(angle));
}
}
}
Now we add this script to the PlatformRotator Object. Run the code and now we see that each time we hit ‘q’, ‘e’, or ‘space’ the platfroms rotate appropriately. YAY!
Okay and now we are done with the biggest chunk of the game! Really all that’s left is adding game mechanics like ending the game and getting points.
Happy Coding!
-TheNappingKat