How does Quaternion.eulerAngles work in Unity? [duplicate] - c#

I've used two examples (from this site too), but results are not the same as those that said Unity.
Quaternion.Euler and .eulerAngles are Unity functions. FromQ doesn't perform singularity check, FromQ2 does.
Results:
eulers = (100,55,-11):
Quaternion.Euler(eulers) == (0.6, 0.4, -0.4, 0.5)
ToQ(eulers)); == (0.5, -0.4, 0.2, 0.7) // 0.5, -0.4 right but in wrong order
FromQ(ToQ(eulers)) == (55.0, 100.0, -11.0)
FromQ2(ToQ(eulers)) == (-55.5, -6.3, 71.0) // something right
Quaternion.Euler(eulers).eulerAngles == (80.0, 235.0, 169.0)
FromQ2(Quaternion.Euler(eulers)) == (65.8, 1.9, 99.8)
ToQ(eulers).eulerAngles == (70.0, 286.9, 341.4)
FromQ(Quaternion.Euler(eulers)) == (-65.8, 76.0, 4.6)
It must be:
FromQ() = FromQ2() = .eulerAngles,
ToQ() = Quaternion.Euler()
The code is here: http://pastebin.ru/eAlTHdYf
Can anyone correct this code? I need code that will return the values ​​that are identical to the values that Unity functions returns.
UPDATE
Here is fixed code: http://pastebin.com/riRLRvch.
Both functions (FromQ and ToQ) work well. But I have a problem with a singularity. It can't detect the singularity properly.
For example (90, 0, 50) in quaternion is (0.6, -0.3, 0.3, 0.6).
test = x * y + z * w = 0 (must be close to 0.5 or -0.5)
FromQ can't calculate correct result so we have the singularity here. The same for (90, 50, 0) - (0.6, 0.3, -0.3, 0.6).
I see only one solution - calculate "test" as xw-yz. But I'm not sure this is right.
How to fix it?

I've found solution
public static Quaternion ToQ (Vector3 v)
{
return ToQ (v.y, v.x, v.z);
}
public static Quaternion ToQ (float yaw, float pitch, float roll)
{
yaw *= Mathf.Deg2Rad;
pitch *= Mathf.Deg2Rad;
roll *= Mathf.Deg2Rad;
float rollOver2 = roll * 0.5f;
float sinRollOver2 = (float)Math.Sin ((double)rollOver2);
float cosRollOver2 = (float)Math.Cos ((double)rollOver2);
float pitchOver2 = pitch * 0.5f;
float sinPitchOver2 = (float)Math.Sin ((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos ((double)pitchOver2);
float yawOver2 = yaw * 0.5f;
float sinYawOver2 = (float)Math.Sin ((double)yawOver2);
float cosYawOver2 = (float)Math.Cos ((double)yawOver2);
Quaternion result;
result.w = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
result.x = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
result.y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
result.z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
return result;
}
public static Vector3 FromQ2 (Quaternion q1)
{
float sqw = q1.w * q1.w;
float sqx = q1.x * q1.x;
float sqy = q1.y * q1.y;
float sqz = q1.z * q1.z;
float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
float test = q1.x * q1.w - q1.y * q1.z;
Vector3 v;
if (test>0.4995f*unit) { // singularity at north pole
v.y = 2f * Mathf.Atan2 (q1.y, q1.x);
v.x = Mathf.PI / 2;
v.z = 0;
return NormalizeAngles (v * Mathf.Rad2Deg);
}
if (test<-0.4995f*unit) { // singularity at south pole
v.y = -2f * Mathf.Atan2 (q1.y, q1.x);
v.x = -Mathf.PI / 2;
v.z = 0;
return NormalizeAngles (v * Mathf.Rad2Deg);
}
Quaternion q = new Quaternion (q1.w, q1.z, q1.x, q1.y);
v.y = (float)Math.Atan2 (2f * q.x * q.w + 2f * q.y * q.z, 1 - 2f * (q.z * q.z + q.w * q.w)); // Yaw
v.x = (float)Math.Asin (2f * (q.x * q.z - q.w * q.y)); // Pitch
v.z = (float)Math.Atan2 (2f * q.x * q.y + 2f * q.z * q.w, 1 - 2f * (q.y * q.y + q.z * q.z)); // Roll
return NormalizeAngles (v * Mathf.Rad2Deg);
}
static Vector3 NormalizeAngles (Vector3 angles)
{
angles.x = NormalizeAngle (angles.x);
angles.y = NormalizeAngle (angles.y);
angles.z = NormalizeAngle (angles.z);
return angles;
}
static float NormalizeAngle (float angle)
{
while (angle>360)
angle -= 360;
while (angle<0)
angle += 360;
return angle;
}

This question is almost three years old, but I needed the same code and the ones posted here seemed to be incorrect, so I tweaked them and found this:
public static Quaternion Euler(float yaw, float pitch, float roll) {
yaw*=Mathf.Deg2Rad;
pitch*=Mathf.Deg2Rad;
roll*=Mathf.Deg2Rad;
double yawOver2 = yaw * 0.5f;
float cosYawOver2 = (float)System.Math.Cos(yawOver2);
float sinYawOver2 = (float)System.Math.Sin(yawOver2);
double pitchOver2 = pitch * 0.5f;
float cosPitchOver2 = (float)System.Math.Cos(pitchOver2);
float sinPitchOver2 = (float)System.Math.Sin(pitchOver2);
double rollOver2 = roll * 0.5f;
float cosRollOver2 = (float)System.Math.Cos(rollOver2);
float sinRollOver2 = (float)System.Math.Sin(rollOver2);
Quaternion result;
result.w = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
result.x = sinYawOver2 * cosPitchOver2 * cosRollOver2 + cosYawOver2 * sinPitchOver2 * sinRollOver2;
result.y = cosYawOver2 * sinPitchOver2 * cosRollOver2 - sinYawOver2 * cosPitchOver2 * sinRollOver2;
result.z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
return result;
}
According to a few quick tests, this matches Quaternion.Euler 100%

This might only be worth a partial answer but here is "ToQ() = Quaternion.Euler()":
public static Quaternion ToQ(Vector3 v)
{
return ToQ(v.y,v.x,v.z);
}
public static Quaternion ToQ(float yaw, float pitch, float roll)
{
yaw*=Mathf.Deg2Rad;
pitch*=Mathf.Deg2Rad;
roll*=Mathf.Deg2Rad;
float rollOver2 = roll * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2);
float pitchOver2 = pitch * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2);
float yawOver2 = yaw * 0.5f;
float sinYawOver2 = (float)Math.Sin((double)yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2);
Quaternion result;
result.w = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
result.x = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
result.y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
result.z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
return result;
}
The 'FromQ' part of your question is a different matter. Euler angle comparison is a pain in the behind.

Here's my solution. This is very very close to Unity's Quaternion.Euler and quaternion.eulerAngles. The discrepancies are small enough that they shouldn't matter for any application.
public static Vector3 QuaternionToEuler(Quaternion q)
{
Vector3 euler;
// if the input quaternion is normalized, this is exactly one. Otherwise, this acts as a correction factor for the quaternion's not-normalizedness
float unit = (q.x * q.x) + (q.y * q.y) + (q.z * q.z) + (q.w * q.w);
// this will have a magnitude of 0.5 or greater if and only if this is a singularity case
float test = q.x * q.w - q.y * q.z;
if (test > 0.4995f * unit) // singularity at north pole
{
euler.x = Mathf.PI / 2;
euler.y = 2f * Mathf.Atan2(q.y, q.x);
euler.z = 0;
}
else if (test < -0.4995f * unit) // singularity at south pole
{
euler.x = -Mathf.PI / 2;
euler.y = -2f * Mathf.Atan2(q.y, q.x);
euler.z = 0;
}
else // no singularity - this is the majority of cases
{
euler.x = Mathf.Asin(2f * (q.w * q.x - q.y * q.z));
euler.y = Mathf.Atan2(2f * q.w * q.y + 2f * q.z * q.x, 1 - 2f * (q.x * q.x + q.y * q.y));
euler.z = Mathf.Atan2(2f * q.w * q.z + 2f * q.x * q.y, 1 - 2f * (q.z * q.z + q.x * q.x));
}
// all the math so far has been done in radians. Before returning, we convert to degrees...
euler *= Mathf.Rad2Deg;
//...and then ensure the degree values are between 0 and 360
euler.x %= 360;
euler.y %= 360;
euler.z %= 360;
return euler;
}
public static Quaternion EulerToQuaternion(Vector3 euler)
{
float xOver2 = euler.x * Mathf.Deg2Rad * 0.5f;
float yOver2 = euler.y * Mathf.Deg2Rad * 0.5f;
float zOver2 = euler.z * Mathf.Deg2Rad * 0.5f;
float sinXOver2 = Mathf.Sin(xOver2);
float cosXOver2 = Mathf.Cos(xOver2);
float sinYOver2 = Mathf.Sin(yOver2);
float cosYOver2 = Mathf.Cos(yOver2);
float sinZOver2 = Mathf.Sin(zOver2);
float cosZOver2 = Mathf.Cos(zOver2);
Quaternion result;
result.x = cosYOver2 * sinXOver2 * cosZOver2 + sinYOver2 * cosXOver2 * sinZOver2;
result.y = sinYOver2 * cosXOver2 * cosZOver2 - cosYOver2 * sinXOver2 * sinZOver2;
result.z = cosYOver2 * cosXOver2 * sinZOver2 - sinYOver2 * sinXOver2 * cosZOver2;
result.w = cosYOver2 * cosXOver2 * cosZOver2 + sinYOver2 * sinXOver2 * sinZOver2;
return result;
}

Related

Converting Calculating To Movement Overtime?

I am moving an object in a parabolic arc this way:
public IEnumerator ParabolicMovement()
{
Vector3 startPos;
Vector3 targetPos;
float speed = 6;
float arcHeight = 3;
Vector3 nextPos = Vector3.zero;
while (transform.position != targetPos)
{
// Compute the next position, with arc added in
float x0 = startPos.x;
float x1 = targetPos.x;
float dist = x1 - x0;
float nextX = Mathf.MoveTowards(transform.position.x, x1, (speed) * Time.deltaTime);
float baseY = Mathf.Lerp(startPos.y, targetPos.y, (nextX - x0) / dist);
float arc = arcHeight * (nextX - x0) * (nextX - x1) / (-0.25f * dist * dist);
nextPos = new Vector3(nextX, baseY + arc, transform.position.z);
transform.position = nextPos;
yield return null;
}
}
This works, but I now want to take this and make it happen over a specific amount of time. I removed the loop from the original method and broke it up into two separate methods to accomplish this:
public IEnumerator BeginJumpOverTime()
{
float duration = 1f;
float startTime = Time.time;
float endTime = startTime + duration;
while (Time.time <= endTime)
{
float tNormalized = Mathf.Clamp((Time.time - startTime) / duration, 0f, 1f);
Vector2 newXAndY = CalculateXAndY(tNormalized);
transform.position = newXAndY;
yield return null;
}
}
public Vector2 CalculateXAndY(float t)
{
Vector3 startPos = GameEngine.Instance.battleManager.TurnHero.transform.position;
Vector3 targetPos = GameEngine.Instance.battleManager.TargetEnemy.transform.position;
float speed = 6;
float arcHeight = 3;
Vector3 nextPos = Vector3.zero;
// Compute the next position to make the parabola
float x0 = startPos.x;
float x1 = targetPos.x;
float dist = x1 - x0;
float nextX = Mathf.MoveTowards(transform.position.x, x1, (speed) * (Time.deltaTime) );
float baseY = Mathf.Lerp(startPos.y, targetPos.y, (nextX - x0) / dist);
float arc = arcHeight * (nextX - x0) * (nextX - x1) / (-0.25f * dist * dist);
nextPos = new Vector3(nextX, baseY + arc, transform.position.z);
return (nextPos);
}
I'm pretty certain this concept should work, I just can't seem to figure out where to factor tNormalized when it's passed into CalculateXandY(). Is anyone math savvy able to assist me with this? Thanks a ton!
Kind regards,

Find an angle to launch the projectile at to reach a specific point

So, Dani in his slightly new video -> "Making a Game, But I Only Have 3 Days" (https://youtu.be/S7Dl6ATRK2M) made a enemy which has a bow and arrow (at 5:39). I tried to recreate that but had no luck... I also can't find the website that he used... Today I found this https://physics.stackexchange.com/questions/56265/how-to-get-the-angle-needed-for-a-projectile-to-pass-through-a-given-point-for-t. It worked very well but still had problems if the target was far away and also it wasn't as accurate. The code so far is
float CalculateAngle()
{
float gravity = Physics.gravity.magnitude;
float deltaX = targetPositionMod.x - currentPosition.x;
float deltaY = targetPositionMod.y - currentPosition.y;
float RHSFirstPart = (velocity * velocity) / (gravity * deltaX);
float RHSSecondPart = Mathf.Sqrt(
((velocity * velocity) * ((velocity * velocity) - (2 * gravity * deltaY))
/ (gravity * gravity * deltaX * deltaX))
- 1);
float tanθ = RHSFirstPart - RHSSecondPart;
float angle = Mathf.Atan2(tanθ, 1) * Mathf.Rad2Deg;
if (angle < 0) return angle;
return -angle;
}
The -angle is because the forward axis starts points up when the x-rotation is negative (Unity). Maybe the reason of this not working as intended is that I am not that good at this kind of Physics (Part of that is me being only 14). Maybe the problem is in the code, maybe it is the formula. Any help is appreciated.
Thanks...
Edit:
The Archer class is:
using UnityEngine;
using System;
public class Archer : MonoBehaviour
{
[SerializeField] float velocity = default;
[SerializeField] Transform target = default;
[SerializeField] GameObject arrowPrefab = default;
[SerializeField] float coolDown = default;
Vector3 targetPositionMod;
Vector3 currentPosition;
Vector3 targetPosition;
float countDown = 0f;
void Start()
{
countDown = coolDown;
UpdateVariables();
}
void Update()
{
UpdateVariables();
SetAngle();
ShootBullet();
}
void UpdateVariables()
{
currentPosition = transform.position;
targetPositionMod = Mod(target.position);
targetPosition = target.position;
targetPosition.x /= 10;
targetPosition.y /= 10;
targetPosition.z /= 10;
countDown -= Time.deltaTime;
}
void SetAngle()
{
Vector3 direction = targetPosition - currentPosition;
Quaternion lookRotation = Quaternion.LookRotation(direction);
Vector3 rotation = lookRotation.eulerAngles;
rotation.x = (float) CalculateAngle();
transform.rotation = Quaternion.Euler(rotation.x, rotation.y, 0f);
}
void ShootBullet()
{
if (!(countDown <= 0f)) return;
countDown = coolDown;
GameObject arrow = Instantiate(arrowPrefab, transform.position, transform.rotation);
Rigidbody Rigidbody = arrow.GetComponent<Rigidbody>();
Rigidbody.AddForce(transform.forward * velocity, ForceMode.Impulse);
}
double CalculateAngle()
{
double gravity = Physics.gravity.magnitude;
double deltaX = targetPositionMod.x - currentPosition.x;
double deltaY = targetPositionMod.y - currentPosition.y;
double RHSFirstPart = (velocity * velocity) / (gravity * deltaX);
double RHSSecondPart = Math.Sqrt(
(((velocity * velocity) * ((velocity * velocity) - (2 * gravity * deltaY))
/ (gravity * gravity * deltaX * deltaX))
- 1));
double tanθ = RHSFirstPart - RHSSecondPart;
double angle = Math.Atan2(tanθ, 1) * Mathf.Rad2Deg;
if (angle < 0) return angle;
return -angle;
}
Vector3 Mod(Vector3 Vec)
{
if (Vec.x < 0) Vec.x -= 2 * Vec.x;
if (Vec.y < 0) Vec.y -= 2 * Vec.y;
if (Vec.z < 0) Vec.z -= 2 * Vec.z;
Vec.x /= 10;
Vec.y /= 10;
Vec.z /= 10;
return Vec;
}
}
Ok, as I can see, your implementation of formula from StackExchange is right, but you have to remember two things:
In unity there is a 3D world, so horizontal distance is not just pos1.x - pos2.x, but
Mathf.Sqrt( deltaX * deltaX + deltaZ * deltaZ ), where deltaX = targetPositionMod.x - currentPosition.x and deltaZ = targetPositionMod.z - currentPosition.z
In computer implementation you have no 100% accuracy of math, so some problems can appear because of computational accuracy. And it can have affect on big distances. You can try to use double instead of float or find another implementation for arctangent function (I think, this can really help). But try this (second) advice only if first didn't help. It's harder to implement and it slows computations a bit.
Algorithm:
Step 1: Set up a function that calculates the appropriate solution of a quadratic equation
a*x^2 + b*x + c = 0
double quadratic_root(a,b,c){
D = b^2 - 4*a*c
return ( - b - Math.Sqrt(D) ) / (2 * a)
}
Step 2: Input
current.x
current.y
current.z
target.x
target.y
target.z
velocity
gravity
Step 3: Calculate coefficients of the quadratic polynomial:
dist = Math.Sqrt( (target.x - current.x)^2 + (target.y - current.y)^2 )
a = gravity * dist^2 / (2 * velocity^2)
b = -dist
c = target.z - current.z + a
Step 4:
theta = Math.Atan2( quadratic_root(a,b,c), 1 )
Calculation behind the algorithm. You are in three space. The current position has coordinates
x = current.x
y = current.y
z = current.z
and the target has coordinates
x = target.x
y = target.y
z = target.z
Assume the angle between the initial velocity and the horizontal plane is theta. The magnitude of the projection of the distance between the current position and the target onto the horizontal $x,y-$plane is
dist = sqrt( (target.x - current.x)^2 - (target.y - current.y)^2 )
You are given the velocity magnitude velocity. Then, the speed with which the shadow (i.e. the orthogonal projection) of the arrow moves along the horizontal line between the source and the target is the magnitude of the shadow (i.e. the orthogonal projection) of the actual velocity
velocity * cos(theta)
The vertical speed of the arrow is then
velocity * sin(theta)
So the motion along dist follows the equation
dist = time * velocity * cos(theta)
and hence
time = dist / (velocity * cos(theta))
In the vertical direction, the motions is described by the equation
z = current.z + time * velocity * sin(theta) - time^2 * gravity / 2
You are interested in the time for which the arrow hits the target, which has vertical coordinate target.z, so
target.z = current.z + time * velocity * sin(theta) - time^2 * gravity / 2
The equation can be written as:
0 = - (target.z - current.z) + time * velocity * sin(theta) - time^2 * gravity / 2
We already know that
time = dist / (velocity * cos(theta))
so
0 = - (target.z - current.z) + dist * velocity * sin(theta) / (velocity * cos(theta)) - dist^2 * gravity / ( 2 * (velocity * cos(theta))^2 )
which can be slightly simplified to
0 = - (target.z - current.z) + dist * sin(theta) / cos(theta) - gravity * dist^2 / ( 2 * (velocity * cos(theta))^2 )
Because 1/( cos(theta)^2 ) = 1 + ( tan(theta) )^2 we obtain the quadratic in tan(theta) equation
a * ( tan(theta) )^2 + b * tan(theta) + c = 0
where
a = gravity * dist^2 / (2 * velocity^2)
b = - dist
c = target.z - current.z + a

C# CS0165 on the result of Quaternion

i want to convert my Quaternions in to euler angles but in this function I get an error on result.W. The error is CS0165.
public static Quaternion Euler(string[]text)
{
double yaw = Convert.ToDouble(text[1]);
double pitch = Convert.ToDouble(text[2]);
double roll = Convert.ToDouble(text[3]);
yaw = (Math.PI / 180) * yaw;
pitch = (Math.PI / 180) * pitch;
roll = (Math.PI / 180) * roll;
double yawOver2 = yaw * 0.5f;
float cosYawOver2 = (float)System.Math.Cos(yawOver2);
float sinYawOver2 = (float)System.Math.Sin(yawOver2);
double pitchOver2 = pitch * 0.5f;
float cosPitchOver2 = (float)System.Math.Cos(pitchOver2);
float sinPitchOver2 = (float)System.Math.Sin(pitchOver2);
double rollOver2 = roll * 0.5f;
float cosRollOver2 = (float)System.Math.Cos(rollOver2);
float sinRollOver2 = (float)System.Math.Sin(rollOver2);
Quaternion result;
result.W = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
result.X = sinYawOver2 * cosPitchOver2 * cosRollOver2 + cosYawOver2 * sinPitchOver2 * sinRollOver2;
result.Y = cosYawOver2 * sinPitchOver2 * cosRollOver2 - sinYawOver2 * cosPitchOver2 * sinRollOver2;
result.Z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
return result;
}
What could be the problem?
Use of unassigned local variable 'name': https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0165
You cannot access W because most probably in your Quaternion definition you use properties or some other code that requires explicit object creation.

C# Create 3x3 Matrix from vector

I have a vector and I want to create a Matrix from this vector. The Matrix3 should be 3times Vector3. How is it possible? Do I have to create a new public static?
The Code below shows my Vector3.
public static Vector3 VectorMultiply(Vector3 v, Matrix3 m)
{
Vector3 vr = new Vector3();
vr.x = v.x * m.X.x + v.y * m.Y.x + v.z * m.Z.x;
vr.y = v.x * m.X.y + v.y * m.Y.y + v.z * m.Z.y;
vr.z = v.x * m.X.z + v.y * m.Y.z + v.z * m.Z.z;
return (vr);

Unity - Clamp Rotation between arbitrary angles

I have a task where I have to clamp an angle between other two angle.
The catch is that the limits can be >360 or <0
(ex. [-45,45] or [275,45]).
Is there a clean way to do this taking into account all the special
cases?
(ex. range [-45,45] and input angle of 225 should be -45).
Thanks in advance!
P.S. I am using unity, so I have all the default Quaternion methods at hand.
Current Code:
Quaternion inputAngle = Quaternion.identity;
if (Character.IsFacingRight)
inputAngle = Quaternion.FromToRotation(forwardVector, playerInput);
else
inputAngle = Quaternion.FromToRotation(playerInput, forwardVector);
Quaternion minAngle = Quaternion.Euler(0F, 0F, addedForce.force.angle);
Quaternion angleRange = Quaternion.Euler(0F, 0F, addedForce.force.angleRange);
Quaternion maxAngle = angleRange * minAngle;
// var yaw = Math.Atan2(2.0 * (inputAngle.y * inputAngle.z + inputAngle.w * inputAngle.x), inputAngle.w * inputAngle.w - inputAngle.x * inputAngle.x - inputAngle.y * inputAngle.y + inputAngle.z * inputAngle.z);
// var pitch = Math.Asin(-2.0 * (inputAngle.x * inputAngle.z - inputAngle.w * inputAngle.y));
float roll = (float)Math.Atan2(2.0 * (inputAngle.x * inputAngle.y + inputAngle.w * inputAngle.z), inputAngle.w * inputAngle.w + inputAngle.x * inputAngle.x - inputAngle.y * inputAngle.y - inputAngle.z * inputAngle.z);
Quaternion correctedRotation = Quaternion.Euler(0F, 0F, roll / Mathf.PI * 180F);
float endAngleZ = ClampRotation2(correctedRotation.eulerAngles.z, minAngle.eulerAngles.z, maxAngle.eulerAngles.z);
Quaternion endAngle = Quaternion.Euler(0F, 0F, endAngleZ);
ClampRotation2:
public static float ClampRotation2(float angle, float min, float max)
{
if (angle < 0) angle += 360;
if (max < 0) max += 360;
if (min < 0) min += 360;
if (min > max) min -= 360;
return Mathf.Clamp(angle, min, max);
}
OK, so I found the solution which clamps the angle correctly in all cases,
Brought to you by Quaternion.Angle() I present :
Quaternion inputAngle = Quaternion.identity;
if (Character.IsFacingRight)
inputAngle = Quaternion.FromToRotation(forwardVector, playerInput);
else
inputAngle = Quaternion.FromToRotation(playerInput, forwardVector);
Quaternion minAngle = Quaternion.Euler(0F, 0F, addedForce.force.angle);
Quaternion angleRange = Quaternion.Euler(0F, 0F, addedForce.force.angleRange);
Quaternion maxAngle = angleRange * minAngle;
float roll = (float)Math.Atan2(2.0 * (inputAngle.x * inputAngle.y + inputAngle.w * inputAngle.z), inputAngle.w * inputAngle.w + inputAngle.x * inputAngle.x - inputAngle.y * inputAngle.y - inputAngle.z * inputAngle.z);
float correctedRotation = roll / Mathf.PI * 180F;
float minAngleF = minAngle.eulerAngles.z;
float maxAngleF = maxAngle.eulerAngles.z;
if (correctedRotation < 0) correctedRotation += 360;
if (maxAngleF < 0) maxAngleF += 360;
if (minAngleF < 0) minAngleF += 360;
if (minAngleF > maxAngleF) minAngleF -= 360;
if (correctedRotation < minAngleF || correctedRotation > maxAngleF)
{
float rotationToMax = Quaternion.Angle(Quaternion.Euler(0F, 0F, correctedRotation), Quaternion.Euler(0F, 0F, maxAngleF));
float rotationToMin = Quaternion.Angle(Quaternion.Euler(0F, 0F, correctedRotation), Quaternion.Euler(0F, 0F, minAngleF));
if (Mathf.Abs(rotationToMax) < Mathf.Abs(rotationToMin))
correctedRotation = maxAngleF;
else
correctedRotation = minAngleF;
}
Quaternion endAngle = Quaternion.Euler(0F, 0F, correctedRotation);

Categories