DeadlyShadow said:
i guess option 2 is more suitable for what I'm trying to achieve
Alright. Then the first thing you should do is write a sub-routine that actually computes where the shell is going to hit. The way you are going to do that is by evaluating the trajectory over small time intervals. You will define a constant timeStep to set how often you are going to re-evaluate the forces. I'll tell you in a moment how to find a good value for it.
Trajectory is going to depend on the following: Initial velocity, computed using muzzle velocity, direction angles, and velocity of the platform. Wind velocity, which will probably be a global parameter. Effective drag coefficient that you will estimate.
Your function will consist of the main loop that will repeat until you actually hit something, at which point you'll return coordinates where the hit took place. If there is a possibility for projectile to leave the map, you should account for it. You might also want to return the total amount of time that elapsed, if you want explosions to happen after proper delay.
For each iteration, you should perform following adjustments:
a = g - k*(v-w)^3/(m*|v-w|)
v = v + a * timeStep
x = x + v * timeStep
Here:
a - acceleration for this step
v - current velocity
x - current position
w - wind velocity
k - effective drag coefficient
g - free fall acceleration
m - mass of the shell
Keep in mind that x,v,w,a, and g are vectors, so you'll have to evaluate all 3 components separately. g only has non-zero component in z direction.
|v-w| is the norm of the vector difference. This is done because drag depends on (v-w)^2, and direction is set by (v-w)/|v-w|, which is unit vector in direction of (v-w). You compute the norm as usual: |b| = sqrt(b
x^2 + b
y^2 + b
z^2)
Before you enter the loop, x should be initialized to (x,y,z) of your cannon, and v should be initialized to the initial velocity vector you computed for the projectile. Both of these should probably be simply passed to the sub-routine.
A bit of explanation for values of k. This value comes from simplifying drag formula to one parameter. The full version is this.
F_D = \frac{1}{2}\rho A C_D v^2
And we simplify the computations by defining
k = \frac{1}{2}\rho A C_D
Here A is cross-section area of your shell. Pi*diameter^2/4 will be a good enough estimate, even though it's not entirely true for duration of flight. Rho is density of the air. I'd set it as a constant. Technically it depends on air pressure and temperature, so feel free to look up the complete formula. Value of rho = 1.2 kg/m^3 should work for you, though. (Make sure your units are consistent, by the way. If you are going to use feet, make sure to convert everything.)
The most complicated is value for C
D. That will depend on a whole lot of things, but you are not going to be able to get a super-precise result anyways. It will have to remain a parameter specific to the shell you are firing. Good values will be somewhere in the 0.1 to 0.3 range. Lower values will be affected less by wind and will go further.
Now that you have the subroutine set up and tested, you should figure out what value of timeStep to use. Here is how you do that. Set up a whole bunch of shots with identical initial conditions but use different timeStep for each. Keep decreasing it until the difference between consecutive shots becomes small enough to be considered identical. (A few meters from each other, perhaps.) Keep in mind, however, that smaller timeStep will increase the number of computations, so a value too small will slow down your program, and might cause lag.
Finally, aiming. This is a bit tricky. You need to have the trajectory subroutine working properly and timeStep firmly decided on. You will start with initial guess. There is no reason to be particularly tricky here. Set horizontal aim to point directly at target, set vertical aim to the following estimate:
\theta = \frac{1}{2}arcsin\left(\frac{R g}{V^2}\right)
Here R is distance to target, g is acceleration due to gravity (just a number here, not a vector) and V is the initial velocity of the projectile. If nothing was moving and there was no air resistance, this would give you a perfect hit. Note that if the part you're taking arcsin of is > 1, then there is no solution. That is, target is out of range, and you shouldn't bother aiming at it.
Obviously, because things move, there are hills, drag, and wind, this solution will be wrong. But it's a fair first guess. Compute where the projectile would really land with these angles, and compute where you target expect to be at that time. Take the difference. Now you compute correction for your guess.
Suppose projectile landed at (p
x,p
y) and the coordinates you used for your guess are (t
x,t
y). Choose the following point: (2*t
x-p
x,2*t
y-p
y). Repeat the initial guess computations but for THIS point now. Then compute new impact point, and so on. With each iteration, your guess should land closer and closer to the target. You should repeat this until you either hit the target, OR you can no longer makes guess for an angle (arcsin(>1)), OR you've made too many guesses. Reason for this cutoff is that there might be an obstacle on the way, or wind/drag could be limiting range, or some other reason for why you can't hit the target now. If you don't stop it, it will keep making guesses forever. A good number to cut off would depend on how precisely you need hits, but I would be surprised if you'd need more than 10-20 consecutive guesses. Try starting with that, if it's not working well, increase it to a 100 or so.
And just in case, here is how you compute the horizontal part of the angle, given coordinates of the target at (t
x,t
y) (Note that I'm ignoring Z entirely. Aiming algorithm should converge regardless.) This coordinate is taken relative to platform you are firing from, so you get it from world coordinates after subtracting off x and y coordinates of your cannon.
Define:
s = t
x/sqrt(t
x^2 + t
y^2)
c = t
y/sqrt(t
x^2 + t
y^2)
If s>0: angle = arccos(c);
If s<0: angle = -arccos(c);
Notice that angle=0 corresponds to aim along x-axis and will vary from -pi to pi (-180° to 180°)I think that's everything you need. This should really be taken one thing at a time, because all together it may be a bit confusing. Let me know if anything isn't clear, or if you'll have problems with the code.