It took me some time to advance on that problem. The problem is not completely solved in the case of "two articulations" but it works for one.
The answers from Morion and derHugo had been useful (it bootstraped me reading further the unity documentation and made me discover Quaternions) but I had a problem when using the transform
methods.
The problem if I am using transform.Rotate
method or hard writing a Quaternion
in transform.rotation
is that it positions the object without any concern for the "articulation constraints" or the "physics".
If I am using it: why bothering defining an articulation ?
I was looking for a sort of a function that imposes a movement on an articulation while still respecting the angle and position limitations.
I found those:
These functions apply a force or a torque on the articulation.
If I use these functions, not only the articulation constraints will be respected but also the physics is respected.
Problem: implementation becomes more challenging.
I want a "rotation" so I need AddTorque
but I still need to find the rotation axis of the torque, this axis may vary if the shoulder is moving. I also want the torque to apply rotation in one direction or the other depending on the angle between the direction I want and the direction of the object.
Another thing I understood during this journey is that there is a "local coordinates" axis local to every game object. What will prove useful is that these coordinates rotate and move together with the game object when the simulation runs.
In the local coordinates of the arm the arm is always pointing to the same direction. All I have to do is to figure out:
- the global coordinates of where my arm is pointing to (
currentDirection
).
- the global coordinates of where I want the arm to point to (
target
).
For the currentDirection
it fits in one line of code:
Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;
For the target I believe I am not too far if I do this:
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * initialDir).normalized;
However for the second articulation whose rotation depends on the rotation of the previous one I think that is closer to the truth:
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;
I am not sure I can use the Quaternion.Euler
here that is maybe the reason why it doesn't work well with the elbow.
Then the torque I want is proportional to the cross vectorial product between those two:
Vector3 torqueVector = force * Vector3.Cross(currentDirection, target);
First a few member variables
public string articulationName; // name of the articulation for the logs
public float targetYAngle; // desired angle around Y axis
public float targetZAngle; // desired angle around Z axis
public float force; // amplify the torque using this factor
public ArticulationBody articulation; // articulation component
public Vector3 localArmInitialDir; // unit axis "forward" of the arm in the local coordinates of the arm referential
public Vector3 initialDir; // forward direction of the arm in the begining

In this example the "forward" axis of the arm is (0, -1, 0)
"arm pointing downwards" and the rotation axis is the z axis (0, 0, 1)
.
The initialDir
is computed like this in the Start
function:
void Start()
{
initialDir = transform.TransformVector(localArmInitialDir).normalized;
}
Next important thing is I need to write the code in the FixedUpdate
function instead of Update
because I am hitting on physics.
Then the code is:
void FixedUpdate() {
Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;
Debug.Log(articulationName + " current direction: " + currentDirection);
Debug.Log(articulationName + " current target: " + target);
float angle = Quaternion.Angle(Quaternion.FromToRotation(initialDir, currentDirection), Quaternion.FromToRotation(initialDir, target));
Debug.Log(articulationName + " current angle: " + angle);
Vector3 torqueVector = force * Vector3.Cross(currentDirection, target);
Debug.Log(articulationName + " force: " + torqueVector);
articulation.AddTorque(torqueVector);
}
I can apply the script on the shoulder and it "kind of works". The arm bounces a bit, I can reduce that effect by increasing both the torque's force and the articulation "joint friction".
My problem now is when I run that script on the elbow. The movements of the elbow are affecting the shoulder articulation as well.