- Home /
Need help with turret targeting system (rotation issues)
I am trying to create a turret prefab that the player can drop down into the playing field. The turret needs to rotate left and right until an enemy is within firing range and then I want to head of the turret to aim towards the closest enemy.
I've tried the LookAt() function and, while this works, the result is extremely unrealistic. If two enemies are in firing range and the first one either leaves or dies, the head of the turret "pops" to the next target. I would like the turret head to animate over time to match up with the next target.
My solution ended up being this; I have an "invisible" cube that sits with the turret head and the cube is using LookAt() to face the target. The head of the turret is going to slerp until it's rotation is equal to the underlying cube. This way, when the invisible cube "snaps" to the next target, it will take the head of the turret some time until it aligns with the next enemy.
My issue ends up being that I am struggling with Quaternions. It's the one concept within unity that is not clicking for me. I just want both the invisible cube, as well as the turret head, to rotate on one axis (LookAt() will rotate up and down when I only want to rotate left and right) I just wanted to reach out for help and see if anyone had any good ideas on how to create a behavior similar to LookAt() that only looks left and right (not up and down as well). Also, if there is a more elegant way to solve my issue that doesn't involve an invisible cube.. please do share. I'd love to learn more about all of this.
Answer by aldonaletto · Dec 20, 2012 at 09:14 AM
The "invisible cube" solution isn't as dumb as you may think! Auxiliary invisible objects often help to solve complicated problems, but usually they are just empty game objects instead of Unity cubes - cubes have colliders, thus are taken into account when checking collisions and waste CPU time).
But this case can be solved without auxiliary objects: get the current enemy direction, zero its Y component to make the direction strictly horizontal (as @CodeMasterMike said), calculate the necessary rotation with Quaternion.LookRotation and Slerp to it in Update:
var currentEnemy: Transform;
var turnSpeed: float = 5.0;
function Update(){
if (currentEnemy){ // enemy alive and at sight: aim at him!
var dir: Vector3 = currentEnemy.position - transform.position; // find direction
dir.y = 0; // keep only the horizontal direction
var rot: Quaternion = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Slerp(transform.rotation, rot, turnSpeed * Time.deltaTime);
// you may shoot here
}
else {
// no enemy or enemy dead: find the nearest
// victim and assign it to currentEnemy
}
}
Thank you for helping out! I had tried similar methods earlier but was not getting the desired result. I was able to deter$$anonymous$$e why my code was yielding unsatisfactory results. The models that my artist created imported properly, but they were somehow rotated 90 degrees on the x axis (both of em) and manipulated to appear as if they were naturally sitting on top of each other with no rotation applied. So when the turret head has 0 for x, y, and z of rotation, it points directly at the ground, so the code that you've been so kind to share was causing the turret to rotate towards the enemy, but face the ground.
I had to re-import the model's components from 3DS with the right rotation in order to see the correct effect. Thank you so much for you help though! I did gather a better understanding of rotation and quaternions through this whole issue. It is awesome that both of you guys are willing to come help out your community... keep up the good spirit, and keep makin' good games!
This is a common problem with imported models - they are rotated or have two axes swapped, pointing to the wrong direction. Fixing this at the original 3D editor is the best solution, but many times it's easier to child the model to an empty object, adjust its local rotation and reference the empty object in the scripts ins$$anonymous$$d of the model. Anyway, there are occasions when you want some object to point its Y or X axis to some direction ins$$anonymous$$d of the Z axis - in these cases, use Quaternion.FromToRotation ins$$anonymous$$d of Quaternion.LookRotation to get the desired rotation - for instance:
var rot = Quaternion.FromToRotation(Vector3.up, enemyDirection);
This rotation aligns the object's original up direction to the enemy.
Answer by CodeMasterMike · Dec 20, 2012 at 06:34 AM
Have you tried to use the Vector3.RotateTowards function when rotating your turret? Maybe it will work better.
If you still have the issue, where the turret rotates up and down as well as to the sides, make sure the objects (or at least the vectors you use as inparameter) have the same y-axis (or which axis you use as up). If the up-axis are different then there is bound to be a up/down rotation as well.
One option is to manually set the y-axis yourself before the check:
// Get the correct vector data first, then manually set the y-axis to something good.
currentVector.y = 0.0f;
targetVector.y = 0.0f;
currentTurret.Transform.Rotation = Vector3.RotateTowards(currentVector, targetVector, 1.0f, 1.0f);
Link to RotateTowards() function.
Good luck!
Thank you for taking the time to swing by and help. You have a very good point. I will elaborate on what was going on in the answer to the question (@AldoNaletto's answer). I do appreciate you reaching out to help the community!
The basic idea is correct, but you should assign Vector3.RotateTowards to transform.forward ins$$anonymous$$d of transform.rotation - this RotateTowards version returns a Vector3. There's also the Quaternion version of RotateTowards, but it needs quaternions ins$$anonymous$$d of vectors as arguments, what could complicate things a little.
Answer by cgcookie · Dec 21, 2012 at 12:56 AM
I would recommend a lerp between the two rotations. We cover some javascript behind this in our turret defense series: http://cgcookie.com/unity/2012/05/22/unity-tower-defense-tutorial-part-01-autoturret/
Answer by HarleysZoonBox · Oct 11, 2013 at 12:25 PM
Even Though this is not an answer this solution should include one more thing, and that is a way to make the turret sleep if the player or enemy is not in range.
If it is not a answer, please, remove it and add as commentary.