I have a character that can only turn on the Y-axis, so left and right basically, if up is Vector3.up. The character has a child which is the position where grenades spawn (his right hand), and now I'm trying to figure out how I can calculate the correct rotation for the parent so the child looks exactly at a target, but only "horizontally". The x and z rotation of the parent must be 0. Is there a way to do that? It's fine if the child only looks into the correct direction on the "horizontal plane", but doesn't look up or down to face the target perfectly.
This answer almost does what I want: http://answers.unity.com/comments/1339850/view.html
The only problem is that the resulting rotation is a mix of all three axes, instead of it being just a y-rotation.
I would use trigonometry to solve this. Explanation in comments:
Vector3 rotAxis = Vector3.up;
// flatten child pos, target pos, char pos, and child forward.
Vector3 flatChildPos = Vector3.ProjectOnPlane(childTransform.position, rotAxis);
Vector3 flatTargetPos = Vector3.ProjectOnPlane(targetTransform.position, rotAxis);
Vector3 flatCharPos = Vector3.ProjectOnPlane(charTransform.position, rotAxis);
Vector3 flatChildForward = Vector3.ProjectOnPlane(childTransform.forward, rotAxis);
Vector3 flatCharToTarget = flatTargetPos-flatCharPos;
Vector3 flatChildToChar = flatCharPos-flatChildPos;
// Find the angle going from the direction of child to parent to the child's forward
// The sign will come in handy later
float childAngle = Vector3.SignedAngle(flatChildToChar, flatChildForward,
rotAxis);
float absChildAngle = Mathf.Abs(childAngle);
// Find the distance between child and character:
float childDist = flatChildToChar.magnitude;
// Find the distance between character and target:
float targetDist = flatCharToTarget.magnitude;
// Consider the triangle made by character position, position of target, and
// desired child position. Use sin rule to find angle of target's corner
float targetAngle = Mathf.Rad2Deg * Mathf.Asin(
Mathf.Sin(absChildAngle * Mathf.Deg2Rad)
* childDist/targetDist);
// determine angle in parent's corner:
float desiredParentAngle = 180f - absChildAngle - targetAngle;
// Determine sign of angle at character from target to child.
// It's the same as the sign of angle at child from char to target.
float sign = Mathf.Sign(childAngle);
// Consider the triangle made by character position, position of target, and
// current child position. Determine the current signed angle in character corner.
float currentParentAngle = Vector3.SignedAngle(flatCharToTarget, -flatChildToChar,
rotAxis);
// Calculate the diff in angle needed to make the current triangle the desired one.
float diffAngle = desiredParentAngle * sign - currentParentAngle;
// Apply the diff in world space
Quaternion newRot = Quaternion.AngleAxis(diffAngle, rotAxis) * charTransform.rotation;
Interestingly, in some situations there maybe two possible rotations. Determining when that is the case and what the second rotation is left as an exercise to the reader. Hint: there are sometimes multiple valid values for targetAngle
and desiredParentAngle
also ;)