Camera rotation jitters after a certain threshold (video) - c#

So I have this setup:
Camera rotation is clamped on the Y axis. It can only rotate from -75 to 75 degrees left and right, and its rotation is controlled by the mouse.
Player body can rotate around its Y axis, and its Y rotation value is added to -75 and 75 degree camera clamps in order to keep the camera rotation space in front of the player body. Example: If the player rotates 90 degrees on the Y axis, the clamp values would change to 15 and 165 degrees.
The problem arises when the player body exceeds 359 degrees on the Y axis because camera clamp values jump from 284 and 434 back to -75 and 75, which causes a noticeable snap in camera movement. How can I eliminate this snap?
Video example - Note the camera clamps (Limit L, Limit R) in the MouseLook script after 00:40
*The handleBodyRotation method just rotates the player body to face the red cube which is a child of the camera.
public class MouseLook : MonoBehaviour
{
public Transform CameraBox;
public Transform lookAtPivot;
public Transform lookAtObject;
public Transform playerBody;
public float sensitivity;
public float bodyRotSpeed;
float xRotation = 0f;
float yRotation = 0f;
public float limitL;
public float limitR;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
void handleCamRotation()
{
float mouseX = Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -45, 35);
yRotation += mouseX;
yRotation = Mathf.Clamp(yRotation, limitL, limitR);
//Handle and clamp Camera, lookAtPivot, and lookAtObject rotation
transform.rotation = Quaternion.Euler(xRotation, yRotation, 0f);
//Handle left limit and right limit
limitL = -75 + playerBody.transform.rotation.eulerAngles.y;
limitR = 75 + playerBody.transform.rotation.eulerAngles.y;
}
void handleBodyRotation()
{
Quaternion targetRotation = Quaternion.Euler(playerBody.transform.rotation.eulerAngles.x, lookAtObject.transform.rotation.eulerAngles.y, playerBody.transform.rotation.eulerAngles.z);
playerBody.transform.localRotation = Quaternion.Lerp(playerBody.transform.localRotation, targetRotation, bodyRotSpeed * Time.deltaTime);
}
void Update()
{
//Camera Rotation (clamped)
handleCamRotation();
//Body Rotation
if (Input.GetKey(KeyCode.W))
{
handleBodyRotation();
}
}
}

Your problem is the value of Transform.eulerAngles
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
In particular the eulerAngles will always return a value between 0 and 359.9999... !
You say yourself it jumps to 284 and 434 and then back to -75 and 75.
Why?
Because
359 - 75 is 284 and 359 + 75 is 434
0 - 75 is -75 and 0 + 75 is 75.
Your eulerAngles.y value was somewhere close to the wrap point at 359.999... <-> 0.
I think you would have to wrap around these values like e.g.
limitL = -75 + playerBody.transform.rotation.eulerAngles.y;
limitR = 75 + playerBody.transform.rotation.eulerAngles.y;
if(limitR > 360) limitR -= 360;
if(limitR < 0) limitR += 360;
if(limitL > 360) limitL -= 360;
if(limitL < 0) limitL += 360;
And not sure but I guess you also would have to clamp like
if(limitR > limitL)
{
yRotation = Mathf.Clamp(yRotation, limitL, limitR);
}
else
{
yRotation = Mathf.Clamp(yRotation, limitR, limitL);
}
Don't know how Unity handled that case .. might be redundant.
Note: Typed on smartphone but I hope the idea gets clear

Related

How can I limit the rotation on the Y axis so that the player can't continuously spin the camera in Unity

I have an upcoming project that I have to present on Monday and this is the last bug I have to resolve. It would be nice if someone could help me out, and could teach me how to apply an axis limiter, thanks in advance everyone.
The issue is that the camera can spin 360 degrees
Below is my current code that controls the camera
public float sensitivity = 30.0f;
private GameObject cam;
float rotX, rotY;
private void Start()
{
sensitivity = sensitivity * 1.5f;
Cursor.visible = false; // For convenience
}
private void Update()
{
rotX = Input.GetAxis("Mouse X") * sensitivity;
rotY = Input.GetAxis("Mouse Y") * sensitivity;
//Apply rotations
CameraRotation(cam, rotX, rotY);
}
private void CameraRotation(GameObject cam, float rotX, float rotY)
{
//Rotate the player horizontally
transform.Rotate(0, rotX * Time.deltaTime, 0);
//Rotate the players view vertically
cam.transform.Rotate(-rotY * Time.deltaTime, 0, 0);
}
Adding logical clamping to this makes it quite a bit clearer to read through, this method clamps the vertical rotation between 60 and -60 degrees, the comments should help in modifying it if you need
I ended up jumping into Unity to test it this time, instead of going off the top of my head
void CameraRotation(GameObject cam, float rotX, float rotY)
{
//Rotate the player horizontally
transform.Rotate(0, rotX * Time.deltaTime, 0);
//Rotate the players view vertically
cam.transform.Rotate(-rotY * Time.deltaTime, 0, 0);
//Grab current rotation in degrees
Vector3 currentRot = cam.transform.localRotation.eulerAngles;
//If (x-axis > (degreesUp*2) && x-axis < (360-degreesUp))
if (currentRot.x > 120 && currentRot.x < 300) currentRot.x = 300;
//else if (x-axis < (degreesDown*2) && x-axis > (degreesDown))
else if (currentRot.x < 120 && currentRot.x > 60) currentRot.x = 60;
//Set clamped rotation
cam.transform.localRotation = Quaternion.Euler(currentRot);
}
Goodluck with the project

Unity Rotation of Parent object along 1 Axis

I'm working on developing a game, and my current roadblock is Camera Rotation. I want the mouse to control the camera, and when the camera turns, I want to rotate the player as well. However, the code I am using rotates the player object COMPLETELY with the player, causing the player to turn like this:
The player rotating forward not only looks strange, but causes clipping problems with the terrain. How do I make my code only rotate the player along one axis, while allowing the camera to rotate along any axis (to allow the audience to look around, without the player object being turned upwards or down).
this is the code that is rotating the player:
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
and this is the complete script I am using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraOrbit : MonoBehaviour
{
protected Transform _XForm_Camera;
protected Transform _XForm_Parent;
protected Vector3 _LocalRotation;
protected float _CameraDistance = 10f;
public float MouseSensitivity = 4f;
public float ScrollSensitvity = 2f;
public float OrbitDampening = 10f;
public float ScrollDampening = 6f;
public bool CameraDisabled = false;
// Use this for initialization
void Start()
{
this._XForm_Camera = this.transform;
this._XForm_Parent = this.transform.parent;
}
void LateUpdate()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
CameraDisabled = !CameraDisabled;
if (!CameraDisabled)
{
//Rotation of the Camera based on Mouse Coordinates
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0)
{
_LocalRotation.x += Input.GetAxis("Mouse X") * MouseSensitivity;
_LocalRotation.y += Input.GetAxis("Mouse Y") * MouseSensitivity;
//Clamp the y Rotation to horizon and not flipping over at the top
if (_LocalRotation.y < 0f)
_LocalRotation.y = 0f;
else if (_LocalRotation.y > 90f)
_LocalRotation.y = 90f;
}
//Zooming Input from our Mouse Scroll Wheel
if (Input.GetAxis("Mouse ScrollWheel") != 0f)
{
float ScrollAmount = Input.GetAxis("Mouse ScrollWheel") * ScrollSensitvity;
ScrollAmount *= (this._CameraDistance * 0.3f);
this._CameraDistance += ScrollAmount * -1f;
this._CameraDistance = Mathf.Clamp(this._CameraDistance, 1.5f, 100f);
}
}
//Actual Camera Rig Transformations
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
}
}
A picture of my heirarchy:
(Please note that Player is an empty object containing all of my scripts and physics components, whereas the GFXs component is simply the model, with an animator component clean of any physics components.)
It sounds like what you want is basically to rotate the player around its local Y axis until it is facing the same direction as the camera. You can find out if things are facing the same direction using Vector3.SignedAngle and passing in the axis that you care about. Here is some sample code that should get you going.
float turnThresholdDegrees = 15;
float playerTurnSpeed = 1;
// Get delta angle in degrees on the Y axis between the direction player is facing and the direction camera is facing
float angleDelta = Vector3.SignedAngle(player.transform.forward, camera.transform.forward, Vector3.up);
// Calculate a rotation speed, clamping it to 0 when the angle delta is < some threshold
float rotateSpeed = Mathf.Abs(angleDelta) > turnThresholdDegrees ? Mathf.Sign(angleDelta) * playerTurnSpeed : 0;
// Rotate the player around its local Y axis by the rotation speed
player.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);

Prevent player rotation from changing last second in Unity

Unity using C#
In an top down game where the camera rotation is locked, I have made a character controller which is restricted to 8 axis and will only rotate in 45 degree increments. Think Links awakening. Everything works except when moving in a diagonal direction(using WASD).
When I let go of W & D for example, whichever key I let go of last even if by a fraction of a second, the player will end up facing in that direction rather than the intended up and to the right diagonal direction.
I am looking to modify the following code to add some sort of buffer so that the player remains facing the intended direction when I let go of the 2 keys. I'd like to keep it as simple as possible.
public float defaultSpeed = 6f;
CharacterController controller;
private Vector3 playerMovement;
Vector2 input;
float angle;
Quaternion targetRotation;
Transform cam;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
cam = Camera.main.transform;
}
// Update is called once per frame
void Update()
{
Movement();
}
public void Movement()
{
//Get inputs
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
//Only update if keys are pressed
if (Mathf.Abs(input.x) < deadZone && Mathf.Abs(input.y) < deadZone) return;
//Get camera angle: input to radians - radians to degrees - degrees plus the camera angle - limit angle to 45 degree increments
angle = Mathf.Atan2(input.x, input.y);
angle = Mathf.Rad2Deg * angle;
angle += cam.eulerAngles.y;
angle = Mathf.Round(angle / 45) * 45;
//Rotate Character
targetRotation = Quaternion.Euler(0, angle, 0 );
transform.rotation = targetRotation;
//Move Character
transform.position += transform.forward * defaultSpeed * Time.deltaTime;
}
Make a courotine that checks if a key is released, and waits, say 0.5 seconds for another key to be released. If another key is released, then stay in that direction. Hope it works. Good luck.

Rotate object with RotateAround and give it a limit

I have an object in my scene that rotate (RotateAround) by the mouse swipe. and I want to give the object some rotation limits, for example -45 and 45 degree for the X axis, so when its rotation become 45 degree it can't go beyond it.
So I tried Mathf.Clamp method in my script as you see bellow, and its working fine for the Y axis, the object rotate around his X axis and didn't go beyond the Y limits. but in the X axis, when the object's Y rotation reach O it change immediately to 30 degree with a weird rotation! Can you please tell what's wrong in my code?
Rotation scripts:
float sensitivity = 10f;
Vector3 firstPressPos;
Vector3 secondPressPos;
float minRotationX = 45;
float maxRotationX = 100;
float minRotationY = 30;
float maxRotationY = 30;
void Update () {
if (Input.GetMouseButtonDown(0))
{
//save began touch 2d point
firstPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
}
if (Input.GetMouseButton(0))
{
//save ended touch 2d point
secondPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
if (firstPressPos != secondPressPos)
{
float RotX = Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
float RotY = Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
transform.RotateAround(Vector3.up, RotX);
transform.RotateAround(Vector3.right, -RotY);
Vector3 angles = transform.eulerAngles;
angles.x = Mathf.Clamp(angles.x, minRotationX, maxRotationX);
angles.y = Mathf.Clamp(angles.y, -minRotationY, maxRotationY);
angles.z = 0;
transform.eulerAngles = angles;
}
}
}
In the editor, rotation values are between -180 and 180, but in transform.eulerAngles they are actually between 0 and 360.
So you need to adjust the value of your angle before clamping it.
if(angles.y > 180)
{
angles.y -= 180f;
}
angles.y = Mathf.Clamp(angles.Y, minY, maxY);

Rotate item: 90 degree rotations

I found this script that lets you click on an item then drag to rotate it, but it rotates based on where your finger is, what can I do to rotate the object 90 degrees like you would in Tetris?
public class RotateDrag : MonoBehaviour {
void OnMouseDrag(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
}
Example
In photoshop when you want to rotate an item in a layer you can hold Shift and it will snap to 45° angles as you rotate.
I however would like 90° angle snapping.
What you need to do is just take the direction from the point selected, as the sign on the angle and apply that to a 90 degree rotation thus:
float ang = 90.0 * Math.Sign(Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg);
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);

Categories