My problem is making the player’s head look at the opponent. It works almost great. Since the axes from blender are different from Unity, I had to do some conversion. See my code below.
The rotation I care about is the local x rotation, because in Unity, it behaves like the y local rotation. If just do the headBone.lookAt(opponent), then I get the x rotation I want, but then the z and y rotations are messed up, so I do some conversion to set those both to zero through local rotations because I do not need them to rotate. After doing the conversion, it works fine, however, the x rotation does not go beyond -90 or 90 degrees. Is there a way I can fix this and only set the y and z axis to zero, but leave the x alone?
//the axes are messed up since this model is from blender. In this case, the x local axis controls what would have been the y local axis (rotate head left to right). the y local axis controls what would have been the x local axis, and the z local axis works as it is.
headBone.LookAt(opponent); //i just make the bone look at the opponent. This would give a weird rotation
Quaternion localRot = headBone.localRotation;
localRot.eulerAngles = new Vector3(localRot.eulerAngles.x, 0, 0); //I extract the x local rotation and set the others to zero since I do not need them to rotate.
headBone.localRotation = localRot; //I set the local rotation, and it mostly works, however, the x rotation does not go outside of -90 or 90 degrees. If I pass by my opponent at 90 degrees, the head bone x rotation just rotates the other way instead of going to 110...120...130, etc
The problem you are facing has to do with the difference between quaternions and euler angles. Quaternions uniquely describe rotations and orientations, or in other words, given any specific orientation or rotation there is exactly ONE quaternion that corresponds to it. However, for any one particular orientation there are an infinite number of euler angles that describe it.
So, when you request the euler angles from your “localRot” quaternion, Unity gives you some arbitrary euler representation of the quaternion. I’m not sure how it chooses which orientation to give you or how the math works, but it gives you the simplest euler representation it can find. For small rotations this is not a problem but once you start asking for larger rotations (in your case more than 90 degrees) unity starts to give you euler representations you do not expect, but are indeed simpler. The result of this however, is that even though you think that removing the y and z components of the euler angles are effective, they are actually responsible for a larger part of your rotation than you expect. So the piece of the rotation that you are left with only contains rotation information from -90 to 90 degrees in the x axis.
So how do you solve this? Well since you only want rotation in 1 axis you can calculate this pretty easily yourself by just calculating the angle between where you want to look and the forward vector of the head. This code also assumes the head is also always parallel to the would axis, if the head is ever tilted (like by going over uneven terrain) this code won’t work as well.
NOTE - This is sudo code and has not been tested, you may need to make some tweaks like some sign changes or axis changes but this should point you in the right direction.
headBone.rotation = Quaternon.identity; //Just to simplify calculation lets remove the current rotation for now
//Get the vector from your head to the opponent
Vector3 toOpponent = opponent - headBone.position;
toOpponent.x = 0; //Put the vector in the YZ plane so that we get a simple x rotation. This is ultimately the direction we want to look in.
float xAngle = Vector3.Angle(headbone.forward, toOpponent);
//Returns the shortest angle (between 0 and 180), so we need to normalize from -180 to 180
if(toOpponent.y < 0) xAngle *= -1;
headBone.rotation = Quaternion.Euler(xAngle, 0, 0);