Camera global rotation clamping issue Unity3D - c#

I have a problem, I'm making an smooth camera movement script, and I can't figure out how to clamp correctly camera rotation.
Actually, camera is a child of player object.
When WASD is pressed the player is rotated, but the camera doesn't follow this rotation because of the script.
var mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
mouseDelta = Vector2.Scale(mouseDelta, CursorSensitivity); //new Vector2(CursorSensitivity.x * smoothing.x, CursorSensitivity.y * smoothing.y));
if (m_doSmooth)
{
_smoothMouse.x = Mathf.Lerp(_smoothMouse.x, mouseDelta.x, 1f / smoothing.x);
_smoothMouse.y = Mathf.Lerp(_smoothMouse.y, mouseDelta.y, 1f / smoothing.y);
// Find the absolute mouse movement value from point zero.
_mouseAbsolute += _smoothMouse;
}
else
_mouseAbsolute += mouseDelta;
if (clampInDegrees.x > 0)
_mouseAbsolute.x = Mathf.Clamp(_mouseAbsolute.x, -clampInDegrees.x, clampInDegrees.x);
if (clampInDegrees.y > 0)
_mouseAbsolute.y = Mathf.Clamp(_mouseAbsolute.y, -clampInDegrees.y, clampInDegrees.y);
Camera.transform.rotation = Quaternion.AngleAxis(-_mouseAbsolute.x, Vector3.up) //transform.InverseTransformDirection(Vector3.up))
* Quaternion.AngleAxis(_mouseAbsolute.y, Vector3.right);
Well, the problem is easy, the clamped rotation values are global, because we set Camera.transform.rotation instead of Camera.transform.localRotation.
And this happens: Link to video
The script clamps values according to player rotation.
So if we have a rotation (eulerAngle) on the player on the X axis of 90 degrees, and clamp value is from 90 to -90, the clamped rotation will be 0, 180, instead of -90, 90.
But if I change from rotation to localRotation this happens: Link to video
I also tried the following:
Vector3 euler = Camera.transform.eulerAngles;
if (clampInDegrees.x > 0)
euler.x = ClampAngle(euler.x, -clampInDegrees.x, clampInDegrees.x);
if (clampInDegrees.x > 0)
euler.y = ClampAngle(euler.y, -clampInDegrees.y, clampInDegrees.y);
Camera.transform.eulerAngles = euler;
After setting localRotation... But this only make weird things. Like for example, when reaching the min clamp value at rotating the clamp returns the max clamp value.

Related

Mathf.clamp() function in Unity

I have a Cube(Player) in my game scene. I have written a C# script to constrain the movement(using Mathf.Clamp()) of the Cube so it does not leave the screen.
Below is my FixedUpdate() method from the Script
private void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.velocity = movement * speed;
rb.position = new Vector3(
Mathf.Clamp (rb.position.x, x_min, x_max),
0.5f,
Mathf.Clamp(rb.position.z, z_min, z_max)
);
}
Values of x_min, x_max, z_min, z_max are -3, 3, -2, 8 respectively inputted via the unity inspector.
PROBLEM
The script is working fine but my player(Cube) can move up to -3.1 units in negative X-AXIS (if I keep pressing the left arrow button) which is 0.1 units more in negative X-AXIS(This behavior is true for other axes too). It obviously clamps -3.1 to -3 when I stop pressing the button.
Why is this happening? Why doesn't it(Mathf.Clamp()) restrict the Cube to -3 units in first place?
Why am I getting that extra 0.1 units?
And Why is this value 0.1 units only? Why not more or less?
Your issue could be caused because you are setting velocity and then position, but before the next frame unity adds the velocity to your objects position. Which is why it ends up 0.1 units off.
To fix this, try resetting the velocity of the object to zero if it's about to leave your boundaries.
This is happening because the internal physics update moves the cube after you have put it in its clamped position. Mathf.Clamp() does restrict the cube to the expected position in the first place, as you can verify by putting a Debug.Log directly after you assign the position
You are getting the extra .1 units because you give the object a speed that gets applied after you clamp
It is 0.1 units because of the speed and the setting for Fixed Timestep (in Project Settings > Time). If you would set a higher speed it would be more. If you would lower the Fixed Timestep it would also be a bit more.
You already got the reason why this happens.
In order to fix the problem, you can use this code to prevent the cube to go outside your boundaries:
private void FixedUpdate() {
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
// Get new position in advance
float xPos = rb.position.x + movement.x * speed * Time.fixedDeltaTime;
float zPos = rb.position.z + movement.z * speed * Time.fixedDeltaTime;
// Check if new position goes over boundaries and if true clamp it
if (xPos > x_max || xPos < x_min) {
if (xPos > x_max)
rb.position = new Vector3(x_max, 0, rb.position.z);
else
rb.position = new Vector3(x_min, 0, rb.position.z);
movement.x = 0;
}
if (zPos > z_max || zPos < z_min) {
if (zPos > z_max)
rb.position = new Vector3(rb.position.x, 0, z_max);
else
rb.position = new Vector3(rb.position.x, 0, z_min);
movement.z = 0;
}
rb.velocity = movement * speed;
}

Instantiate Object on Raycast2D hit and rotate Instance

I want to move an instance of a gameObject along the outline of another gameobject. Its a 2D Project in Unity.
My current Code:
Vector3 mousePosition = m_camera.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(new Vector2(mousePosition.x, mousePosition.y), new Vector2(player.transform.position.x, player.transform.position.y));
if (hit.collider != null && hit.collider.gameObject.tag == "Player") {
if (!pointerInstance) {
pointerInstance = Instantiate(ghostPointer, new Vector3(hit.point.x, hit.point.y, -1.1f), Quaternion.identity);
} else if(pointerInstance) {
pointerInstance.gameObject.transform.position = new Vector3(hit.point.x, hit.point.y, -1.1f);
pointerInstance.gameObject.transform.eulerAngles = new Vector3(0f, 0f, hit.normal.x);
}
}
Unfortunately, the gameObject doesn't rotate towards the mouse and the position on the left side of the playerObject is also sometimes off. I tried to use Instantiate() with Quaternion.LookRotation(hit.normal), but no luck either.
Here a rough sketch of what I want to achieve:
Any help is appreciated. Thanks!
it's better to use Mathematical way instead of physical way(Raycasting),because in raycasting you have to throw ray several time for checking hit point and rotate your object,it makes lag in your game.
Attach this script to your instantiated object:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
public Transform Player;
void Update()
{
//Rotating Around Circle(circular movement depend on mouse position)
Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Player.position);
targetScreenPos.z = 0;//filtering target axis
Vector3 targetToMouseDir = Input.mousePosition - targetScreenPos;
Vector3 targetToMe = transform.position - Player.position;
targetToMe.z = 0;//filtering targetToMe axis
Vector3 newTargetToMe = Vector3.RotateTowards(targetToMe, targetToMouseDir, /* max radians to turn this frame */ 2, 0);
transform.position = Player.position + /*distance from target center to stay at*/newTargetToMe.normalized;
//Look At Mouse position
var objectPos = Camera.main.WorldToScreenPoint(transform.position);
var dir = Input.mousePosition - objectPos;
transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg);
}
}
Useful explanations
Atan2:
atan2(y,x) gives you the angle between the x-axis and the vector (x,y), usually in radians and signed such that for positive y you get an angle between 0 and π, and for negative y the result is between −π and 0.
https://math.stackexchange.com/questions/67026/how-to-use-atan2
Returns the angle in radians whose Tan is y/x.
Return value is the angle between the x-axis and a 2D vector starting at zero and terminating at (x,y).
https://docs.unity3d.com/ScriptReference/Mathf.Atan2.html
Mathf.Rad2Deg:
Radians-to-degrees conversion constant (Read Only).
This is equal to 360 / (PI * 2).

Imposing a limit on translation movement

I have a zoom function on my camera that works by moving it on the Z axis when I pinch my fingers on the screen. However, when the camera moves into any value greater than zero, it has adverse effects on the rest of my camera code (movement, orbiting).
I put in a place a bool that would stop my camera moving once it got to a certain value, but it makes it very jumpy. And if your pinching, the camera will still move past the value until you let go.
So what I'm trying to do now, is use Mathf.Clamp to limit the range it can move, but I'm unsure if I'm using it correctly. This is my method right now:
void CameraZoom()
{
// if fingers moved certain distance apart, zoom
float dot = Vector2.Dot(Input.GetTouch(0).deltaPosition.normalized, Input.GetTouch(1).deltaPosition.normalized);
if (dot < fingerDistance)
{
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// apply zoom to camera
transform.Translate(0, 0, -deltaMagnitudeDiff * perspectiveZoomSpeed);
// clamp movement
transform.position = new Vector3(transform.position.x, transform.position.y, Mathf.Clamp(0, 0, maxDistance));
}
}
}
I only want this to affect the Z axis, but when I zoom, my whole rig gets moved. What is it that I'm doing wrong?
Update
Ive changed my code to the following, but now when I zoom, it just jumps between two points and I can no longer zoom.
New code:
transform.Translate(0, 0, -deltaMagnitudeDiff * perspectiveZoomSpeed);
Vector3 pos = transform.position;
pos.z = Mathf.Clamp(transform.position.z, 0.0f, -200.0f);
transform.position = pos;
Check the documentation for the Mathf.Clamp. You are using the parameters in the wrong order, which breaks the internal implementation of the function. The min value should be the second and the max value should be the third. So changing your line to this should stop the odd behavior:
pos.z = Mathf.Clamp(transform.position.z, -200.0f, 0.0f);

Adjust Input.GetAxis To Face Player Axis

I am learning Unity and using the tutorials for Evac City with version 4.3. This sample is a top down 2D shooter that uses the mouse for rotation and the keyboard for movement using the arrows or WASD keys.
One thing that I was trying and not having much luck with was with changing the orientation of the movement. I would like the keyboard movement to be relative to the the direction of the player instead of to the world plane so that the W key will move you forward in the direction you are facing, the S key moves you back, the A key slides you left and the D keys slides you right.
The relevant parts of the code seem to be:
void FindPlayerInput()
{
// find vector to move
inputMovement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// find vector to the mouse
tempVector2 = new Vector3(Screen.width * 0.5f, 0, Screen.height * 0.5f); // the position of the middle of the screen
tempVector = Input.mousePosition; // find the position of the moue on screen
tempVector.z = tempVector.y; // input mouse position gives us 2D coordinates, I am moving the Y coordinate to the Z coorindate in temp Vector and setting the Y coordinate to 0, so that the Vector will read the input along the X (left and right of screen) and Z (up and down screen) axis, and not the X and Y (in and out of screen) axis
tempVector.y = 0;
inputRotation = tempVector - tempVector2; // the direction we want face/aim/shoot is from the middle of the screen to where the mouse is pointing
}
void ProcessMovement()
{
tempVector = rigidbody.GetPointVelocity(transform.position) * Time.deltaTime * 1000;
rigidbody.AddForce(-tempVector.x, -tempVector.y, -tempVector.z);
rigidbody.AddForce(inputMovement.normalized * moveSpeed * Time.deltaTime);
transform.rotation = Quaternion.LookRotation(inputRotation);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y + 180, 0);
print("x:" + transform.eulerAngles.x + " y:" + transform.eulerAngles.y + " z:" + transform.eulerAngles.z);
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
I have tried a few different things and recognize that the Input.GetAxis calls come from the keyboard mappings but not how to re-orient the movement to the player axis.
What you want to do is transform the input direction from the player's local space to world space, to put it in the good-to-know "space" terminology. You can do this with Transform.TransformDirection:
Vector3 worldInputMovement = transform.TransformDirection(inputMovement.normalized);
rigidbody.AddForce(worldInputMovement * moveSpeed * Time.deltaTime);
This works because TransformDirection will rotate the vector you give it to put it in world space, and AddForce expects a world-space vector.

Camera rotation in maze game depending of accelerometer

I'm still newbie to Unity... I'm trying to develop simple maze game. I want to roll my ball through maze, but I have to rotate camera left or right, otherwise the player can't see what's behind while rolling the ball on the left or on the right.
void Start ()
{
offset = transform.position - player.transform.position;
}
void LateUpdate()
{
transform.position = player.transform.position + offset;
}
I'm using low pass filter for accelerometer values:
Vector3 lowpass()
{
float LowPassFilterFactor = AccelerometerUpdateInterval / LowPassKernelWidthInSeconds;
lowPassValue = Vector3.Lerp(lowPassValue, Input.acceleration, LowPassFilterFactor);
return lowPassValue;
}
Accelerometer values are from -1 to 1 for each coordinate. Because of that I check my lowPassValue.x value and limit it. If it's positive ( >0.3 ), then the camera should turn right and if it's negative ( <-0.3 ) then camera should turn left.
Quaternion rotation = Quaternion.AngleAxis(45, Vector3.up* Time.deltaTime) ; // right
transform.rotation = rotation;
Quaternion rotation = Quaternion.AngleAxis(-45, Vector3.up * Time.deltaTime) ; // left
transform.rotation = rotation;
But then my offset doesn't work anymore, I can't see ball anymore. And camera rotation doesn't work as it should.
Is there any better solution for this or I'm using the wrong function?
Any help will be very appreciated!
You have a problem with your AngleAxis() where you are multiplying the Axis to rotate on, by Time.deltaTime. this means that essentially you are changing the axis of rotation, and it is unpredictable. Most likely you wont get your "Right" or "Left" rotation. Multiply deltaTime by the angle itself instead.
Also, Quaternions are to be multiplied if you want to apply an angle with them:
Quaternion rotation = Quaternion.AngleAxis(45 * Time.deltaTime, Vector3.up) ; // right
transform.rotation *= rotation;
Quaternion rotation = Quaternion.AngleAxis(-45 * Time.deltaTime, Vector3.up) ; // left
transform.rotation *= rotation;
or this could just be achieved as:
transform.Rotate(45 * Vector3.right * Time.deltaTime); // right
transform.Rotate(45 * -Vector3.right * Time.deltaTime); // left

Categories

Resources