clamping a vertical rotation in Unity - c#

I started a FPS project. I have a capsule as a Player, an empty GO as the head and the camera is the child object of the head.
On the vertical Axis, I clamp my y-rotation to -70 (min) and 70 (max). Surprisingly the value does not get clamped.
[SerializeField]
private Transform playerHead;
float inputHorizontal;
float inputVertical;
float sensitivity = 50;
float yMin = -70;
float yMax = 70;
Vector2 direction; // movement direction
Transform playerTransform;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
private void Update()
{
inputHorizontal = Input.GetAxisRaw("Mouse X");
inputVertical = -Input.GetAxisRaw("Mouse Y"); // Inverted Input!
direction = new Vector2(
inputHorizontal * sensitivity * Time.deltaTime,
Mathf.Clamp(inputVertical * sensitivity * Time.deltaTime, yMin, yMax)); // The horizontal movement and the clamped vertical movement
playerTransform.Rotate(0, direction.x, 0);
playerHead.Rotate(direction.y, 0, 0);
}
The value
direction.y
gets clamped but I can still turn around my head by 360 degrees..

You are clamping your delta rotation - not your actual rotation.
Said in another way, direction is not the final rotation, it is the change in rotation. What you are effectively doing, is limiting the rotation to 70 degrees per frame.
You probably want to limit the actual rotation of playerHead, for example by adding the following lines to the end of update:
Vector3 playerHeadEulerAngles = playerHead.rotation.eulerAngles;
playerHeadEulerAngles.y = Mathf.Clamp(playerHeadEulerAngles.y, yMin, yMax);
playerHead.rotation = Quaternion.Euler(playerHeadEulerAngles);
There is also no reason to create a direction vector, when you are using each component separately anyway.

Clamp the final Y direction value not the current mouse movement -
[SerializeField]
private Transform playerHead;
float inputHorizontal;
float inputVertical;
float sensitivity = 50;
float yMin = -70;
float yMax = 70;
Vector2 direction; // movement direction
float currYDir = 0,prevYDir = 0;
Transform playerTransform;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
private void Update()
{
inputHorizontal = Input.GetAxisRaw("Mouse X");
inputVertical = -Input.GetAxisRaw("Mouse Y"); // Inverted Input!
direction = new Vector2(
inputHorizontal * sensitivity * Time.deltaTime,
inputVertical * sensitivity * Time.deltaTime); // The horizontal movement and the clamped vertical movement
playerTransform.Rotate(0, direction.x, 0);
currYDir = prevYDir + direction.y;
currYDir = Mathf.Clamp(currYDir, yMin, yMax);
playerHead.Rotate(currYDir-prevYDir, 0, 0);
prevYDir = currYDir;
}

Related

Movement without Riggibody

How to do without Rigidbody, rotate the object along the Y axis in both directions, and move the object forward in the right direction? How can you add acceleration and inertia to an object?
You can have a look at the Unity Documentation Here, or look at code provided.
float horizontalSpeed = 2.0f;
float verticalSpeed = 2.0f;
float speed = 10.0f;
void Movement()
{
// Get the horizontal and vertical axis.
// By default they are mapped to the arrow keys.
// The value is in the range -1 to 1
float x = Input.GetAxis("Vertical");
float z = Input.GetAxis("Horizontal");
// Make it move 10 meters per second instead of 10 meters per frame...
var moveVector = new Vector3(x, 0 , 0);
// Move translation along the object's z-axis
transform.Translate(moveVector * Time.deltaTime);
}
void Rotation()
{
// Get the mouse delta. This is not in the range -1...1
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float v = verticalSpeed * Input.GetAxis("Mouse Y");
transform.Rotate(v, h, 0);
}

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);

Rotate 2D object linearly

My 2D object is looking up (transform.up = (0,1)).
When I click somewhere in the screen, I want the object to look to where I clicked. I also want it to rotate linearly: it should rotate d degrees per second, implying that it takes twice the time to rotate twice the angle.
Here is my code:
void Update() {
if (Input.GetMouseButtonDown(0)) {
Vector2 wPos = cameraRef.ScreenToWorldPoint(Input.mousePosition);
StartCoroutine(LookAt(mouseScreenPosition));
}
}
public IEnumerator LookAt(Vector2 position) {
Vector2 direction = (position - (Vector2) transform.position).normalized;
float angle = Vector2.SignedAngle(direction, transform.up);
float startAngle = transform.eulerAngles.z;
for(float t = 0; t <= 1; t += Time.deltaTime * (180 / Mathf.Max(1, Mathf.Abs(angle)))) {
transform.eulerAngles = new Vector3(0, 0, Mathf.Lerp(startAngle, startAngle - angle, t));
yield return null;
}
}
The factor I multiply Time.deltaTime is 180 / Mathf.Max(1, Mathf.Abs(angle)), which says that the bigger the angle, the bigger the time it takes to rotate, but I don't know if I'm doing it right (it works) or if this is the better way to do it.
Define some rotationSpeed field:
public float rotationSpeed = 90f;
Use Quaternion.LookRotation to get the Quaternion you want to rotate towards. You want the local forward to point in the global forward direction and local up to point towards the target position, so use Quaternion.LookRotation(Vector3.forward,targetDirection):
public IEnumerator LookAt(Vector2 position) {
// casting to Vector2 and normalizing is redundant
Vector3 targetDirection = position - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(Vector3.forward, targetDirection);
Then use Quaternion.RotateTowards with the max amount of degrees you want to rotate in that frame until you are done rotating toward the goal rotation:
while (Quaternion.Angle(targetRotation, transform.rotation) > 0.01)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
time.deltaTime * rotationSpeed);
}
Altogether:
public float rotationSpeed = 90f;
public IEnumerator LookAt(Vector2 position) {
// casting to Vector2 and normalizing is redundant
Vector3 targetDirection = position - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(Vector3.forward, targetDirection);
while (Quaternion.Angle(targetRotation, transform.rotation) > 0.01)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
time.deltaTime * rotationSpeed);
}
}

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);

Categories