Fixing Quaternion rotation to local axis - turret problem.

Hi all,

Yet another turret problem. I’ve scoured the forums and can’t find anything specific enough to solve my problem, though I’m sure there may be something out there; I would still appreciate any help you can offer.

I have a tank. It has a turret, which is a child of the tank. The turret has a gun, which is a child of the turret.

I can rotate the turret to look at a point aimed at via the centre of the screen around the y-axis, but the problem is that when the tank is off the level - on a hill slope, for example - the turret is no longer rotating about the y-axis local to the tank, but the global y-axis still.

Here’s my code:

lookDirection = rayHit.point - Turret.transform.position;

lookDirection.y = 0;  / This locks the turret to horizontal rotation only, the gun will handle vertical rotation.

Turret.transform.rotation = Quaternion.Slerp(Turret.transform.rotation, lookDirection, Time.deltaTime * 2.0f);

Please please please please somebody help me. I’ve wasted a whole day trying to work out what the problem is, alternative methods of rotating the turret, scouring the forums and t’internet, but have got nowhere other than making my gf extremely unhappy with me. :frowning:

I had similar problems with the gun itself as well which I think are probably linked. I’m sure it’s something dead simple…

Oops - sorry, here’s the actual code I’m using to rotate the turret… the last line of the one above was a test and was bugged.

Turret.transform.forward = Vector3.Slerp(Turret.transform.forward, lookDirection, Time.deltaTime * turnSpeed);

Ah lookdirection is absolute and you’re trying to rotate the turret based on it’s local axis only.

Try using something like:

var lookvector=turrent.InverseTransformPoint(target.position);

Now if lookvector.x>0 you can turrent.Rotate(turret.rightrotationSpeedTime.deltaTime) (use -turrent.right if x<0…now I might have x> and x< flipped, you’ll know if the turret goes backwards!)

Probably you’ll also have to decide on a “good enough” rotation, or you’ll end up with the turret shaking back and forth.

Edit, here’s a script i had:

public var rotRate:float=10;
public var target:Transform;
public var tolerance:Vector3=new Vector3(0.05,0.05,0.05);
function Update () {
	var n:Vector3=transform.InverseTransformPoint(target.position);

	if (Mathf.Abs(n.x)>tolerance.x){
		transform.Rotate(transform.up*rotRate*Mathf.Sign(n.x)*Time.deltaTime);
	}

	
}

Hi quentinp, and thanks for replying. :slight_smile:

I’ve tried your code, with a couple of minor tweaks to get it to work with the existing code, but a couple of issues arise. Firstly, it’s aiming the turret at the origin of the terrain.transform, rather than the point at which we’re targetting the terrain, and secondly the turret is still rotating in the global y axis rather than relative to the body of the tank…

Here’s the full code I’m using:

ray = Camera.main.ScreenPointToRay(Vector3(gunCam.pixelWidth / 2 , gunCam.pixelHeight / 2 , 0));
        
    if (Physics.Raycast(ray, rayHit, Mathf.Infinity, layerMask)){
        
        lookDirection = rayHit.point - Turret.transform.position;

        var gunTarget = rayHit.point;
        
        gunTarget.x = Gun.transform.position.x;
        gunTarget.z = Gun.transform.position.z;
        gunLookDirection = gunTarget - Gun.transform.position;
        
        lookDirection.y = 0;
        Turret.transform.forward = Vector3.Slerp(Turret.transform.forward, lookDirection, Time.deltaTime * turnSpeed);
       
      }

That’s my current code for turning the turret. Works perfectly on the flat (all the time the global y-axis matches the tank body’s y-axis) but as soon as I drive to a slope, the turret rotates in the global y-axis and ignores the orientation of the tank body to which it’s attached.

Otherwise, it’s targetting the correct spots just fine (albeit ignoring the elevation of the gun barrel, because I haven’t coded that in yet until I fix this problem.)

I’m considering buying a tank package to get this problem solved so I can see how someone else has done it - I just find it a little odd that with all the questions in the forums re: turrets, no-one has made a freely available script to do it.

There is a working turret for a tank and a jeep mounted machine gun floating around. It’s within the old vehicle example project, the one that also contained the boat, helicopter and airplane. It’s long depreciated, but you should be able to find it with a google search of the forums as an attachment.

Quietus - thanks for that, should have thought of that myself, especially as I have a copy sitting on my hard drive somewhere… :facepalm:

quentinp - thanks very much for the code, I’ve got it working fine now! Here’s the modified code below:

    ray = Camera.main.ScreenPointToRay(Vector3(gunCam.pixelWidth / 2 , gunCam.pixelHeight / 2 , 0));
        
    if (Physics.Raycast(ray, rayHit, Mathf.Infinity, layerMask)){

        // Aim turret
        n = transform.InverseTransformPoint(rayHit.point);
        if (Mathf.Abs(n.x)>tolerance.x){
            Turret.transform.Rotate(Vector3.up * turnSpeed * 10 * Mathf.Sign(n.x) * Time.deltaTime);
        }
        // Aim gun
        if (Mathf.Abs(n.y)>tolerance.x){
            Gun.transform.Rotate(Vector3.right * turnSpeed * Mathf.Sign(n.y) * Time.deltaTime);
        }

The only problem I’ve got now is getting the gun elevation to work, as per the last bit of code - it just seems to want to rotate constantly in the x-axis… (P.S. Don’t worry about tolerance.x not being tolerance.y, the values are all the same anyway.)

Quietus - I will revisit the vehicle project and see what I can find as well to help fix the gun bug…

Thanks!

I think you may need to recalculate n using the gun transform!

Quietus quentinp - thanks for all the help. :smile:

I have found a very clean solution in the scripting used in the vehicle example project. Wish I’d thought of trying that a few days ago, would have saved some grey hairs! :wink:

I’m typing this in my phone but I had this exact problem. Search under my name for quatrinion.rotatetowards innacuriate when not level. I think that was the title topic. The answer is in the topic. When I get home I can post the working code if you still need it.