- Home /
Project an Angle onto a Plane (Turret Rotation)
Flipbookee has a mastery of concise english, so I am copying his explanation almost verbatim:
I have a tank on an arbitrarily inclined surface. I know the direction in world space (euler angles) where I want it to shoot. I need to know the x and y rotation (also euler angles) in the tank's local space where the turret needs to aim in order to shoot at that desired direction.
Any insight? Thank you very much!
I realize I may have made this sound more complicated that it is; all I need is a way to deter$$anonymous$$e the angle an arbitrary vector makes on an arbitrary plane.
This way, I use the vector of the world-space azimuth/elevation, and then compare it to two planes: the plane that the turret pans on, and the plane the turret tilts on.
"arbitrary vector makes on an arbitrary plane" it's possible you're just looking for the dot operation, or something like this
http://docs.unity3d.com/Documentation/ScriptReference/Vector3.Angle.html
If I understood correctly you have a tank on arbitrarily inclined surface and you know the direction in world space coordinates where you want to shoot, so you need to know the azimuth and elevation in local tank's space where tank's turret needs to rotate in order to shoot at that desired direction. Is that correct?
Updated the explanation using Flipbookee's simplification. Should make a whole lot more sense now, haha. Thanks!
Answer by aldonaletto · Sep 13, 2012 at 06:29 AM
If you just want to rotate the turret and the gun around their local Y and X axes in order to aim to some target point, there's a much easier alternative: project the target position into the turret's local horizontal plane and use LookAt to rotate the turret to this point, then simply use LookAt(target) to control the gun direction (does exactly the same effect that a gun childed to the turret and rotating only around its local X axis).
This approach requires that you child the turret and the gun to an intermediate empty object - the Base object - which in turn is childed to the tank, like below:
Hierarchy:
Tank <- tank is the parent of the Base empty object Base <- control script goes here Turret <- turret is childed to Base Gun <- gun is childed to Base
The script gets references to Gun and Turret, and control both:
var target: Transform;
var damping: float = 5.0;
private var turret: Transform;
private var gun: Transform;
private var trf: Transform;
function Start () { // gun and turret are childed to Base:
trf = transform;
gun = trf.Find("Gun");
turret = trf.Find("Turret");
}
var aimPos: Vector3; // position the gun must aim at
var elevationAngle: float = 30; // fixed elevation angle
var projectileVel: Vector3; // velocity needed to reach the target
function AimAtTarget(euler: Vector3, distance: float){
// get vector Base->target:
var dir = Quaternion.Euler(euler) * Vector3.forward * distance;
var h = dir.y; // get height difference
dir.y = 0; // retain only the horizontal direction
var dist = dir.magnitude ; // get horizontal distance
var a = elevationAngle * Mathf.Deg2Rad; // convert elevation angle to radians
dir.y = dist * Mathf.Tan(a); // set dir to the elevation angle
dist += h / Mathf.Tan(a); // correct for small height differences
// calculate the velocity magnitude
var vel = Mathf.Sqrt(dist * Physics.gravity.magnitude / Mathf.Sin(2 * a));
projectileVel = vel * dir.normalized; // save projectile velocity vector
aimPos = gun.position + dir; // save position to aim at
}
function Update () {
// convert target position to local space:
var pos = trf.InverseTransformPoint(aimPos);
pos.y = 0; // project it in the horizontal plane
// convert pos back to world space:
pos = trf.TransformPoint(pos);
// find the desired turret rotation and Slerp to it:
var rot = Quaternion.LookRotation(pos-trf.position, trf.up);
turret.rotation = Quaternion.Slerp(turret.rotation, rot, damping*Time.deltaTime);
// just Slerp the gun to the target:
rot = Quaternion.LookRotation(aimPos-gun.position, trf.up);
gun.rotation = Quaternion.Slerp(gun.rotation, rot, damping*Time.deltaTime);
}
// how to use:
var projPrefab: Rigidbody; // projectile prefab
function Shoot(eulerAngles: Vector3, distance){
AimAtTarget(eulerAngles, distance);
yield WaitForSeconds(3); // give some time for gun positioning
var proj: Rigidbody = Instantiate(projPrefab, gun.position, gun.rotation);
proj.velocity = projectileVel; // shoot with the calculated velocity
}
This code actually shoots the projectile according to the calculated trajectory - the turret/gun orientation is just a visual aid. By the way, I'm assuming here that the Euler angles define the target direction based on the Gun object. If they are relative to the tank position, replace gun by the transform.parent (assuming Base is childed to the tank) in the Instantiate instruction.
NOTE: You must use some trick to avoid collisions between the projectile and the tank, turret or gun. An alternative is to create two new layers, Tank and Projectile, disable collisions between them in the Physics Manager and move the tank/turret/gun and projectile to the appropriate layers. Another alternative: make the projectile collider be a trigger during a few cycles, so that it can ignore collisions until it's at some distance from the tank - like this:
...
var proj: Rigidbody = Instantiate(projPrefab, gun.position, gun.rotation);
proj.collider.isTrigger = true; // projectile is a trigger, initially
proj.velocity = projectileVel; // shoot with the calculated velocity
yield WaitForSeconds(0.2); // let it reach a safe distance...
proj.collider.isTrigger = false; // and make it a regular collider again
}
Thank you for the well written-out reply; it's a bit trickier in that the turret must aim upwards in order to form a trajectory to hit the target point. I am working on drawing the situation out graphically, but it is proving difficult! I will definitely use your script as reference, but allow me to explain the problem in more detail... again, working on it now, sit tight!
I believe this is actually almost exactly what I need; but ins$$anonymous$$d of a target point, I have a target direction in euler angles. Could this be revised to use InverseTransformDirection? I've been trying to do it, but I'm having a bit of trouble getting the different vectors to agree with each other.
Thanks for the help!
If the Euler angles are relative to the world axes, you can calculate the target position like this (Base script):
// calculate the direction vector: var dir = Quaternion.Euler(angX, angY, angZ) * Vector3.forward; // add to Base position to get the target point: var targetPoint = transform.position + dir;This code just calculates a point in the line Base->target, which is enough to aim the turret/gun in the target direction with the script above.
If you want to compensate for gravity, however, I suggest a different approach: calculate the precise target position, use my function BallisticVel to calculate the necessary velocity and direction, then calculate the ai$$anonymous$$g point with the script above. I'll edit my answer to include this.
I've actually figured out a simple solution; I've created a dummy Transform childed to the turret. I set it's global y rotation to the azimuth and its global x rotation to the elevation, and then read its /local/ y and x rotations for the turret's yaw and pitch, respectively.
I'm studying the code you've written to possibly remove the necessity for the extra Transform, but for now it is a solution. Thanks for helping me find it! :]
This answer is over 4 years old. That means this post has gone though an Q&A engine port as well as several server moves. In the past it was properly marked as code. It still has the <pre>
html tags. I'll fix the code highlighting.
Answer by Meater6 · Sep 13, 2012 at 05:02 AM
I can't really visualize your problem. Can you make like a set of isometric pictures?
I honestly didn't know the definition of "azimuth" and upon looking it up, it does not sound like the "elevation". The terms, without pictures or a perhaps a better description, can be a source of confusion. If you want to get more answers, I suggest editing those parts.
Now, it seems that you want to determine which way the turret is facing (the "pan"). This may sound dum, since I can't really understand the problem, can't you have the turret as a separate game object attached to the top of the tank, and use its euler Y value?
Pictures are an excellent idea... I will simplify the description and whip up a picture. Sorry about the confusion; I'm using aircraft terms, camera terms, and field artillery terms all in the same description! I'll trim it down.
I am working on a graphic to explain the situation... it's proving very difficult--the situation is much harder to draw out than I had ever imagined, haha. Hang tight, I will edit the post with the graphic once it's done. :] Thanks fellas!
Your answer
Follow this Question
Related Questions
Make two sets of objects have same angle between them 2 Answers
3D Turret Rotation 0 Answers
instantiate works on one but not other object 0 Answers
Making a Turret Have a Circle Sector Range 1 Answer
Limiting Local Turret Rotation 0 Answers