Quaternions and Turrets and Tops, Oh My!

Good afternoon, everyone!

I’m trying to get my head wrapped around Quaternions and local object rotations, and I seem to be running into a bit of trouble getting a turret to work via the LookRotation and LookAt functions.

Currently, I’m just using primitives in Unity. I’m trying to stay away from the modeling end of things until I have the rules of combat worked out. Thus, it is just cubes and cylinders right now. The turret root is an empty object that is fixed to the ship and will turn and rotate with her. The turret root is essentially an anchor that will hold all the information regarding turret rotation and elevation limits. Attached to that is a box that serves as the turret ‘base’ that will rotate between those limits according to the heading of a given target object.

I’ve been able to establish a target for my turret and, using Vector3.Angles, I’ve been able to return seperate angles for the turret base and the turret root. However, these angle returns are based on the overall angle in 3d space. I’m unsure how to return seperate rotations for the local x y and z rotations. This is where I run into the problem, as I think I would need these to determine how the turret should turn and take aim.

You see, I’ve been able to use the following code to make the base object look toward it, but that base object also rotates up to to so. This is particularly troublesome when only the barrels are meant to handle the elevation. It also looks horrible!

I have two lines that I’ve been fiddling with and neither one works particularly well. The first Quaternion.Slerp uses the local rotation, but it tends to go a little wierd when the TargetRotation.z and .x = 0 lines are active. When I uncomment the baseTargetRotation.z and .x = 0 lines, it does lock the turret to the horizontal axis - until you rotate the root object. At that time, it tends to start spinning in a direction opposite to the x or z value that has been entered. Very wierd. It must be why we don’t change the .x or .z values unless we know what we’re doing. Very clearly, I don’t. :slight_smile:

The second Slerp line is set to use the standard rotation, rather than localRotation. My understanding is that this is related to the world coordinates. I don’t get a spinning top anymore, but uncommenting the .z and .x = 0 lines does cause it to slowly level out according to the world coordinates - even if the base object is rotated over on her side. This makes sense, since that’s the world y axis, but it still isn’t what I’m looking for. :slight_smile:

Anyhow, without further delay, here is the code in the present format. It’s applied to the turret root object, to which the base object is a child, and where the turret base and target objects are then assigned. If anyone has any suggestions, or perhaps an easier way to go about making this turret move, please let me know. All this talk of Slerps is making me thirsty, though. I’m off to snag a slurpee!

using UnityEngine;
using System.Collections;

public class LargeTurret : MonoBehaviour {
	public Transform target;  //target object
	public GameObject turretBase; //turret base object.  Will handle horizontal angles for aim.
	
	//local angles for the turret root.  Used to determine how far a turret may turn and elevate compared to the root object.
	public int turretMaxAngle = 135;
	public int turretMinAngle = -135;
	public int turretMaxElevation = 50;
	public int turretMinElevation = -10;
	
	//how fast the turret may turn.
	public float turretDegreesPerSecond = 0.02f;
	
	
	private Transform myTransform;  //this is the turret root object
	private Transform baseTransform;  //this is the turret base object

	void Awake(){
		myTransform = transform;
		baseTransform = turretBase.transform;
	}
	
	void Update () {
		//we will check distances and max/min angles after the rotation behaves itself.
		
		//Initial target rotation data for the turret base object
		Quaternion baseTargetRotation = Quaternion.LookRotation(target.position - baseTransform.position);
		
		//zero base object on z and x rotations for the above variable.
		//baseTargetRotation.x = 0;
		//baseTargetRotation.z = 0;
		
		//local rotation line.  Causes spinning when parent object rotates out of 0,0,0 angle.
		//baseTransform.rotation = Quaternion.Slerp(baseTransform.localRotation, baseTargetRotation, turretDegreesPerSecond * Time.deltaTime);
		
		//world rotation line.  Causes turret to become level with world y axis, but at least the turret doesn't turn into a top!
		baseTransform.rotation = Quaternion.Slerp(baseTransform.rotation, baseTargetRotation, turretDegreesPerSecond * Time.deltaTime);
		
		//Turret root and base rotation information.  Angles to target.
		Vector3 targetDir = target.position - transform.position;  //turret root target direction
		Vector3 baseTargetDir = target.position - baseTransform.position;  //turret base target direction
		Vector3 baseForward = baseTransform.forward;  //turret base facing
		Vector3 forward = transform.forward;  //turret root facing
		
		//DEBUG code.  To be removed later.
		Debug.DrawLine(target.transform.position, myTransform.position, Color.yellow);
		Debug.Log("Target Angle from Bearing: " + Vector3.Angle(targetDir, forward));
		Debug.Log("Target Angle from Base: " + Vector3.Angle(baseTargetDir, baseForward));		
	}
	
	
}

Good evening, folks!

Well, the code has changed, somewhat. I’ve added in another object to control the elevation of the turret barrels and a dummy object to stand in front of them as the aiming point for visual reference during the debug stage. I took out the Quaternion Slerp code for now, in lieu of a steady rotation that seems to work without any issues. I can rotate the parent objects and it does not interfere with the proper attached rotation that one would see in a mounted gun turret. Now comes the problem of determining each of the X and Y rotations that need to be made in order to line up the guns on a given target. In this, I’m still rather confused.

The rotation was achieved by applying a Rotate method during the update function. So far, these are broken down into the RotateRight, RotateLeft, PitchUp and PitchDown functions. These would be called once there are differences between the target rotation and their own position. For now though, I am still at a loss to determine what those differences are according to each object’s relative axis. Y being for the turret base,and X for the gun barrels. I will continue to work on it tonight.

Here is the current code as it stands. If anyone knows how I can determine the euler angles between the turret and the target, I would be very eager to know. In the meantime, I will continue to hack through the code and attempt to find the solution on a trial-and-error basis.

Happy coding, everyone!

using UnityEngine;
using System.Collections;

public class LargeTurret : MonoBehaviour {
	public Transform target;  //target object
	public GameObject turretBase; //turret base object.  Will handle horizontal angles for aim.
	public GameObject turretElevation;
	public GameObject gunLead;
	
	//local angles for the turret root.  Used to determine how far a turret may turn and elevate compared to the root object.
	//Not used at the present time.
	public int turretMaxAngle = 135;
	public int turretMinAngle = -135;
	public int turretMaxElevation = 50;
	public int turretMinElevation = -10;
	
	//how fast the turret may turn in degrees per second.
	public float turretDegreesPerSecond = 30.0f;
	public float turretElevationDegreesPerSecond = 30.0f;
	
	
	private Transform myTransform;  //this is the turret root object
	private Transform baseTransform;  //this is the turret base object
	private Transform elevationTransform;

	void Awake(){
		myTransform = transform; //the current turret object (empty gameobject)
		baseTransform = turretBase.transform; //the turret base object
		elevationTransform = turretElevation.transform; //the turret barrel elevation object (empty gameobject).  Barrels are attached to this.
	}
	
	void Update () { //called every frame.
		//we will check distances and max/min angles after the rotation behaves itself.

		//Turret root, base and elevation rotation information.  Determines Angles to target.  Used for debugging at this time.
		Vector3 targetDir = target.position - transform.position;  //turret root target direction
		Vector3 baseTargetDir = target.position - baseTransform.position;  //turret base target direction
		Vector3 baseForward = baseTransform.forward;  //turret base facing
		Vector3 turretElevation = elevationTransform.forward;
		Vector3 forward = transform.forward;  //turret root facing
		
		//DEBUG code.  To be removed later.
		Debug.DrawLine(target.transform.position, myTransform.position, Color.yellow);
		Debug.DrawLine(elevationTransform.position, gunLead.transform.position, Color.red);
		Debug.Log("Target Angle from Bearing: " + Vector3.Angle(targetDir, forward));
		Debug.Log("Target Angle from Base: " + Vector3.Angle(baseTargetDir, baseForward));
		Debug.Log("Target Angle from Barrels: " + Vector3.Angle(targetDir, turretElevation));
		
		//Autorotation code.  Uses functions below to establish the rate of turn.  At present, the limits are not enforced.
		//RotateLeft();
		//PitchUp();
	}
	
	//Custom functions
	private void RotateRight(){
		baseTransform.Rotate(Vector3.up * turretDegreesPerSecond*Time.deltaTime);
	}
	private void RotateLeft(){
		baseTransform.Rotate(Vector3.up * -turretDegreesPerSecond*Time.deltaTime);
	}
	private void PitchUp(){
		elevationTransform.Rotate(Vector3.left * turretElevationDegreesPerSecond * Time.deltaTime);
	}
	private void PitchDown(){
		elevationTransform.Rotate(Vector3.left * -turretElevationDegreesPerSecond * Time.deltaTime);
	}	
}

Good afternoon, everyone!

My brain is currently breaking. I’m having issues converting between Quaternion and Vector3 values. The code above results in debug.log rotations between 360-180 (from forward to rear-left) and 0-180 (from forward to rear-right), yet the numbers in the rotation boxes in the editor can be -5, -10 degrees, things like that. Is there a way to change these localEulerAngles to handle a number that is a little more consistant with the numbers that are in the toolset? This would make it a fair bit easier to work with the rotation angle numbers. For example, -135 degrees to +135. I’m still trying to get my head wrapped around how to convert between Vector3 values and Quaternions, how they relate and play off of one another. Does anyone have any suggestions on reading material for this, and / or perhaps a tutorial? I’ll continue to dig at work tonight, but any assistance would be very much appreciated.

Until then, fellow coders!

hi mate,

first, tldr (too long didn’t read)

you don’t need to know the theory behind quaternions to be able to use them effectively.
Unity has built in function to go from Euler Angles to quaternions and vice a versa:
Quaternion.Euler
Quaternion.eulerAngles

if you insist though, you can read about quaternions on wikipedia:
http://en.wikipedia.org/wiki/Quaternion
http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation

Awesome, ivkoni. Thank you very much for the function listings! I took a look through the wiki links you gave, along with their description of finite-dimensional division rings… and yeah. I’m very glad we don’t need to know about Quaternions to use them. It took this long to scoop up my exploded brain and put it back where it belonged. I will try out those functions after i get some sleep and post the results in a few hours.

Thanks again! I’ll throw up some more questions when I have 'em! I think we both know I will.

Until then,
-Lance

Hmm… okay, I’m still missing something, I think. I’m essentially trying to use the Quaternion.LookRotation function, but I am trying to restrict the axis of movement to only the local y axis of the turret base object. Elevation (x) would be determined seperately for the barrels, but I’ll worry about that once the turret base is behaving itself.

your text is really to long to read, but i’m working on a turret with parts moving separately.
base part is moving and rotating independently from top part. Top part has barrel and is looking at it’s target.

so i have this script attached ONLY to a top part.
It handle interception point calculation as well, so it’s shooting ahead a target.
hope you’ll find something useful in it

// Target to look at
var target : Transform;
//Slowdown for rotation
static var damp = 6;
//max distance from turret to target
static var attackRange = 25.0;
//velocity vector of target with it's magnitude and direction
var pVelocity : Vector3;
//target
var player : GameObject;
    player =  GameObject.Find("Player");

//coeficient based on exponential function to provide bulletSpeed - targetDistance dependency
var coef;
//distance to target
var dist;

function Start () {
    // Auto setup player as target through tags
    if (target == null  GameObject.FindWithTag("Player"))
        target = GameObject.FindWithTag("Player").transform;
}

function Update () {
//calculate distance to target
    dist = Vector3.Distance(target.position, transform.position);
//calculate coeficient
    coef = 3*Mathf.Exp(-0.05*dist)+0.4;
//get target's speed and it's direction
    pVelocity = player.rigidbody.velocity;
    
    if (dist < attackRange){
    

    
        if(target){
            //var rotate - angle to interception point
            var rotate = Quaternion.LookRotation((target.position + (pVelocity/coef))- transform.position);
            //transform.rotation - Direct Line of Sight interpolated from current Line Of Sight and interception point
            transform.rotation = Quaternion.Slerp(transform.rotation, rotate, Time.deltaTime * damp);
        }    
    }
    
}