axis independent turret lookat

Hi, i’m writing a turret lookat script, where the turret base rotates on it’s transform.up, to face the target, and the turret barrels, rotates on it’s transform.right.

But in my scenario, turret can be instanced with any orientation (on the ground or on the side of a wall, hanging from a ceiling, you got the idea.)

So instead of setting target.y to zero (common workaround to “lock” a rotation axis)
I have to make different calculations, always costraining the turret rotation, to it’s transform.up

function turret_follow()
{
	//..
	// transform is the turret base parent of cannon
	
	var base_to_target =  target_obj.transform.position - transform.position;
	var cannon_to_target = target_obj.transform.position - cannon.transform.position;
	
	//vectors from turret base, and turret cannon, pointing to the target
	
	var base_projection = Vector3.Project(target_obj.transform.position, transform.up);
	
	var position_relative_to_base = target_obj.transform.position-base_projection;
	
	var turret_base_rotation = new Quaternion.LookRotation(position_relative_to_base - transform.position, transform.up);
	var turret_cannon_rotation = new Quaternion.LookRotation(position_relative_to_base - transform.position, transform.up);

	transform.rotation = turret_base_rotation;
	cannon.transform.rotation = turret_cannon_rotation;
}

Besides I’m sure i’m doing a lot of unneeded calculations, the results, is very jittery: sometimes turrets start spinning madly on all axis, and even if they are not, checkinh their rotation in inspector, yields unpredictable values, where there should only be 0.

Any idea where the jitter comes from ? Suggestions on a more efficient way of “splitting” a direction into two separate vectors, for two separate rotations constrained on different axis ?

Thanks :slight_smile:

Here is another solution to your particular problem. It is a script I posted awhile back that was buried under a bunch of comments, so I’m posting here again. Note that it does use LookAt() and Quaternions. @semiessessi made comments on another of my posts earlier today that indicate issues with Quaternions and Quaternions.Slerp(). I have not had time to explore his comments, and he may very well be right. This code assumes the gun is a child of the turret. There is also some angle limiting code here that you don’t need.

  #pragma strict
    
    var target : Transform;
    var gun : Transform;
    var turretDegreesPerSecond : float = 45.0;
    var gunDegreesPerSecond : float = 45.0;
    
    var maxGunAngle = 45.0;
    
    private var qTurret : Quaternion;
    private var qGun : Quaternion;
    private var qGunStart : Quaternion;
    private var trans : Transform;

function Start() {
    trans = transform; 
    qGunStart = gun.transform.localRotation;
}

function Update () {
    var distanceToPlane = Vector3.Dot(trans.up, target.position - trans.position);
    var planePoint = target.position - trans.up * distanceToPlane;

    qTurret = Quaternion.LookRotation(planePoint - trans.position, transform.up);
    transform.rotation = Quaternion.RotateTowards(transform.rotation, qTurret, turretDegreesPerSecond * Time.deltaTime);

    var v3 = Vector3(0.0, distanceToPlane, (planePoint - transform.position).magnitude);
    qGun = Quaternion.LookRotation(v3);

    if (Quaternion.Angle(qGunStart, qGun) <= maxGunAngle)
       gun.localRotation = Quaternion.RotateTowards(gun.localRotation, qGun, gunDegreesPerSecond * Time.deltaTime);
    else
       Debug.Log("Target beyond gun range");
}

what you want to use here is ‘axis-angle’ rotation. something which unity thankfully has some support for in Quaternion.AngleAxis

if you combine this with a little geometry you can find the angle yourself using either a cross or dot product and the trig relations involving them (e.g. |a||b|cos(angle_between) = a.b). You need to use transform.forward and the cannon to target vector to find this angle (these would be a and b in your dot product) if you normalise the cannon to target vector then doing Mathf.Acos on the dot product should be accurate enough. Once you have this you then use your transform.up as the axis and you can plug it in with the angle to get a new quaternion representing the required rotation to get the turret to its new position.

However be aware that quaternions are a terrible representation to use for interpolation in most common cases (such as this) - if you want smooth motion or delays you will want to do the interpolation yourself by stomping the transform rotation every frame. Otherwise the turret will wobble a bit on the way round even though the initial and final orientations will be correct.