Quaternion correction - unity3d

I'm trying to implement network correction for a client simulated Rigidbody. 30 times a seconds I get the target rotation from which I calculate the rotation correction. Then I apply this correction over a number of frames.
Network update:
rotationCorrection = receivedRotation * Quaternion.Inverse(transform.rotation);
Every frame:
var a = Mathf.Min(1, Time.deltaTime * 8);
var final = Quaternion.Slerp(transform.rotation, transform.rotation * rotationCorrection, a);
var actualCorrection = final * Quaternion.Inverse(transform.rotation);
rotationCorrection *= Quaternion.Inverse(actualCorrection);
//rotationCorrection = Quaternion.Slerp(rotationCorrection, Quaternion.identity, a); // First try
_rigidbody.MoveRotation(final);
The reason I don't just interpolate the current rotation to the corrected on is the GameObject contains a Rigidbody which should simulate the object in addition to the correction.
This does work sometimes (at least the first try version), except at angles between roughly -90 and 90. I suspect its the code to slerp the rotationCorrection to identity.

(work in progress since the question was not 100% clear)
The subtraction formulae yield wrong results. Please try using euler, subtract the angles and you can make the desired quaternion.
Are you sure you don't need Lerp instead of Slerp?
Anyway final should not use Lerp or Slerp.
If as final you want transform.rotation * rotationCorrection (which is the current rotation plus the correction), then just use it as that. Multiplications actually add the rotation.
Lerp and Slerp are to be used with an a parametric to time, where a==0 will return the starting rotation, a==1 will return the final rotation. So a should be scaled with time. If, on the other hand, the network correction is exactly what you need and you get it at specific fixed-time updates, then without any Lerp, might as well try transform.rotation = transform.rotation*rotationCorrection.
rotationCorrection = receivedRotation * Quaternion.Inverse(transform.rotation);
What this does is return a rotation that starts from receivedRotation and ends to the opposite rotation of the current transform rotation. So neither the start nor the end point match with the current one.
so:
You set a final quaternion as currentRot * change
a starting as currentRot
and then update transform.rotation by making it = (S)Lerp with start, end and a parameter that equals 0 at starting time and 1 at ending time.

The network update was wrong. Corrected:
rotationCorrection = Quaternion.Inverse(transform.rotation) * receivedRotation;

Related

Unity make Joystick always result in speed 1

So I have a joystick object, which gives me values of -1 to 1 for each axis.
float horizontalMove = joystick.Horizontal * speed;
float verticalMove = joystick.Vertical * speed;
rb.velocity = new Vector3(horizontalMove, verticalMove, 0);
Now, what I want is that no matter how far you pull the joystick in each direction, it will always result in speed 1. Just like how my current code works, but my joystick is always pulled to the edge. I also made it so max. 1 directions can be set to 0.
You can use the .normalized property of the vector, which ensures it either has length 1, or is equal to Vector3.zero.
rb.velocity = new Vector3(horizontalMove, verticalMove, 0).normalized;
Unlike using Mathf.Sign on each axis, the angle of the vector is preserved, so the player will still be able to move in any orientation, not just along axes and diagonals.

How to move an object by a certain angle over a period of time in a circle [duplicate]

I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
Most examples out there including Unity examples from their official website are using Lerp in the wrong way. They didn't even bother to describe how it works in the API documentation. They just starch it in the Update() function and call it a day.
Mathf.Lerp, Vector3.Lerp, and Quaternion.Slerp work by changing from one position/rotation to another with the t value(last parameter) being passed in.That t value is also know as time.
The min of the t value is 0f and the max is 1f.
I will explain this with Mathf.Lerp to make it easier to understand. The Lerp functions are all the-same for both Mathf.Lerp, Vector and Quaternion.
Remember that Lerp takes two values and returns values between them. If we have a value of 1 and 10 and we do Lerp on them:
float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
float x = Mathf.Lerp(1f, 10f, 1f); will return 10
As you can see, the t(0) returns the min of the number passed in, t(1) returns the max value passed in and t(0.5) will return mid point between the min and the max value. You are doing it wrong when you pass any t value that is < 0 or > 1. That code in you Update() function is doing just that. Time.time will increase every second and will be > 1 in a second, so you have problems with that.
It recommended to use Lerp in another function/Coroutine instead of the Updated function.
Note:
Using Lerp has a bad side of it when it comes to rotation. Lerp does not know how to rotate Object with the shortest path. So bear that in mind. For example, you have an Object with 0,0,90 position. Lets say you want to move the rotation from that to 0,0,120 Lerp can sometimes rotate left instead of right to reach that new position which means it take longer to reach that distance.
Let's say we want to make the rotation (0,0,90) from whatever the current rotation is. The code below will change the rotation to 0,0,90 in 3 seconds.
ROTATION OVER TIME:
void Start()
{
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Quaternion currentRot = gameObjectToMove.transform.rotation;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
INCREMENTAL ANGULAR ROTATION OVER TIME:
And to just rotate the Object to 90 in z axis, the code below is a great example of that. Please understand there is a difference between moving Object to new rotational point and just rotating it.
void Start()
{
StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;
Vector3 currentRot = gameObjectToMove.transform.eulerAngles;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
All my examples are based on frame-rate of the device. You can use real-time by replacing Time.deltaTime with Time.delta but more calculation is required.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.

Rigidbody.Moveposition issue

I'm trying to make a type of projectile with Rigidbody:
private void FiredAsProjectile(GameObject target)
{
Vector3 moveVector = Vector3.Zero;
float velMod = 8f // A placeholder value to later calculate with mass / velocity
if(target != null)
{
moveVector = (target.transform.position - transform.position);
m_rb.MovePosition(transform.position + moveVector * Time.deltaTime * velMod);
}
}
This is updated via FixedUpdate upon calling this method somewhere else. I have a few things I needed this to behave: to have velocity output and move to a position, not direction.
However, I'm getting a weird outcome. Even though this object moves as a kinematic rigidbody and its interpolation is set to None this object slows down before reaching the target vector as if it has interpolation. Let's say I wanted a bullet to be fired from the barrel and fly to a point in world instead of direction, that's what I wanted this object to behave like, but not quite so.
Am I missing something or misunderstanding something?
Is there a better way to move this kinematic rigidbody whilst still outputting rigidbody.velocity and still collide?
This happens because you don't Normalize the moveVector.
To explain why this happens, we'll suppose that Time.deltaTime = 0.01 every step:
At t0 time, let's say that the distance between the moving object (in x = 0) and the target object (x = 100) is 100.
The argument of MovePosition is 0 + 100*8*.01 = 8 and the object is moved accordingly to this new position (since you said it's Kinematic without interpolation)
At t1 time, moveVector = 100-8 = 92
The new argument now is 8 + 92*8*.01 = 15.36
Notice that between t0 and t1 you moved 8 units, whereas between t1 and t2 you moved only 7.36 units. So, the more your moving object is near the target object, the less you'll move the object, giving the effect of "slowing down" (and moreover it'll never reach the target object, you'll stop at a distance equal to the minimum floating number precision).
In order to fix this, you just need to normalize the moveVector, i.e. making its module fixed every step. So, just add this line:
moveVector = (target.transform.position - transform.position);
moveVector.Normalize();
m_rb.MovePosition(transform.position + moveVector * Time.deltaTime * velMod);
and the distance moved every step will always be the same regardless of distance.

rotate Object towards another in Unity3D

I have already this function from this question. I changed the sign of the rotation:
void rotateBotConnector()
{
Vector3 diff = (player.transform.position - botConnector.transform.position).normalized;
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
botConnector.transform.localRotation = Quaternion.Euler(0f, 0f, -(rot_z - 90f));
}
But the problem is, that now my object follows the player on the XZ plane but when the rotation reaches a certain degree, left or right, the object stops to rotate towards my player.
For better understanding: http://imgur.com/vWaqc31
Why not just use:
var target : Transform;
transform.LookAt(Vector3(target.transform.position.x, target.transform.position.y, transform.position.z);
It seems a lot easier than using euler. This way you look at target's x & y but transform your z.
Also I'm no expert with euler but it seems like it is limited to a 90 degree turn and I think this may be the reason why:
Quaternion.Euler(0f, 0f, -(rot_z - 90f));
Unless you have absolute necessity to rotate via angles maybe you'd rather go with manipulating the forward vector of the object, it's easier to understand the logic this way:
void rotateBotConnector()
{
Vector3 targetForwad = botConnector.transform.position - player.transform.position;
targetForward.y= 0f;
targetForward.Normalize(); // <-- this is very expensive function try avoid using it whenever possible
botConnector.forward = Vector3.Lerp(botConnector.forward, targetForward, Time.deltaTime));
}
once again, if you are short on cpu cycles you may want to go for trying to calculate angles instead of vectors. But in most cases this is ok. (the code needs a spell checking)

Quaternion rotation in coroutine does not work properly?

Alright, I'm trying to make an object called 'pathblock' rotate 90 degrees clockwise every time the player presses 'E'. The player should be able to spam the button, with the block turning 360 degrees without fail.
I want to see the rotation animation, so I am using coroutine in combination with a slerp function. The coroutine is called in update, if E is pressed.
//Rotates the selected pathblock by 90 degrees over a specified time. A coroutine is necessary to render each slerp() result per seperate frame
IEnumerator RotatePathblock()
{
Debug.Log("Start rotation!");
Quaternion start = Quaternion.Euler(new Vector3(pathblock.transform.rotation.x, pathblock.transform.rotation.y, pathblock.transform.rotation.z)); //Set start variable for Slerp(), the current rotation of the pathblock
Quaternion end = Quaternion.Euler(new Vector3(start.x, start.y, start.z-90.0f));
Debug.Log(string.Format("Target Angle: {0}", end.eulerAngles.z));
float normalizationFactor = 1.0f / pathblockRotationTime; //We need to normalize time since slerp() works with values between 0-1; we can convert values by multiplying with this factor
float timePassed = 0.0f; //Time passed since the start of the linear interpolation. Starting at 0, it increases until it reaches 1. All values are rendered.
while(timePassed < 1.0f) //While the time passes is less than 1 (the maximum of a linear interpolation)
{
timePassed += Time.deltaTime * normalizationFactor; //Increase the timePassed with the time passed since the last frame; the time is first normalized
pathblock.transform.rotation = Quaternion.Slerp(start, end, timePassed); //Set the pathblock rotation to a new value defined by linear interpolation
yield return null; //Stop the function, finish Update() and return to this while loop; this will cause all slerp() values to render, resulting in a smooth animation
}
}
The first press turns the pathblock 90 degrees, as expected.
The second press sets the pathblock back to its original rotation, and turns it 90 degrees again.
This leads me to believe that the 'start' variable never changes, even though it should set itself to the new pathblock.transform.rotation when the function is called, getting the new rotation.
If anyone could look at what's wrong, I'd appreciate it.
Any other critique on my code, comments etc. would also be appreciated!
Quaternion start = Quaternion.Euler(new Vector3(pathblock.transform.rotation.x, pathblock.transform.rotation.y, pathblock.transform.rotation.z)); //Set start variable for Slerp(), the current rotation of the pathblock
Should be:
Quaternion start = Quaternion.Euler(new Vector3(pathblock.transform.eulerAngles.x, pathblock.transform.eulerAngles.y, pathblock.transform.eulerAngles.z)); //Set start variable for Slerp(), the current rotation of the pathblock