I have my player rotating when it jumps, once it touches the ground it needs to quickly lerp to the nearest 90 degree angle to prevent weird behavior. Can I get some help please? I’ve been experimenting with a lot of different methods but can’t get it to work. (See line 52)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeController : MonoBehaviour
{
public Rigidbody rb;
public float jumpSpeed;
public int speed;
public int rotateSpeed;
private float rotation = 0;
private bool touchingSurface;
void Start()
{
rb = GetComponent<Rigidbody> ();
}
void FixedUpdate()
{
//Movement
rb.transform.Translate (Vector3.right * Time.deltaTime * speed, Space.World);
//Jump
if (Input.GetMouseButtonDown(0) && (touchingSurface)) {
rb.AddForce (Vector3.up * jumpSpeed);
}
}
//Rotate on touch
void OnCollisionExit(Collision collision)
{
touchingSurface = false;
}
void OnCollisionEnter(Collision collision)
{
touchingSurface = true;
}
void Update ()
{
if (!touchingSurface)
{
//Rotation
rotation += Time.deltaTime * -rotateSpeed;
transform.rotation = Quaternion.Euler (0, 0, rotation);
} else if (touchingSurface)
{
//lerp to nearest 90 degree angle
}
}
}
When you land, you probably want to identify that point (in OnCollisionEnter()) and calculate what the “nearest 90 degree point” would be, based on where your ‘rotation’ variable is now.
When you calculate that nearest point, you want to add 45 degrees to your rotation, then divide it by 90 degrees, use the integer value of that, and multiply that again by 90.
Now you gotta decide what to do: if you just instantly snap the rotation to that angle it may look funny. This means you have to think about how long it should take to align to that new 90-degree snap.
The easiest way to try that last part is to make a coroutine that iterates for a certain amount of time (say 0.25 seconds) and tweens (using Mathf.Lerp()) between the initial rotation and the 90-degree-aligned rotation, and “drives” your character to that orientation, then ends, making sure to set that final 90-degree-aligned rotation.
During the running of that coroutine, you may or may not want to “lock out” further jumping until the 90-degree snap has been reached. You might want to make sure that rotating it also does not cause OnCollisionExit() to trigger, which would then cause your “in-air rotation” routine to start firing… which is another reason for a lockout to occur during alignment to the ground.
With that last bit in mind, remember if a box hits at 45 degrees (lowest point) and you rotate it so one side aligns with the ground, the collider definitely will no longer be in contact with the ground, so you will get an OnCollisionExit()
Alright im not exactly sure what a coroutine is or how to use it, but I tried to use what you told to do and I got something that is almost what I want.
I created a private integer called nearestAngle and used that to do that math you told me to use and then applied it through a quaternion euler. This does snap the rotation when the player lands but put the player at the nearest 45 degree angle, not 90 degrees.
Edit: it is not snapping to 45 degrees perfectly. Everytime I land after jumping the number changes a bit, so 45 before jumping turns into 136.971 after landing from the first jump.
Is that just the physics solver skewing you slightly from the perfect 135 degree mark as you slide along the ground?
Also for the rounding math to work out, you need to assign (rotation + 45) / 90 to the integer variable (using a cast) and only after that multiply it by 90 degrees. If you insist on doing it in one statement,
nearestAngle = (int)((rotation + 45) / 90) * 90;
Doing it in one statement is always a cute programmer trick, but it only makes it a lot harder (or even impossible) to debug than doing the individual calculations one at a time so you can inspect intermediate values when it doesn’t quite work the way you expect.
Also, as your code stands from the snippets above, you should be getting compiler errors for trying assign a float expression to an int.
Okay so now the player snaps to 90 degree angles, but only 90 degrees (90/-90) it will not snap to 180 or -180 and will instead snap to the nearest 90 degree angle.
EDIT: I’m thinking there might have been some sort of misunderstanding, by nearest 90 degree angle I mean angles like (90, 180, 270, 360)
I’m quite new to unity, so it’s not really a ‘trick’ I just don’t know any other way of doing it
I always recommend using Quaternions over Euler, even in cases where you’d only rotate on one axis (where you wouldn’t suffer gimbal lock) if only to get more familar with Quaternion. While the Quaternion is complex math (literally, they involve “complex numbers” by name), the Quaternion class trivializes that. Theres very little benefit to using Euler over Quaternion. Below is your same class using Quaternion math instead of Euler math.
Quaternion targetRotation;
void OnCollisionEnter(Collision collision)
{
touchingSurface = true;
// Convert the current rotation into a look direction and constrain the direction to one of four
// cardinal directions. multiplying a quaternion by a vector3 is a special operator availible in
// the Quaternion class to get a direction relative to the rotation. Here we get the direction
// the y-axis ( hence Vector3.up) is pointing at.
Vector3 currentLook = transform.rotation * Vector3.up;// or you can use transform.up
// flatten direction to one world axis
if( Mathf.Abs(currentLook .x) > Mathf.Abs(currentLook .y))
currentLook.y = 0;
else
currentLook.x = 0;
currentLook = currentLook.normalized;
//now convert the new look direction back to Quaternion
targetRotation = Quaternion.LookRotation(Vector3.forward, currentLook);
}
void OnCollisionExit(Collision collision)
{
touchingSurface = false;
}
void Update()
{
float rotationDelta = Time.deltaTime * rotateSpeed;
if (!touchingSurface)
transform.Rotate(Vector3.forward,-rotationDelta);
else
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationDelta);
}
The code is using Quaternion math but its doesn’t look that complicated does it? if the target angles was something other than a multiple of 90 then we would have to resort to modulo Euler angles or perform some trig. but with 90 degrees we can do this purely in Quaternion.