So, when my i let go off my keys the controller stops like it hits a wall, i tried changing that but all that changed is that now it gets flung into outer space every time i press a key:
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 newMovement = transform.right * x + transform.forward * z;
momentum = new Vector3(characterController.velocity.x, 0, characterController.velocity.z);
newMovement.y = 0;
if (!newMovement.normalized.Equals(momentum.normalized))
{
Debug.Log("new" + newMovement.normalized);
Debug.Log(momentum.normalized);
momentum = (momentum.magnitude - 2f) > 0 ? momentum.normalized * (momentum.magnitude - 2f) : Vector3.zero;
if (newMovement.x == momentum.x)
momentum.x = 0;
if (newMovement.z == momentum.z)
momentum.z = 0;
}
else
momentum = Vector3.zero;
characterController.Move((newMovement * speed + velocity + momentum) * Time.deltaTime);
Also for some reason even though sometimes both vectors are equal they pass through the if statement(i tried using !=)(both vectors are logged on the first 2 lines of the if statement)
Use https://docs.unity3d.com/ScriptReference/Vector3.SmoothDamp.html, it will gradually slow the movement to zero, depending on the value of smoothTime:
public float smoothTime = 0.3F;
private Vector3 velocity = Vector3.zero;
private Vector3 newMovement;
void Update()
{
newMovement = transform.right * x + transform.forward * z;
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
}
I want to spin an object around x and rotate y axis to a direction.
Quaternion qr = Quaternion.Euler(transform.eulerAngles.x, 0,0);
transform.rotation = Quaternion.Lerp(transform.rotation,qr,Time.deltaTime*5);
transform.rotation = Quaternion.Euler(xRotation(xRotationSpeed),
transform.eulerAngles.y, 0);
x and y Rotations combined make weird rotations and sometimes even rotate z even though I have it set to 0.
Here is an example that yawing an object while rolling.
Notice that the rotation is applied from right to left.
const float ROLL_SPEED = 120f;
const float YAW_SCALE = 60;
float _lastYawBase;
void Update()
{
var yawBase = Input.GetAxis("Horizontal");
var tsf = transform;
tsf.localRotation =
// 4. apply new yaw
Quaternion.Euler(0, yawBase * YAW_SCALE, 0) *
// 3. roll
Quaternion.Euler(ROLL_SPEED * Time.deltaTime, 0f, 0f) *
// 2. revert last yaw
Quaternion.Euler(0, -_lastYawBase * YAW_SCALE, 0) *
// 1. current rotation
tsf.localRotation;
_lastYawBase = yawBase;
}
I want to proceduraly generate meshes. I created a method to supply the vertices of a circle. The idea is that it creates a shape in 2d and then rotates it in 3d assuming that "rotation" is the vector of the shapes normal axis.
public List<Vector3> Loop (Vector3 center, Vector3 rotation, float radius, int divisions)
{
List<Vector3> loop = new List<Vector3>();
for(int p = 0; p < divisions; p++)
{
float u = (float)Math.Cos(2 * Math.PI * p / divisions) * radius;
float v = (float)Math.Sin(2 * Math.PI * p / divisions) * radius;
float x = 0;
float y = 0;
float z = 0;
// Apply rotation to u & v to get x, y, z
loop.Add(new Vector3(x, y, z));
}
return loop;
}
Creating the circle in 2d (u & v) was super easy but when I looked into applying 3d rotations, it seemed to be a complete rabbit hole completely beyond my comprehension.
Is there a way to use existing API to do this?
I would pass in an axis parameter that you are rotating rotation around, then use Cross products to find the "up" direction for the "forward" that is the normal of the circle.
Use Quaternion.LookRotation, then Quaternion * Vector3 to apply the rotation to the position:
public List<Vector3> Loop (Vector3 center, Vector3 rotation, Vector3 axis, float radius, int divisions)
{
List<Vector3> loop = new List<Vector3>();
for(int p = 0; p < divisions; p++)
{
float u = (float)Math.Cos(2 * Math.PI * p / divisions) * radius;
float v = (float)Math.Sin(2 * Math.PI * p / divisions) * radius;
Vector3 fromPosition = new Vector3(u, v, 0f);
Vector3 up = Vector3.Cross(rotation.normalized, axis.normalized);
Quaternion rot = Quaternion.LookRotation(rotation, up);
loop.Add(rot * fromPosition);
}
return loop;
}
So you could do something like: List<Vector3> res = Loop(Vector3.zero, Vector3.up, Vector3.right, 10f, 20);
In the function below, The distance between ball and target is
known(R). Also, the angle between the resultant vector and the x-axis is
known(LaunchAngle). Thanks to these parameters(R, LaunchAngle), I
calculate the initial velocity of a ball. I checked all the values.
According to physics, they are all correct. Although all calculations
are correct, the ball does not hit the target.
void LaunchFromTargetPositionWithoutFrictionForce()
{
Vector3 projectileXZPos = new Vector3(transform.position.x, 0.0f, transform.position.z);
Vector3 targetXZPos = new Vector3(TargetObjectTF.position.x, 0.0f, TargetObjectTF.position.z);
transform.LookAt(targetXZPos);
float R = Vector3.Distance(projectileXZPos, targetXZPos);
float G = -Physics.gravity.y;
float Vz = Mathf.Sqrt(G * R / Mathf.Sin((2.0f * LaunchAngle) * Mathf.Deg2Rad));
float Vy = Vz * Mathf.Sin(LaunchAngle * Mathf.Deg2Rad);
float Vx = Vz * Mathf.Cos(LaunchAngle * Mathf.Deg2Rad);
text2.text = "vz: " + Vz.ToString() + " vy: " + Vy.ToString() + " vx: " + Vx.ToString();
Vector3 localVelocity = new Vector3(0f, Vy, Vx);
Vector3 globalVelocity = transform.TransformDirection(localVelocity);
rigid.velocity = globalVelocity;
bTargetReady = true;
if (isSlowMotion) timeManager.slowMotion();
}
First location of the ball
And after 2-dimensional motion it is hit before target
I changed first 3 line with below codes. And problem solved.
Vector3 projectileXZPos = transform.position;
Vector3 targetXZPos = TargetObjectTF.position;
float dist = Vector3.Distance(projectileXZPos, targetXZPos);
transform.LookAt(targetXZPos);
I want to lerp between two rotations with different velocities on three different axis (yaw/pitch/roll) in unity3d, and tried to achieve that with Quaternion.LookRotation().
Quaternion.LookRotation() takes a direction Vector as first parameter, so i thought that i could lerp the direction first and then look at it with a lerped up-vector.
It should be no problem with Vector3.lerp(), but in this case i need to lerp the direction with different velocities on two axis (X and Y) relative to the initial direction.
So for example i have a camera facing a target, then the target moves up and right a bit, and now i want the camera to tilt slowly to the right too, but a bit faster up to the targets position (keeping its own position).
How to lerp the direction vector with different speeds on both axis to use it in Quaternion.LookRotation()?
EDIT:
Changed the title from "Lerp between Vector3 with different velocities on X/Y" to "Quaternion lerp with different velocities for yaw/pitch/roll" and modified the question to match the topic.
Thanks to minorlogic and the CjLib, i tried the following:
public Quaternion QuaternionLerpOn3Axis(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Lerp up direction
Vector3 vecUp = Vector3.Slerp(
rot1 * Vector3.up,
rot2 * Vector3.up,
lerpSpeedRoll
);
// Get new rotation with lerped yaw/pitch
Quaternion rotation = QuaternionUtil.Sterp(
rot1,
rot2,
rot1 * Vector3.right,
lerpSpeedYaw,
lerpSpeedPitch,
QuaternionUtil.SterpMode.Slerp
);
// Look at new direction and return rotation
return Quaternion.LookRotation(
rotation * rot1 * Vector3.forward,
vecUp
);
} else {
return rot1;
}
}
To try this without downloading CjLib, here is the whole code including the relevant parts for decoding the swing/twist:
public Quaternion QuaternionLerpOn3Axis(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Lerp up direction
Vector3 vecUp = Vector3.Slerp(
rot1 * Vector3.up,
rot2 * Vector3.up,
lerpSpeedRoll
);
// Get difference between two rotations
Quaternion q = rot2 * Quaternion.Inverse(rot1);
// Decompose quaternion into two axis
Quaternion rotYaw;
Quaternion rotPitch;
DecomposeSwingTwist(
q,
rot1 * Vector3.right,
out rotYaw,
out rotPitch
);
// Lerp yaw & pitch
rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
// Look at new direction and return rotation
return Quaternion.LookRotation(
rotPitch * rotYaw * rot1 * Vector3.forward,
vecUp
);
} else {
return rot1;
}
}
public static void DecomposeSwingTwist(
Quaternion q,
Vector3 twistAxis,
out Quaternion swing,
out Quaternion twist
) {
Vector3 r = new Vector3(q.x, q.y, q.z); // (rotation axis) * cos(angle / 2)
float Epsilon = 1.0e-16f;
// Singularity: rotation by 180 degree
if (r.sqrMagnitude < Epsilon) {
Vector3 rotatedTwistAxis = q * twistAxis;
Vector3 swingAxis = Vector3.Cross(twistAxis, rotatedTwistAxis);
if (swingAxis.sqrMagnitude > Epsilon) {
float swingAngle = Vector3.Angle(twistAxis, rotatedTwistAxis);
swing = Quaternion.AngleAxis(swingAngle, swingAxis);
} else {
// More singularity: rotation axis parallel to twist axis
swing = Quaternion.identity; // no swing
}
// Always twist 180 degree on singularity
twist = Quaternion.AngleAxis(180.0f, twistAxis);
return;
}
// Formula & proof:
// http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
Vector3 p = Vector3.Project(r, twistAxis);
twist = new Quaternion(p.x, p.y, p.z, q.w);
twist = Normalize(twist);
swing = q * Quaternion.Inverse(twist);
}
public static Quaternion Normalize(Quaternion q) {
float magInv = 1.0f / Magnitude(q);
return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
By now this is the only way i could achieve a quaternion (s)lerp with different velocities on three different axis with a reasonably acceptable result.
But in my opinion it is not a real mathematical solution, it does not work really well if the lerp values are below ~1.5f (especially the Z/Roll-axis), and it has much overhead.
Any ideas how to solve this puzzle with less/better code?
...another approach:
Now i tried to extend the concept of decomposing the swing/twist to decomposing yaw/pitch/roll.
This works fine (?) if the target does not flip over 180°, and it still needs some input/feedback from someone who really knows how to deal with quaternion rotations.
public Quaternion QuaternionLerpYawPitchRoll(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Decompose quaternion into yaw/pitch/roll
Quaternion rotYaw;
Quaternion rotPitch;
Quaternion rotRoll;
DecomposeYawPitchRoll(rot1, rot2, out rotYaw, out rotPitch, out rotRoll);
// Lerp swing & twist
rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
rotRoll = Quaternion.Slerp(Quaternion.identity, rotRoll, lerpSpeedRoll);
// Combine yaw/pitch/roll with current rotation
return Quaternion.LookRotation(
rotPitch * rotYaw * rot1 * Vector3.forward,
rotRoll * rot1 * Vector3.up
);
} else {
return rot1;
}
}
public static void DecomposeYawPitchRoll(
Quaternion rot1,
Quaternion rot2,
out Quaternion yaw,
out Quaternion pitch,
out Quaternion roll
) {
Vector3 pitchAxis = rot1 * Vector3.right;
Vector3 rollAxis = rot1 * Vector3.forward;
Vector3 yawAxis = rot1 * Vector3.up;
// Get difference between two rotations
Quaternion diffQ = rot2 * Quaternion.Inverse(rot1);
Vector3 r = new Vector3(diffQ.x, diffQ.y, diffQ.z); // (rotation axis) * cos(angle / 2)
float Epsilon = 1.0e-16f;
// Singularity: rotation by 180 degree
if (r.sqrMagnitude < Epsilon) {
Vector3 rotatedPitchAxis = diffQ * pitchAxis;
Vector3 rotatedYawAxis = Vector3.Cross(pitchAxis, rotatedPitchAxis);
Vector3 rotatedRollAxis = diffQ * rollAxis;
if (rotatedYawAxis.sqrMagnitude > Epsilon) {
float yawAngle = Vector3.Angle(pitchAxis, rotatedPitchAxis);
yaw = Quaternion.AngleAxis(yawAngle, rotatedYawAxis);
} else {
// More singularity: yaw axis parallel to pitch axis
yaw = Quaternion.identity; // No yaw
}
if (rotatedRollAxis.sqrMagnitude > Epsilon) {
float rollAngle = Vector3.Angle(yawAxis, rotatedYawAxis);
roll = Quaternion.AngleAxis(rollAngle, rotatedRollAxis);
} else {
// More singularity: roll axis parallel to yaw axis
roll = Quaternion.identity; // No roll
}
// Always twist 180 degree on singularity
pitch = Quaternion.AngleAxis(180.0f, pitchAxis);
} else {
// Formula & proof:
// http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
pitch = GetProjectedRotation(diffQ, pitchAxis);
roll = GetProjectedRotation(diffQ, rollAxis);
yaw = diffQ * Quaternion.Inverse(pitch);
}
}
public static Quaternion GetProjectedRotation(Quaternion rotation, Vector3 direction) {
Vector3 r = new Vector3(rotation.x, rotation.y, rotation.z);
Vector3 proj = Vector3.Project(r, direction);
rotation = new Quaternion(proj.x, proj.y, proj.z, rotation.w);
return Normalize(rotation);
}
public static Quaternion Normalize(Quaternion q) {
float magInv = 1.0f / Magnitude(q);
return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
Author of CjLib here.
It sounds like you actually don't need swing-twist decomposition.
I'd say just get the decomposed yaw/pitch/row for the current quaternion and desired quaternion. And then update the yaw/pitch/row values depending on how fast you want them to individually track the target value, and generate an updated quaternion from that set of yaw/pitch/row values.
Lerping with a maximum speed cap (which I refer to as "seeking") might be fine, but it would not look smooth. I recommend using critically-damped numeric springing. And here's a shameless placement of a 3-part series I wrote on this topic.