How to increase rotation angle with Unity using Quaternion - c#

In my Unity scene, I'm trying to rotate a cube wrt the head movement. Here's my code:
m_cubeName.rotation = Quaternion.Lerp(m_cubeName.rotation, m_Camera.rotation, Time.deltaTime);
It seems to work and rotates exactly the same angle as the head. I want to use a multiplying factor so that when the head rotates, say 1 degree, the cube rotates 2 degrees.
So how do I convert the qauternion rotation value to something I can multiply with a factor?

To rotate the cube by a factor of 2, just rotate it twice:
Quaternion doubleCameraRotation = m_Camera.rotation * m_Camera.rotation;
m_cubeName.rotation = Quaternion.Lerp(
m_cubeName.rotation,
doubleCameraRotation,
Time.deltaTime);
To multiply the rotation by a non-integer factor, you can use Quaternion.LerpUnclamped, (or SlerpUnclamped for better accuracy) then pass the scaling factor as t. For example:
Quaternion doubleCameraRotation =
Quaternion.LerpUnclamped(Quaternion.identity, m_Camera.rotation, 2f);

Related

Rotate a Rigidbody on X axis based on mouse movement

My Player object consists of a playermodel itself and an empty GameObject "FollowTarget".
FollowTarget has a script attached to it, which makes the camera rotate along with it; Cinemachine is also made to follow it.
So essentialy, the camera itself works well and follows the player around just as intended.
But I also need my player to rotate around the X axis along with rotating the camera.
I am currently using this code to rotate the player around X axis:
rb.MoveRotation(rb.rotation * Quaternion.Euler(new Vector3(0, mouseX * 200f * Time.fixedDeltaTime, 0)));
This works, but makes the mouse rotation on X axis rough, unpolished, uneven - something like that. On FollowTarget I use a simple code to get mouse input:
mouseX += Input.GetAxis("Mouse X") * mouseSens;
mouseY += Input.GetAxis("Mouse Y") * mouseSens;
mouseY = Mathf.Clamp(mouseY, minAngleY, maxAngleY);
transform.localRotation = Quaternion.Euler(-mouseY, 0, 0);
Not adding mouseX to this code, because the previous one already reads mouseX, but as I said, it makes the X rotation rough and it feels weird to rotate the camera in-game. When I add mouseX to this code the player doesn't rotate as intended. Using mouseX only in this code obviously makes the player not rotate at all.
So how can I combine it? How can I make the camera move through FollowTarget, and also make my Rigidbody playermodel rotate on X axis based on mouse input? Tried many different solutions, combinations, code examples and checked many different posts and nothing worked for me.

Unity: How to stop object from spinning when rotating towards object?

When I try to rotate an object towards a point, it starts spinning, but I want it to be able to freely spin, while still rotating towards that point. I can't just clamp the y rotation, because obviously it's already rotating towards a point and setting the absolute y rotation while doing so and you can't access an object's relative angles in Unity. As seen here, the blue gizmo always faces the top of the sphere (the centre of the sphere is the point I'm rotating towards). I want it to be able to move freely.
Here's my rotation code:
Quaternion dir = Quaternion.LookRotation((target - transform.position).normalized) * Quaternion.Euler(new Vector3(-90, 0, 0));
transform.rotation = Quaternion.Slerp(transform.rotation, dir, smooth);
Does anyone know how to fix this?

Arrow rotating to face cursor needs to only do so while inside an angle made by two given directions

I have a 2d arrow rotating to always face the a target (the target in this case is the cursor), the pivot is my player character. I need to restrict this arrow to only follow the target if it is inside an angle of the player, an example would be 90 degrees, so it would only follow if the cursor is in the top right part of the screen.
I have worked with vector directions and methods such as Vector2D.angle, but they all seem to have some restriction i can't workaround, Vector2D.angles restriction is that the 3rd position it uses to calculate the angle is the world center(0, 0), my player is mobile so that doesn't work.
So i think what im asking is if theres a way to store an angle, and then check if something is within that.
Here is the code i use for rotating my arrow, theres more to the script but i removed the unnecesary parts:
public float speed;
public Transform target;
void Update()
{
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = target.position - transform.position;
target.position = mousePosition;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, speed * Time.deltaTime);
Sorry if this is formatted poorly, its my first time posting here, thank you a million times if you are able to help me, i have been stuck on this for days.
Was asked to clarify question so here is an attempt:
This picture shows an example of what i mean, the arrow is rotating around the center of the circle, (it is rotating so it always points towards my cursor). What i need is a way to restrict it so it only points towards the cursor if it is within a specific angle (red lines in picture), that's what im having trouble with. I can't find a way to store this threshold and i can't seem to find a way to compare the cursors direction with it.
I think it would be possible if it was possible to choose a custom center for Vector2D.angle, but that doesn't seem to be the case.
I hope this clarifies what my question, i may just very well be stupid and overlooking something obvious but i really can't find a way to make this possible.
thanks again.
Important fields/inputs
First, we need to know the boundary directions in world space:
public Vector2 boundaryDirectionLeft;
public Vector2 boundaryDirectionRight;
The important piece is that the angle made clockwise from boundaryDirectionLeft to boundaryDirectionRight is the region the arrow shall remain inside. In the case of your image, boundaryDirectionLeft could be Vector2.up and boundaryDirectionRight could be something like new Vector2(1f,-1f).
We also need to know which local direction the arrow is facing before any rotation is applied. E.g., if the arrow is always pointing with the local red arrow axis (the local right direction), this would be Vector2.right:
public Vector2 localArrowDirection;
And lastly, we need a top speed for our rotation, so we don't warp the arrow around when it's time to rotate. A good value to start with might be 360f, 360 degrees per second. Try experimenting with different values to find one that you like:
public float maxRotationSpeed;
The update procedure
In Update, determine the direction to the target:
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
Then, we need to know where the arrow is currently pointing. We can use Transform.TransformVector to find where the local localArrowDirection is pointing in world space:
Vector2 currentDirection = transform.TransformVector(localArrowDirection);
Then, determine the signed angle from boundaryDirectionLeft to the target direction, the same from boundaryDirectionLeft to boundaryDirectionRight, and the same from boundaryDirectionLeft to the current facing direction:
float directionAngle = Vector2.SignedAngle(boundaryDirectionLeft, direction);
float boundaryAngle = Vector2.SignedAngle(boundaryDirectionLeft, boundaryDirectionRight);
float currentAngle = Vector2.SignedAngle(boundaryDirectionLeft, currentDirection);
These values range from [-180,180], but we want them expressed in the range [0,360) to make the math easier later, so we can add 360f and use Mathf.Repeat on that sum:
directionAngle = Mathf.Repeat(directionAngle+360f, 360f);
boundaryAngle = Mathf.Repeat(boundaryAngle+360f, 360f);
currentAngle = Mathf.Repeat(currentAngle+360f, 360f);
At this point directionAngle is how many clockwise degrees from boundaryDirectionLeft that target is, and boundaryAngle is how many boundaryDirectionRight is, and currentAngle is the same for what direction we're currently facing.
So, now, we need to know how to properly clamp the angle between 0 and boundaryAngle. Anything too far above boundaryAngle is actually closer to the left boundary and should be clamped to the left boundary. In fact, since everything is between 0 and 360, anything higher than boundaryAngle+(360f-boundaryAngle)/2f is closer to the left. So, we just set anything higher than that to be 0 degrees away from boundaryDirectionLeft:
if (directionAngle > boundaryAngle + (360f - boundaryAngle)/2f)
{
directionAngle = 0f;
}
So, now we can clamp directionAngle with a high of boundaryAngle (it is already bottom clamped at 0f, so we can use Mathf.Min here):
directionAngle = Mathf.Min(directionAngle, boundaryAngle);
Now we can limit the angular difference between directionAngle and currentAngle using our maxRotationSpeed:
float deltaAngle = Mathf.Clamp(directionAngle-currentAngle,
-maxRotationSpeed * Time.deltaTime,
maxRotationSpeed * Time.deltaTime);
Now we can rotate the transform deltaAngle degrees clockwise (in world space):
transform.Rotate(0f,0f,deltaAngle,Space.World);

Changing the x and y rotation weirdly changes the z too in Unity

I tried to make a first person controller in Unity so that I know every single detail of it. I made the movement, but when I created the camera rotation i simply got stuck. Ok, it worked on the x axis, on the y axis, but why does the z axis also change?
void Update () {
transform.position = Character.transform.position + offset;
float h = Input.GetAxis("Mouse X") * horizontalSpeed;
float v = Input.GetAxis("Mouse Y") * verticalSpeed;
transform.Rotate(h, v, 0, Space.World);
}
When working with rotations and euler angles you have to understand that there are different coordinate spaces, like the object's local space and a world space. the x,y,z directions in world space will always look at the same direction, whereas the local space is the coordinate space of the local object (for example your camera). Thats why I would recommend you to rotate vertically in local space and horizontally in world space.
transform.Rotate(0,h,0,Space.World);
transform.Rotate(-v, 0, 0, Space.Self);
Rotating your camera vertical in local space will change the object's local up axis (y-axis). this is why then rotating around that axis will give you your unwanted result. Just select the gameobject in your Scene view and observe it's local coordinate system while rotating and you might be able to better visually understand.
To also give you a simple,fun reallife example to observe yourself, sit upright on your office chair and look straight ahead and rotate on your chair. If you now look a little upwards or downwards and rotate your head instead you will notice a difference in rotation between rotating your neck and rotating on your chair, which should always have the same up-axis, which is different from the local up axis of your neck.
ps.: there are already many solutions to implement a first person camera, like in Unity's own standard assets, or SmoothMouseLook
Looking at tutorials, following them and trying to understand them might bring you further than trying to figure it out on your own.
Euler angles are weird. When you rotate with euler angles, the rotations stack, and a rotation around one axis changes the other axis of rotation. It's best not to "add" euler angles (essentially what transform.Rotate() is doing) and best to work with them in absolute terms. In this case, your code should keep the "z axis" at 0 if you do the following:
void Update () {
transform.position = Character.transform.position + offset;
float h = Input.GetAxis("Mouse X") * horizontalSpeed;
float v = Input.GetAxis("Mouse Y") * verticalSpeed;
# Get the current euler angle in absolute terms
Vector3 eulers = Character.transform.localEulerAngles;
# now modify that euler angle, creating a new absolute euler angle
eulers.x += h;
eulers.y += v;
eulers.z = 0;
# and now assign the new euler angle back to the transform, overwriting the old value
Character.transform.localEulerAngles = eulers;
}
In general I'd advise against using Transform.Rotate() for... most everything. I would also advise using quaternions wherever you're able. You don't need a complete understanding of quaternions for them to be very powerful.

Rotation around a point

Can anyone help me with this please
I want to be able to rotate a 3D object around a stationary 3D object. Well there will be no movement involved as I just want to draw the objects at their locations once the game starts and then they will remain there for the remainder of the game.
Say for instance I have a object X that is stationary in 3D space. I then have 2 other objects, Y1 and Y2. Both of these objects are stationary as well and cant be moved. All 3 objects are on the same x and y axis. Lets say X is at (0,0,0) and Y1 is at (0,0,-50). I want to draw Y2 at a 45 degree angle from Y1 around the Y-axis but keep it the same distance from X.
Can anyone please suggest the best way of doing this please?
I have tried the following but that just rotates the object around its origin. So I guess I have to rotate it around the world origin? How is this done?
Matrix.CreateRotationY(Rotation)
I'm not sure what you want, but this is one method for rotate one object around another:
Vector3 Origin; // Stationary Object
float Yaw, Pitch; // Angles
float Distance;
Vector3 OrbitOffset = Vector3.UnitX * Distance;
// Other approach that consider the initial pos of the object to rotate
// Vector3 OrbitOffset = OrbitPos - Origin;
Matrix Rotation = Matrix.CreateFromYawPitchRoll(Yaw, Pitch, 0);
Vector3.Transform(ref OrbitOffset, ref Rotation, out OrbitOffset);
Vector3 OrbitPos = Origin + OrbitOffset; // Final position of the rotated object
if you dont need rotation about more than 2 angles at once, you can use basic Euler method.
see :
http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
thats a mathematical approach tough... but it works..
Just if you want a rotation around multiple axes, you will have serious problems with gimbal lock

Categories