quaternion.slerp issue

I am trying to make an enemy turn to face my character on the Y axis and make the arms move on the X axis so that the guns on the end of the arms are pointing at me.

The the arms are a seperate object but are atteached to the enemy so if it turns so do they.

I have both sets of rotations working properly but if I have both running at the same time then it starts affecting the arm’s Y axis even though the code explicitly removes any Y axis movement from the arms. The only Y rotation should come directly from the enemy but that shouldn’t show in the arm’s rotation in the inspector and in this it is changes even though the slerp fuction should be moving it between 0 and 0 on the Y and Z axes.

This is the code now:

var arms:GameObject;
var leftGun:GameObject;
var rightGun:GameObject;
var bullet:GameObject;
private var lastKnownPlayerLocation:Transform;
private var currentAimAngle:float;

function Update () {
	aim();
}
    
function aim(){
		var newRotation = Quaternion.LookRotation(lastKnownPlayerLocation.position - this.transform.position, Vector3.up);
		newRotation.x = 0.0;
    	newRotation.z = 0.0;
		transform.rotation = Quaternion.Slerp (transform.rotation, newRotation, Time.deltaTime * 5.0);
		
		
		var new2Rotation = Quaternion.LookRotation (lastKnownPlayerLocation.position - arms.transform.position, Vector3.up);
		new2Rotation.y = 0.0;
		new2Rotation.z = 0.0;
		arms.transform.rotation = Quaternion.Slerp (arms.transform.rotation, new2Rotation, Time.deltaTime * 5.0);
}

You can calculate the rotation manually. Since you only need one angle is’t not that hard :wink:
Assuming the enemy already faces the player, you just need to calculate the arcsin.

Vector3 dir = lastKnownPlayerLocation.position - arms.transform.position;
float opposite = dir.y;
float hyp = dir.magnitude;
float angle = Mathf.Asin(opposite / hyp) * Mathf.Rad2Deg;

arms.transform.localEulerAngles = new Vector3(angle,0,0);

Haven’t tried the code, maybe you need to invert the angle

Two problems that I see. First, you’re treating the quaternion x/y/z components as if they are Euler angles – which they aren’t. You can get the canonical euler angles of a rotation using the eulerAngles property. (And you can convert them back into quaternion notation using Quaternion.Euler.)

Secondly, you’re doing a world-transform manipulation of the arms, which complicates things. I would just modify the localRotation. (Note I assume your arms are children of the object that is doing these calculations.)

So, taking those two into account:

   	Quaternion newRotation = Quaternion.LookRotation(lastKnownPlayerLocation.position - transform.position, Vector3.up);
	transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.Euler (0, newRotation.eulerAngles.y, 0), Time.deltaTime * 5.0f);

	Quaternion new2Rotation = Quaternion.LookRotation (lastKnownPlayerLocation.position - arms.transform.position, Vector3.up);
	arms.localRotation = Quaternion.Slerp (arms.localRotation, Quaternion.Euler (new2Rotation.eulerAngles.x, 0, 0), Time.deltaTime * 5.0f);	

Note that I do not use JS, so it is an exercise left to the questioner to convert this to JS:

When you modify the quaternion’s components that way you’re producing non-normalized quaternions, which may cause erroneous rotations. As @chainedlupine also said, you should not mix rotations and local rotations. You should find the target direction and zero its Y component to keep it horizontal, and get the local target direction and zero its X component to keep it vertical, then Slerp rotation to the horizontal direction and Slerp localRotation to the vertical direction - like this:

function aim(){
    // dirH = player direction
    var dirH = lastKnownPlayerLocation.position - this.transform.position;
    // dirV = arms local player direction
    var dirV = arms.transform.InverseTransformDirection(dirH);
    dirH.y = 0; // keep dirH strictly horizontal
    dirV.x = 0; // keep dirV strictly vertical
    var newRotH = Quaternion.LookRotation(dirH, Vector3.up);
    var newRotV = Quaternion.LookRotation(dirV, Vector3.up);
    transform.rotation = Quaternion.Slerp (transform.rotation, newRotH, Time.deltaTime * 5.0);
    arms.transform.localRotation = Quaternion.Slerp (arms.transform.localRotation, newRotV, Time.deltaTime * 5.0);
}

why not just set the arms to the parents facing rotation ? and save yourself the trouble of trying to work it out ?

Get rid of trying to calculate a whole new rotation for the arms and let them be dragged around the Y axis by its parent. Then when you check that it is facing the correct way rotate the arms localEularAngles.x up or down by a few degrees until the Dot Product is within a zone where you want it ? I could hash up this code for you if you like but this is just off the top of my head