Turret tutorial without using Quaternion.Slerp

In the tutorials I’ve studied that demonstrate how to make an object rotate over time to point at the player, they all use Quaternion.Slerp. For example: http://answers.unity3d.com/questions/18057/i-need-a-turret-script
I’ve been trying to work out a way to accomplish a similar result but using rigidbody.AddTorque instead, mainly as an exercise to learn more about the physics engine, and rotations in general. It seems to me that with two objects controling themselves within the physics engine, using Slerp is a cheat. The player’s ship has a certain fixed turning capability, and when using Slerp, I’m not limiting the enemy AI to the same restrictions. In other words, rigidbody.AddTorque(transform.TransformDirection(0,.4,0)); has no equivalent.

The heart of the problem is determining the current rotation of the enemy ship, and the rotation the enemy ship must rotate to in order to take aim on the player, then somehow deriving the correct torque to apply to get there. The latter part, of course, is where I get foggy, not to mention switching between euler and quaternion as I try to figure this out. i wish I could say i’m well versed in using Quaternion.Angle, AngleAxis, Dot, etc, but I’m still experimenting (a lot) with these… and alternately getting confused then lucid at any given moment.

Thanks for any thoughts on this, even if it’s to tell me to give it up!

I have a relevant problem here as well.

I have an object whose tilt is controlled by the player using the x (roll) and z (pitch) axes of a joystick. The object is subject to physics, so I don’t want to use Transform or Slerp etc to change its angle of tilt. It can also yaw about its y axis, which makes things confusing.

My aim is thus:

Get a target tilt angle from the joystick input (e.g. where maximum deflection = 90 degrees tilt and zero deflection = upright).
Apply a torque with damping to make the object tilt towards the target angle.

I think the middle steps are “Calculate the current tilt angle.
Find the difference between the current and target tilt angles.
Base the torque force on this.”

I’ll explain what I currently have a bit later (it might help you, Paulygon), but now I must go to work!

Hi crunchysaviour, I was getting lonely with just these crickets! From other posts I’ve read I get the feeling this is hard to do, which might explain the lack of response. I haven’t given up on it yet, and will post as I progress, even if it’s the report of failure and abject depression! :wink:

Thanks for chiming in.

edit: http://forum.unity3d.com/threads/31420-Left-Right-test-function
I found this thread a few weeks back, and it just occured to me, that if it’s possible to determine left or right, then it must be possible to figure out above or below. For my problem, that’s all I would need, then I could just apply torque twice, once for pitch, once for yaw.

The thread I posted earlier lead to the solution. Thanks HigherScriptingAuthority!

var target: Transform;
var theLateral : String = "centered";
var theVertical : String = "centered";
var accuracy : float;


function FixedUpdate(){
	if(accuracy > 5){
		if(theLateral == "left"){rigidbody.AddTorque (transform.TransformDirection(0, -.4, 0.0));}
		if(theLateral == "right"){rigidbody.AddTorque (transform.TransformDirection(0, .4, 0.0));}
		if(theVertical == "above"){rigidbody.AddTorque (transform.TransformDirection(-.4, 0, 0.0));}
		if(theVertical == "below"){rigidbody.AddTorque (transform.TransformDirection(.4, 0, 0.0));}
	}
}


function Update () {
	var heading: Vector3 = target.position - transform.position;
	theLateral = LatDir(transform.forward, heading, transform.up);
	theVertical = VertDir(transform.forward, heading, transform.right);
	aimedRotation = Quaternion.LookRotation(target.position - transform.position);
	accuracy = Quaternion.Angle(transform.rotation, aimedRotation);
}


function LatDir(fwd: Vector3, targetDir: Vector3, up: Vector3) {
	var perp: Vector3 = Vector3.Cross(fwd, targetDir);
	var dir: float = Vector3.Dot(perp, up);
	
	if (dir > 0.0) {
		return "right";
	} else if (dir < 0.0) {
		return "left";
	} else {
		return "centered";
	}
}


function VertDir(fwd: Vector3, targetDir: Vector3, up: Vector3) {
	var perp: Vector3 = Vector3.Cross(fwd, targetDir);
	var dir: float = Vector3.Dot(perp, up);
	
	if (dir > 0.0) {
		return "below";
	} else if (dir < 0.0) {
		return "above";
	} else {
		return "centered";
	}
}

Superb, thanks for this, Paulygon. I have now transferred some of this code to my game and I can now make the player object tilt towards a desired angle by using torques. Imagine a tilting marble maze controlled by a joystick; it’s a bit like that (but without mazes or marbles - all will be revealed!).

I don’t know why the dot product gives the amount of rotation in one dimension only, but it does. It’s very elegant.

I notice that LatDir and VertDir are effectively the same function; perhaps you could simplify this.

You could also use the size of the variable “dir” to give a larger torque at higher amounts of rotation.

For torque, I use AddRelativeTorque instead of the longer way that you suggest.

I’m now trying to make this easily controllable; I’m encountering problems with tumbling and oscillations; I’ll play with damping and post any relevant stuff I find here.

A follow up FYI, the way this is calculating the accuracy variable:
accuracy = Quaternion.Angle(transform.rotation, aimedRotation);
works OK except that roll is a part of that calculation. In other words, the object might be pointing straight at the intended target, but if it’s upside down in relation to that target, it makes the accuracy variable ineffective. I haven’t found a way around that yet, or another way to calculate “am I pointing at the target without taking roll into account.”

And thanks for the feedback. All good suggestions!

Try just using a Physics.Linecast…

If the return hit is the “target” you are pointed at the target…

OH! of course. Although I REALLY liked the “fuzzy” aim with a value that I can test against, As in, if (accuracy < 5). Still, casting would do the trick.

Just use both… except that will cause extra overhead