- Home /
local space and rotation issues
So i have a "ship", with "turrets" that are parented... I have a script that identifies close targets and sets them, the turrets then use these targets to coordinates to fire... problem comes in when they do not rotate correctly.
I could only guess that its... a) because i have no idea what axis "LookAt" actually is aligning to b) because I most likely have "local space - world space" issues
public virtual void Fire(){
Ray raycheck = new Ray(transform.position, target.transform.position);
if (!ship.GetComponent<Renderer>().bounds.IntersectRay(raycheck)){
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(target.transform.position,Vector3.right), 0.7f * Time.deltaTime);
if (!IsInvoking("AttackHandler"))
Invoke("AttackHandler", 5);
}
}
public virtual void AttackHandler(){
Rigidbody Shot;
Shot = Instantiate (Ammo,mount.transform.position,Quaternion.LookRotation(target.transform.position)) as Rigidbody;
Shot.velocity = Shot.transform.TransformDirection(Vector3.forward)*200 * Time.deltaTime;
}
Quite the amount to look throught I know. on a slightly different note. what is the best way to rotate towards a target with a certain speed?
Answer by aldonaletto · Apr 02, 2012 at 12:31 PM
The main problem here is that you're confusing points and vectors. Although both are stored in Vector3 structures, the meanings are different. A point is a location in the 3D space, while a vector represents a direction - think of it as an "arrow" from (0,0,0) to the vector coordinates: Vector3.up, for instance, is (0,1,0), what means a vertical arrow (from y=0 to y=1).
But there are other problems too: you're using Quaternion.Slerp to turn gradually to the target, but this will only work if you call the Fire function each Update (or in a coroutine) - Quaternion.Slerp rotates a little each time it's called, thus using it only once gives no noticeable effect. Another weird thing: why are you checking intersection between the ray and renderer.bounds? To avoid shooting "across" your own ship? bounds is an AABB, Axis Aligned Bounding Box, and grows or shrinks when you rotate your ship.
But lets get back to the basics: you should calculate a dir vector in the direction ship->target, what you can do just subtracting ship.position from target.position. When shooting, start a coroutine that will turn gradually to the target during duration seconds and then shoot. To avoid multiple coroutines running in the same turret, a shooting flag is used:
public virtual void Fire(){ Vector3 dir = target.transform.position-transform.position; Ray raycheck = new Ray(transform.position, dir); if (!ship.GetComponent< Renderer>().bounds.IntersectRay(raycheck)){ // call Shoot only when previous shot has finished if (!shooting) StartCoroutine(Shoot(target.transform)); } }
public float duration = 0.5f; // time in seconds to turn to target bool shooting = false; // flag to avoid multiple Shoot calls
IEnumerator Shoot(Transform tgt){ shooting = true; // Shoot is running! Quaternion rot0 = transform.rotation; // save initial rotation float t = 0.0f; while (t < 1.0f){ // turn to the target during duration seconds t += Time.deltaTime/duration; // advance variable t Vector3 dir = tgt.position-transform.position; // update the direction each loop // sets rotation to an intermediate direction proportional to t: transform.rotation = Quaternion.Slerp (rot0, Quaternion.LookRotation(dir), t); yield return 0; // let Unity breath till the next frame } Rigidbody Shot; Shot = Instantiate(Ammo, mount.transform.position, transform.rotation) as Rigidbody; Shot.velocity = dir * 20; // shoot in the target direction shooting = false; // Shoot has finished } NOTE: This is untested C# code, thus may contain some errors.
I probably shouldn't have sent so much mucked up code, as i was using a slightly different set of functions before screwing with it to get it working... tho i never once thought to calculate my own vector using 2 positions, that was very helpful.
I was using Quaternion.RotateTowards so that i could get a constant speed and variable time, I just had my speed set so low it never really pointed at its target...
tho i don't totally understand why the coroutine is nessesary as you can block a regular function the same way
A coroutine works this way: StartCoroutine creates an internal object with the coroutine code attached, and starts executing this code. When a yield instruction is found in the code, Unity stops executing it and returns to the caller routine. In the next frame, after executing all Updates, Unity resumes automatically the coroutine right after the yield, and does it over and over until the coroutine finishes, when the internal object is destroyed.
In other words, coroutines are the ideal way to execute loops without freezing Unity, because one iteration is executed per frame. In this case, Fire starts the coroutine Shoot and returns - Unity does the job automatically for you.