- Home /
Midair Strafing
I'm making a flying game where the ship has hovering style controls. I want to have it be able to strafe left and right while hovering. The problem is, if I strafe left and right while the ship is banked (i.e. during a turn) then it strafes up or down as well. If I set the transform.translate to be relative to world space then it doesn't have that problem, but then it will always go left and right relative to the world and not the direction of the ship.
So the question is, how can I make the ship strafe right or left relative to the way it is facing while keeping it's height constant relative to the ground.
Note: This is not physics based. I am not using a rigidbody and I would prefer to keep it that way.
Any help would be greatly appreciated.
Answer by aldonaletto · Jul 23, 2011 at 04:47 PM
The best way to split the movements is to split the objects: make an empty object which will effectively turn, strafe and move forth/back, and child to it another object which will fake the banking. If it's a first person game, you should child the camera to the inner object; if the camera is external, child the 3d ship model to the inner object. You can place the control script in the external object, find the inner one with Find("innerName") and tilt it around Z when banking with localEulerAngles:
var bankSpeed: float = 2.0; // set the bank speed
function Update(){ var bankAng = 0.0; ... // do the main object movement, and set bankAng to the desired banking angle var inner:Transform = Find("Inner"); // get child object called "Inner" inner.localEulerAngles.z = Vector3.Lerp(inner.localEulerAngles.z, bankAng, Time.deltaTime * bankSpeed); }
EDITED: That's a simple example on how to bank in the correct order (avoid Rotate: it accumulates errors and the model may get more and more skewed):
function Update(){ var v = Input.GetAxis("Vertical"); var h = Input.GetAxis("Horizontal"); // move the external object using v and h then do the banking: var inner = transform.Find("Inner"); inner.localEulerAngles.x = v*15; // forth/back banking first! inner.localEulerAngles.z = -h*15; // left/right }
Thanks so much for your quick response!
I tried your solution Aldonaletto and I got it working. I thought of doing something like that, but I was hoping there was a simpler solution. I also want to implement a flight mode (not hovering) in which case the pitch of the ship will actually matter, but I guess I can retrieve the Euler Angles from the ship model and use that to deter$$anonymous$$e forward movement even if the inner is unchanged.
Thanks again! I've been stuck on this for a while.
I suppose the pitch should be done by the external object, since it affects the direction of movement - unless it's a fake pitch, just to look down or up in a first person perspective, for instance.
So, since I got the strafing working I decided to get banking to work when moving forward or backwards as well (like a helicopter). I used the exact same solution, having the ship model tilt forward and back but the actual forward and backwards movement controlled by the external object. It works fine but I've discovered that if I combine the two forms of banking, ie left or right while tilted forward or back, then it causes the forward direction of the ship model vs. the external object to become skewed. This means that forward and backwards motion from the external object no longer match the forward direction of the ship model.
Any suggestions?
P.S. I didn't use a Vector3.Lerp like in your example, I just used a simple transform.Rotate. I don't know if that makes a difference.
Lerp was only to rotate gradually. The problem is caused by the order Unity converts eulerAngles or Rotate to Quaternions: z, x then y. Since z is rotated first, the x axes gets tilted, and when rotated the ship nose will point to the other side. The solution is to use localEulerAngles.x first, then localEulerAngles.z. Don't use Rotate because it can accumulate errors and after some time the model will not return to initial position. I edited my answer to show a simple example of how to bank using this approach.
Life has kept me away from this project for a while, but now I'm back and deter$$anonymous$$ed to get it working right. I tried your newest suggestion and it does seem to correct that issue. $$anonymous$$y only question is, shouldn't I be using Time.deltaTime to make sure the banking is frame rate independent rather than just input times a set value (15 in your example)? I tried putting Time.deltaTime in (as I had it before when I was using Rotate) but it just causes the model to jitter rather than bank properly. Any help on this last point would be greatly appreciated and thanks for all your help so far.
Answer by Blankzz · Jul 24, 2011 at 03:24 PM
Could you not take the cross product of your ships forward vector and a world up vector to get one direction. Then flip it to get the other direction?
Edit: Something like the following. Give it a try I'm not sure if it will work. You may have to swap the directions because the cross product depends on the hand rule of the world coordinate system.
Vector3 leftDirection = Vector3.Cross(this.transform.forward, Vector3.up).normalized;
Vector3 rightDirection = -leftDirection;
That's a very interesting solution, but if I understand it correctly, it wouldn't work if the ship was pitched forward or backwards, which is definitely something I want to implement.
By pitch I assume you mean tilting backwards and forwards. If so It will work if the ship is tilted too.
The cross product will equal 0 if your two vectors are the same. Depending on your calculations they probably will never be exactly the same but you could do a check in your code first and adjust the forward vector slightly so the cross product will return a suitable vector.
Answer by DaveA · Jul 23, 2011 at 04:13 PM
Couldn't you save the world Y position, have it strafe relative to the ship, then set the world Y position back to what you saved?
Thanks for your quick response DaveA. Your solution sounds interesting but I don't really think I understand it or how to implement it. Do you think you could explain a little more?
Thanks!