Flipping sprite by y axis when rotated past a certain point - c#

I'm new to unity, and an amateur C# user. I have a submarine sprite that I would like to flip by the y-axis when rotated more than 90 degrees, and less than -90 degrees so that it won't be upside down. It's rotated by mouse movement which I'll give the code if necessary. I'm not sure why but, this doesn't seem to work. Any help would be appreciated!
Code:(rot90 is a bool)
if (transform.rotation.z > 90 & transform.rotation.z >-90)
{
rot90 = false;
}
if (transform.rotation.z < 90 & transform.rotation.z < -90)
{
rot90 = true;
}
if (rot90 == true)
{
Vector3 scale = transform.localScale;
scale.y = -22;
transform.localScale = scale;
}
if (rot90 == false)
{
Vector3 scale = transform.localScale;
scale.y = 22;
transform.localScale = scale;
}

transform.rotation is a Quaternion!
A Quaternion has not only 3 but 4 component x, y, z and w, and they all move in ranges between -1 and 1.
Your conditions can never become true!
You could use the eulerAngles and do e.g.
var zAngle = transform.eulerAngles.z;
// clean out the angle to a value between -180 and +180
while(zAngle > 180) zAngle -= 360;
while(zAngle < -180) zAngle += 360;
Vector3 scale = transform.localScale;
scale.y = Mathf.Abs(zAngle) > 90 ? -22 : 22;
transform.localScale = scale;
in case you are using a SpriteRenderer component you should rather go for SpriteRenderer.flipY
var zAngle = transform.eulerAngles.z;
// clean out the angle to a value between -180 and +180
while(zAngle > 180) zAngle -= 360;
while(zAngle < -180) zAngle += 360;
// You should of course rather cache this reference e.g. in Awake only once
// and then reuse it here
GetComponent<SpriteRenderer>().flipY = Mathf.Abs(zAngle) > 90;

Related

Clamping my camera to a min and max value

I tried many approaches, watched tutorials but can't wrap my head around to make the clamp work with my code that I have right now.
So I can zoom in and out but infinitely, how to clamp the camera to max value -5 which is slightly above my player, and min value around -15 which is far above my player.
// Control the distance between the object && camera
private void ZoomIntoObject(float maxZoom, float minZoom)
{
float scrollInput = Input.GetAxis("Mouse ScrollWheel");
//zPos = scrollInput;
// zPos = Mathf.Clamp(zPos, minZoom, maxZoom);
// While scrollwheel
if (scrollInput > 0.0f)
{
// Move forward on the z-as && Clamp maxZoom
transform.position += transform.forward;
} else if (scrollInput < 0.0) {
// Move forward on the z-as && Clamp maxZoom
transform.position -= transform.forward;
}
Debug.Log(zPos);
}
Calculate your new position, clamp its z component, then assign to position.
private void ZoomIntoObject(float maxZoom, float minZoom)
{
float scrollInput = Input.GetAxis("Mouse ScrollWheel");
Vector3 newPos = transform.position;
if (scrollInput > 0.0f)
{
newPos += transform.forward;
} else if (scrollInput < 0.0) {
newPos -= transform.forward;
}
newPos.z = Mathf.Clamp(newPos.z, minZoom, maxZoom);
transform.position = newPos;
}

How to clamp camera rotation axis correctly?

I am trying to clamp the X and Y axis of my camera, which I have managed to do. However, when the clamped value reaches the MAX threshold it will jump back to the MIN threshold!
Any idea what is causing this in my code?
private void ClimbingLookRotation()
{
if (input.mouseX != 0 || input.mouseY != 0f)
{
orientation.rotation *= Quaternion.AngleAxis(input.mouseX, Vector3.up);
orientation.rotation *= Quaternion.AngleAxis(input.mouseY, Vector3.right);
}
var rotX = orientation.eulerAngles.x;
var rotY = orientation.eulerAngles.y;
rotX = Mathf.Clamp(rotX, 1, 25);
rotY = Mathf.Clamp(rotY, 200, 355);
orientation.eulerAngles = new Vector3(rotX, rotY, 0);
}
Any help would be appreciated!
Thankyou.
I found something the works well so thought I'd answer in case it would help anyone!
public static float ClampAngle(float angle, float min, float max)
{ //Normalises angle value passed in between -180 to 180 to make the angle clampable
angle = NormalizeAngle(angle);
if (angle > 180)
{
angle -= 360;
}
else if (angle < -180)
{
angle += 360;
}
min = NormalizeAngle(min);
if (min > 180)
{
min -= 360;
}
else if (min < -180)
{
min += 360;
}
max = NormalizeAngle(max);
if (max > 180)
{
max -= 360;
}
else if (max < -180)
{
max += 360;
}
return Mathf.Clamp(angle, min, max);
}
public static float NormalizeAngle(float angle)
{ //If the angle is above or below 360 degrees, normalise it
while (angle > 360)
angle -= 360;
while (angle < 0)
angle += 360;
return angle;
}
And then just call the ClampAngle method where ever you need to clamp a value, for example:
private void ClimbingLookRotation()
{
if (input.mouseX != 0 || input.mouseY != 0f)
{
orientation.rotation *= Quaternion.AngleAxis(input.mouseX, Vector3.up);
orientation.rotation *= Quaternion.AngleAxis(input.mouseY, Vector3.right);
}
var rotX = orientation.eulerAngles.x;
var rotY = orientation.eulerAngles.y;
rotX = HelperFunctions.ClampAngle(rotX, -10, 25); //Here
rotY = HelperFunctions.ClampAngle(rotY, 200, 340); //And here
orientation.eulerAngles = new Vector3(rotX, rotY, 0);
}
I've called it in my camera rotation function to clamp both rotX and rotY, which is later applied to the rotation of my orientation game object.
Thankyou again to Ruzihm for your help before :)
You could re-center your euler angles around the center value then clamp the difference:
float centralX = 13f;
float extentX = 12f;
float centralY = 277.5f;
float extentY = 77.5f;
private static float ClampEuler(float val, float center, float extent)
{
return center + Mathf.Clamp((val - center + 360f) % 360f, -extent, extent);
}
private void ClimbingLookRotation()
{
if (input.mouseX != 0 || input.mouseY != 0f)
{
orientation.rotation *= Quaternion.AngleAxis(input.mouseX, Vector3.up);
orientation.rotation *= Quaternion.AngleAxis(input.mouseY, Vector3.right);
}
var rotX = orientation.eulerAngles.x;
var rotY = orientation.eulerAngles.y;
rotX = ClampEuler(rotX, centralX, extentX);
rotY = ClampEuler(rotY, centralY, extentY);
orientation.eulerAngles = new Vector3(rotX, rotY, 0);
}
By the way you may want to do orientation.rotation *= Quaternion.AngleAxis(input.mouseY, orientation.right); to rotate around the local right instead of global right. Rotating around global right may produce a rolling effect over time which you may not want.

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

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

Clapming rotation of child objects in unity

I have a game object that contains another game object that should be able to rotate towards target (imagine a tank turret). And so I've created the below script:
public class Rotator : MonoBehaviour {
public GameObject _enemy;
void Update () {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var step = 2 * Time.deltaTime;
var target = Quaternion.LookRotation(targetDir.normalized, Vector3.up);
var actual = target * Quaternion.Inverse(transform.parent.rotation);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, step);
targetRotation.eulerAngles = ClampRotation(targetRotation.eulerAngles);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Mathf.Clamp(eulerAngles.x > 180 ? eulerAngles.x - 360 : eulerAngles.x, -180, 180);
var y = Mathf.Clamp(eulerAngles.y > 180 ? eulerAngles.y - 360 : eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
}
Objects setup:
The rotation of the object named "parent" is 90deg on the Y axis, everything else is not rotated.
Clamping on the y axis works well - the rotation stays between -45 and 45 degrees. The rotation however doesn't work on the x axis (with, or without clamping).
So the goal here is that when I move the cube left or right, the red one rotates between [-45,45] degrees around the Y axis and when I move it top or down, the red one rotates between [-180,180] degrees around the X axis.
I had some success using the LookAt method of the Transform clas, but for some reason if I try to manually modify eulerAngles of a localRotation it suddenly looses the possibility to rotate backwards on the X axis even though I'm only doing something to the Y values...
After long hours of trial and error and browsing the internet frenzily, I managed to find an answer that I could tailor to my needs. Word of comment to the first if statement of the Clamp method - this is useful if I want the object to also be clamped in it's inverted position (if the target is behind it):
void Update() {
transform.LookAt(_target.transform);
var rotation = transform.localRotation;
var eulers = ClampRotation(rotation.eulerAngles);
transform.localEulerAngles = eulers;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Clamp(eulerAngles.x, -60, 60);
var y = Clamp(eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
return angle;
}
EDIT:
As it turns out, sometimes it's better to create your own fine-grained solution, you can modify more easily, so to anyone who is interested, you can do what I wanted to do with the below code as well:
void Update() {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var target = Quaternion.LookRotation(targetDir.normalized, transform.up);
var actual = Quaternion.Inverse(transform.parent.rotation) * target;
actual.eulerAngles = ClampRotation(actual.eulerAngles);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, 8 * Time.deltaTime);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 newRotation) {
var x = Clamp(newRotation.x, -179, 179);
var y = Clamp(newRotation.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
if (Mathf.Abs(angle) == 360) {
angle = 0;
}
return angle;
}

Categories