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.
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!
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
}
}
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/
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.