Find the offset between the rotation of a bone in Unity and in the original rig - unity3d

Goal:
Calculate a rotation, that can be used, to correct the Transform of the bones, so they get rotated as expected when manually rotated.
In Detial:
Character's bone Transform is not imported to Unity correctily, ie. the left hand's Z axis does not looks towards the top of the hand, but backwards, while the right hand's Z axis looks forward.
The bones default rotation is not the same as you would expect if you have seen the rig in another application before.
I had the idea to create a class, that puts the character in T-Pose, then maps the bones rotation.
(I would assume that Unity does something similiar under the hood, that's how I got the idea.)
So the class would be used with an API like this:
/// This should return the rotation, where the bone pointing forward rotated by rotation.
/// Ie. when rotation = Quaternion.Identity the left hand should look forward.
Quaternion CorrectRotation(HumanBodyBones bone, Quaternion rotation)
{
return Quaternion.Inverse(tPoseRotations[bone]) * rotation;
}
The problem is, that I can't seem to find a good way to map theese rotations.
My last attempt was this:
Vector3 boneDirection = (boneTransform.position - parentTransform.position).normalized;
Quaternion mappedRotation = Quaternion.LookRotation(boneDirection, parentTransform.up);
As you can see in this image, with this method the hands look in the same direction, forward, but still not rotated correctly, they have an offset from the desired result. (The correct rotation is shown by the purple hands.)
When given other rotations, the hands follow with the same offset, so other then this unwanted offset they work correctly.
So basically, I would appriciate any help to fix my problem, either this way, or with a different solution, that works with different rigs.

Related

Rotate a wheel with camber angle

I'm new to 3D design, so this might be a silly question.
I have trouble simulating wheel camber using rotation with Quaternion.
What I understand is that the rotation of the WheelCollider occurs along the X axis, whereas in my case the "visual wheel" is rotated 80 ° along the Z axis so the rotation occurs along the Y axis.
This code works well with the FL wheel because its axis is aligned with the collider axis, while it doesn't work with the FR wheel.
collider.GetWorldPose(out Vector3 position, out Quaternion rotation);
visual.transform.position = position;
// initialVisualRotation is the wuaternion rotation of the visual at Startup
// in this case: X=0 Y=0 Z=80
visual.transform.rotation = initialVisualRotation * rotation;
These are my local and global rotation axes for the right front wheel (FR).
How can I "adjust" the wheel rotation without change initial axes (as I did for the FL wheel)?
Are there some quaternion operations to adopt in these cases?
A bit of workaround for not having to deal with messed up transforms is to make object a child of another empty object.This way you can make parent object rotated to make wheel tilted but Child( actual wheel) will have its original axis always not messed with and so you can use simple script to always rotate it around same axis that is always going to be the same despite whole system(hierarchy of objects) being " tilted".
Parent is responsible on rotating around one axis and child around another.
I think you need to take a step back and arrange your prefabs this way as it seesm you did not.

Walk around square platform. Unity2D

I'm trying to make a character that walks around a platform and if the character reach a corner, it rotates and continue walking on the side of the platform, same with the bottom part.
This is a visual representation of what I'm trying to achive.
Movement
The specific problem is when the character reach the corners, the rotation just go crazy. I'm trayng to achive this using a raycast from the character to the platform and if the raycast doesn't find floor, I start the rotation like this:
times++;
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.AngleAxis(times*-89.9f,Vector3.forward),Time.deltaTime * RotationSpeed);
_characterGravity.SetGravityAngle(transform.localEulerAngles.z);
I'm using a characterGravity script that allows me to change the gravity direction for the character in order to not fall when is walking upside down or on the sides. But this is not working propperly. Is there a better way to do this?
Assuming your 2D view is aligned on the X-Y plane, with the Z-axis aimed into the screen (the same direction the camera is facing), I suggest using Transform.Rotate() instead of trying to linearly interpolate the rotation between two values:
if (ShouldRotateAroundCorner()) {
transform.Rotate(Vector3.forward, RotationSpeed * Time.deltaTime);
}
You'll just need to make sure your ShouldRotateAroundCorner() knows when to start and stop the 90 degree turn, which will take a little bit of additional code to keep track of when it's in this state change.

Which rotation is shown in the Inspector?

The chest bone of my player can be rotated while aiming.
Now I wanted to evaluate how much (minimum and maximum rotation) I should let the chest be rotatable.
To do that, I allowed all degrees of rotation and took a look at the Inspector.
For example, the minimum value that the chest should be rotatable to the left should be Y=-15.
At Y=-15 (seen in the Inspector), it still looked natural.
Now I wanted to code this.
To my surprise, chest.localRotation.Y was a completely different value than what the Inspector is showing.
I have then taken a look at the chest variable and extended the view.
I just can't see the rotation value that the Inspector is showing.
How should I go on in this case, please?
I'm using this to rotate the bone:
Chest.LookAt(ChestLookTarget.position);
Chest.rotation = Chest.rotation * Quaternion.Euler(Offset);
Thank you!
The reason why it doesn't work:
Quaternion is not a human readable value.
One Quaternion is allways unique but can have multiple (infinite?) different representations in Euler space! The other way round one Euler represents allways exactly one Quaternion value.
If you look at the docs it explicitly says
Don't modify this directly unless you know quaternions inside out.
Than as said what you see in the inspector is the localRotation in relation to the parent Transform.
Better said it is one of the many possible Euler inputs that result in the Quaternion. What you see in the debug at localEulerAngles is another possible Euler representation. Unity usually in localEulerAngles also gives you only values > 0.
It seems that the chest anyway will only rotate around the Y axis, right?
If this is the case you can simply get the Angle between the chest's original forward vector and the target. It is way easier to handle Vector3 values than Quaternions ;)
It seems to be the same use case as in this post
// get the target direction
Vector3 targetDir = ChestLookTarget.position - Chest.position;
// Reset any difference in the Y axis
// since it would change the angle as well if there was a difference I the height
// between the two objects
targetDir.y = 0;
// however you currently rotate
// instead rotate only the Vector3 variable without applying it to the transform yet
Vector3 newDir = Vector3.RotateTowards(Chest.forward, targetDir, RotationSpeed * Time.deltaTime, 0.0f);
// Compare the target direction to the parents forward vector
float newAngle = Vector3.Angle(Chest.parent.transform.forward, newDir);
if (newAngle > MaxRotationAngle)
{
// What should happen if angle gets bigger?
return;
}
// If angle still okey set the new direction
Chest.rotation = Quaternion.LookRotation(newDir);

Why am I getting an incorrect vector when trying to find HingeJoint2D.anchor in world space?

In the scene, I have a long chain of children that are connected via hinge to their parent. For my code, I need the position of the hinge anchors in world space, so I use:
public Vector2 hingeVector => hinge.anchor + (Vector2)gameObject.transform.position;
For the first hinge, that code gives the correct position. But for the second hinge this happens:
The red point is the vector I get, the blue point is the actual position. As you can see, it's a somewhat small but still problematic difference.
Is there any way I can fix this? I couldn't find anything like this online.
You need to add the object's rotation
The anchor values are axis aligned and aren't affected by rotation, but in order to calculate the anchor point in world space, knowing the transform's position, you need to rotate the anchor point values by the object's rotation then add it to the position:
Vector2 p = hinge.anchor.Rotate(gameObject.transform.rotation.eulerAngles.z)
+ (Vector2)gameObject.transform.position;

How do I apply rotations to parent child using Rigidbody?

I have a finger object. It is just three cubes representing the finger parts.
The 2nd cube is the child of the 1st one. And the 3rd cube is the child of the 2nd one.
This is the heirarchy: Cube1 -> Cube2 -> Cube3
My goal is to apply a rotation angle to the first cube and let the other cubes do the same locally.
Example: Apply 30 degrees Z rotation to the first cube, 30 degrees Z rotation to the 2nd cube, and also the 3rd one.
This will make a finger that look like this:
(Forgive me if it doesn't look like a finger)
In every Update() frame, I will change the angle (it's just one number) and it will rotate every cube for me.
My question is:
How do I make all these cubes collide properly with other objects?
I tried putting the Rigidbody on all of them and set isKinematic=false because I want to transform them myself. But I still cannot use transform.rotation to update my rotation because it will miss the collision with a ball very easily (especially the tip of the finger because it moves faster than other parts). Continuous detection doesn't help.
So I tried using rigidbody.MoveRotation() and rigidbody.MovePosition() instead, which is a pain because they need absolute values. They worked but the animation is so jumpy when I change the angle quickly.
I'm guessing that the animation is jumpy because there are many Rigidbodies or because the physics engine cannot interpolate the position of each box properly when I use MoveRotation() and MovePosition().
I need to use MovePosition() also because when I use only child.MoveRotation(transform.parent.rotation * originalChildLocalRotation * Quaternion.Euler(0, 0, angle)), the position doesn't move relative to the parent. So I have to compute child.MovePosition(parent.TransformPoint(originalLocalPositionOfTheChild)) every frame too.