OFPEC Forum
Editors Depot - Mission Editing and Scripting => ArmA - Editing/Scripting General => Topic started by: drbobcat on 08 Jun 2008, 00:01:24
-
Although there are many artillery scripts out there that either utilize AI or clever "createVehicle"-ing, I believe it would be far more entertaining for the player if he or she was actually in the gunner's seat. I have been working non-stop on a script that does just that. A hint box offers the gunner data such as his current heading and angle, as well as the heading he'd need to take to be aiming at his target, the distance to his target, and the height difference between himself and the target. Obviously, the next thing needed is the angle at which the barrel must be in order for the shell to strike the target accurately.
My problem: I would like the shell to travel along a "perfect" parabola and strike a certain distance at a given angle. For example, at a thirty degree angle, I want the shell to land x meters away every single time. What kind of formula would I use? How would I go about this? The consideration of height can be dealt with at a later time.
I have already looked at many websites but have not been able to discover a solution to my specific problem. Here is a list of some of those sites....
Wikipedia (http://en.wikipedia.org/wiki/Trajectory_of_a_projectile)
JBM (http://www.eskimo.com/~jbm/calculations/traj/traj.html)
Formularium (http://formularium.org/en/10.html?go=88.114)
Hyperphysics (http://hyperphysics.phy-astr.gsu.edu/Hbase/traj.html)
Measurements Converter (http://measurementsconverter.com/ballistic_trajectory_calculator.html)
An alternative solution may be to specify strict velocities based on the launch angle. Anyway, here is a copy of the main script...if (!(local (_this select 0))) exitWith {};
_info =
{
_gun = _this select 0;
while {(alive _gun) && ((typeOf (vehicle player)) == "M119")} do
{
_xVec = (_gun weaponDirection "M119") select 0;
_yVec = (_gun weaponDirection "M119") select 1;
_zVec = (_gun weaponDirection "M119") select 2;
_tHead = ((getPos _gun select 0) - (getPos targ select 0)) atan2 ((getPos _gun select 1) - (getPos targ select 1));
_cHead = (_xVec atan2 _yVec);
if (_tHead < 0) then {_tHead = (180 - (abs _tHead))} else {_tHead = (180 + _tHead)};
if (_cHead < 0) then {_cHead = _cHead + 360};
_dist = ([getPos _gun select 0, getpos _gun select 1, 0] distance [getPos targ select 0, getPos targ select 1, 0]);
_hDiff = ((getPosASL _gun select 2) - (getPosASL targ select 2));
_ang = (asin ((9.8 * _dist) / 275000));
hint format ["Current Heading: %1 degrees\nCurrent Elevation: %2 degrees\n\nDistance-to-Target: %3 meters\nHeading-to-Target: %4 degrees\nElevation-to-target: %5 degrees\nHeight difference: %6 meters",_cHead,(_zVec * 90),_dist,_tHead,_ang,_hDiff];
sleep 0.01;
};
};
DRB_Art_Fired =
{
_gun = _this select 0;
_ammo = _this select 1;
_wAng = ((_gun weaponDirection "M119") select 0) atan2 ((_gun weaponDirection "M119") select 1);
_rounds = (_gun nearObjects [_ammo,1000]);
_round = objNull;
_located = false;
for [{_i = 0},{_i != (count _rounds)},{_i = _i + 1}] do
{
_round = _rounds select _i;
_rAng = (((vectorDir _round) select 0) atan2 ((vectorDir _round) select 1));
if ((abs (_wAng - _rAng)) <= 2) exitWith {_located = true};
sleep 0.001;
};
if (!(_located)) exitWith {};
switch (_gun getVariable "DRB_Art_Round_Type") do
{
case "he":
{
_he =
{
_round = _this select 0;
_spd = 275;
while {!(isNull _round)} do
{
_round setVelocity [_spd * (sin (getDir _round)),_spd * (cos (getDir _round)),(velocity _round select 2)];
sleep 0.01;
};
};
[_round] spawn _he;
};
case "illum":
{
_illum =
{
_round = _this select 0;
_time = 0;
_spd = 275;
_create = false;
_flare = objNull;
while {!(isNull _round)} do
{
_time = _time + 0.01;
_round setVelocity [_spd * (sin (getDir _round)),_spd * (cos (getDir _round)),(velocity _round select 2)];
if (((getPos _round select 2) <= 300) && (_time >= 2) && (!(_create))) then
{
_flare = "F_40mm_White" createVehicle [0,0,5000];
_create = true;
};
if (((getPos _round select 2) <= 150) && (_time >= 2) && (_create)) then
{
_rPos = (getPos _round);
deleteVehicle _round;
_burst = "weaponHolder" createVehicle [0,0,5000];
_burstB = "G_40mm_HE" createVehicle [0,0,5000];
{_x setPos _rPos} forEach [_burst,_burstB];
sleep 0.25;
_flare setPos _rPos;
{deleteVehicle _x} forEach [_burst,_burstB];
};
sleep 0.01;
};
};
[_round] spawn _illum;
};
case "prox":
{
_prox =
{
_round = _this select 0;
_time = 0;
_spd = 275;
_burst = "weaponHolder" createVehicle [0,0,5000];
while {!(isNull _round)} do
{
_time = _time + 0.01;
_round setVelocity [_spd * (sin (getDir _round)),_spd * (cos (getDir _round)),(velocity _round select 2)];
if (((getPos _round select 2) <= 50) && (_time >= 2)) then
{
_round setVelocity [0,0,0];
_rPos = getPos _round;
{_x setPos _rPos} forEach [_burst,_round];
sleep 0.25;
{deleteVehicle _x} forEach [_burst,_round];
};
sleep 0.01;
};
};
[_round] spawn _prox;
};
case "smoke":
{
_smoke =
{
_round = _this select 0;
_time = 0;
_spd = 275;
_create = false;
_burst = objNull;
_smokeA = objNull;
_smokeB = objNull;
_smokeC = objNull;
_smokeD = objNull;
_smokeE = objNull;
_smokeF = objNull;
_smokeG = objNull;
_smokeH = objNull;
_smokeI = objNull;
_smokeJ = objNull;
_smokeK = objNull;
_smokeL = objNull;
_smokeM = objNull;
_smokeN = objNull;
_smokeO = objNull;
_smokeP = objNull;
_smokeQ = objNull;
while {!(isNull _round)} do
{
_time = _time + 0.01;
_round setVelocity [_spd * (sin (getDir _round)),_spd * (cos (getDir _round)),(velocity _round select 2)];
if (((getPos _round select 2) <= 100) && (_time >= 2) && (!(_create))) then
{
_burst = "G_40mm_HE" createVehicle [0,0,5000];
_smokeA = "SmokeShell" createVehicle [0,0,5000];
_smokeB = "SmokeShell" createVehicle [0,0,5000];
_smokeC = "SmokeShell" createVehicle [0,0,5000];
_smokeD = "SmokeShell" createVehicle [0,0,5000];
_smokeE = "SmokeShell" createVehicle [0,0,5000];
_smokeF = "SmokeShell" createVehicle [0,0,5000];
_smokeG = "SmokeShell" createVehicle [0,0,5000];
_smokeH = "SmokeShell" createVehicle [0,0,5000];
_smokeI = "SmokeShell" createVehicle [0,0,5000];
_smokeJ = "SmokeShell" createVehicle [0,0,5000];
_smokeK = "SmokeShell" createVehicle [0,0,5000];
_smokeL = "SmokeShell" createVehicle [0,0,5000];
_smokeM = "SmokeShell" createVehicle [0,0,5000];
_smokeN = "SmokeShell" createVehicle [0,0,5000];
_smokeO = "SmokeShell" createVehicle [0,0,5000];
_smokeP = "SmokeShell" createVehicle [0,0,5000];
_smokeQ = "SmokeShell" createVehicle [0,0,5000];
_create = true;
};
if (((getPos _round select 2) <= 5) && (_time >= 2) && (_create)) then
{
_rPos = (getPos _round);
deleteVehicle _round;
_burst setPos [(_rPos select 0),(_rPos select 1),0];
_smokeA setPos [(_rPos select 0) + 5,(_rPos select 1),0];
_smokeB setPos [(_rPos select 0) - 5,(_rPos select 1),0];
_smokeC setPos [(_rPos select 0),(_rPos select 1) + 5,0];
_smokeD setPos [(_rPos select 0),(_rPos select 1) - 5,0];
_smokeE setPos [(_rPos select 0) + 3.75,(_rPos select 1) + 3.75,0];
_smokeF setPos [(_rPos select 0) + 3.75,(_rPos select 1) - 3.75,0];
_smokeG setPos [(_rPos select 0) - 3.75,(_rPos select 1) + 3.75,0];
_smokeH setPos [(_rPos select 0) - 3.75,(_rPos select 1) - 3.75,0];
_smokeI setPos [(_rPos select 0) + 10,(_rPos select 1),0];
_smokeJ setPos [(_rPos select 0) - 10,(_rPos select 1),0];
_smokeK setPos [(_rPos select 0),(_rPos select 1) + 10,0];
_smokeL setPos [(_rPos select 0),(_rPos select 1) - 10,0];
_smokeM setPos [(_rPos select 0) + 7.5,(_rPos select 1) + 7.5,0];
_smokeN setPos [(_rPos select 0) + 7.5,(_rPos select 1) - 7.5,0];
_smokeO setPos [(_rPos select 0) - 7.5,(_rPos select 1) + 7.5,0];
_smokeP setPos [(_rPos select 0) - 7.5,(_rPos select 1) - 7.5,0];
_smokeQ setPos [(_rPos select 0),(_rPos select 1),0];
};
sleep 0.01;
};
};
[_round] spawn _smoke;
};
};
_cam = "camera" camCreate (getPos _gun);
_cam cameraEffect ["INTERNAL","BACK"];
_cam camSetTarget _round;
_cam camCommit 0;
_time = 0;
_dist = 0;
while {!(isNull _round)} do
{
_time = _time + 0.01;
_dist = _gun distance _round;
titleText [(format ["%1 kmh \n%2 seconds \n%3 meters traveled \n%4 z velocity", speed _round, _time, _dist, (velocity _round select 2)]),"PLAIN DOWN"];
_cam camSetRelPos [0,-40,20];
_cam camCommit 0;
sleep 0.01;
};
sleep 5;
_cam cameraEffect ["TERMINATE","BACK"];
camDestroy _cam;
};
while {true} do
{
waitUntil {((typeOf (vehicle player)) == "M119")};
_gun = (vehicle player);
[_gun] spawn _info;
_fired = _gun addEventHandler ["fired","[_this select 0,_this select 4] spawn DRB_Art_Fired"];
_act1 = _gun addAction ["Change Type (HE)","DRB_ChangeRoundType.sqf","he",-1,false,true,""];
_act2 = _gun addAction ["Change Type (Illum)","DRB_ChangeRoundType.sqf","illum",-2,false,true,""];
_act3 = _gun addAction ["Change Type (Prox)","DRB_ChangeRoundType.sqf","prox",-3,false,true,""];
_act4 = _gun addAction ["Change Type (Smoke)","DRB_ChangeRoundType.sqf","smoke",-4,false,true,""];
waitUntil {((typeOf (vehicle player)) != "M119") || !(alive player)};
_gun removeEventHandler ["fired",_fired];
_gun removeAction _act1;
_gun removeAction _act2;
_gun removeAction _act3;
_gun removeAction _act4;
};
My thanks,
- dRb
-
Just broad thinking, but you would first have to measure the exact velocity and its air friction to estimate the maximum (turning point) of the parabola, its propulsion velocity and then angles its fired off at. Come up with an equation for this and your set.
:dunno:
-
That is what I am trying to do, yes, but this is all displayed to the gunner PRIOR to firing, not after. Therefore, I need a way to calculate the velocities before hand. Additionally, I do not plan on simulating air friction at all. Let's keep trying to work at this, shall we? I am eager to work past this hurdle, design a few resources for the suite and perhaps even make an addon out of it.
Thank you,
- dRb
-
It is not that you need to simulate air friction, is that air friction is already there. You are "keeping" constant the horizontal speed of the round while vertical speed is somewhat free, that last component of your velocity vector is already affected in the game by gravity and air friction of the moving object. If you want to simulate a round in the vacuum, then you need to calculate your "no-friction" Z component too, if not, then you need to calculate the real initial velocity vector (only once), apply to the round (including correct initial vectorDir and vectorUP) and then let the round free.
-
Precisely, yes. Since I already have a method to calculate the current angle of the barrel, I just need a formula that applies a fixed z-velocity to the next round fired. I have (speed * (sin roundDirection)) for the 'x' velocity and (speed * (cos roundDirection)) for the 'y.' What for the 'z?' My last scholastic dive into trigonometry was too many years ago and I am completely stumped. I imagine I will be able to somehow discover an accurate angle for the gunner through reverse engineering.
- dRb
EDIT: To reiterate my objectives just so all is less confusing....
1) Provide an angle of launch to the gunner before he fires so his shot is relatively accurate.
2) Eliminate drag on the shell as it travels through the air so that it can travel smoothly regardless of the distance.
I will reinstate drag and deviation by applying -small- alterations to the individual velocity speeds using random numbers. Controlled chaos?
EDIT 2: I am not taking into consideration ArmA's air friction at this moment because: (A) there is an utter lack of data regarding how and what the ballistic system calculates, and (B) it would become vastly more complex even than it is now.
-
And heres my steps to you.
Calculate vectorDir on cannon. So say if cannon is fired at 45 degrees (upwards) then do a monitoring script on the round that comes out of it. So many different angles will have to be tested and different conditions aswell.
The monitor script must store the following data:
Round Velocity at a minimum of 6 points for:
-Gravity.
-Air Friction
-Speed of Round
Highest Point (maximum (M))
- More Data for Above Calculations.
- Necessary to help find the equation.
Have fun with the testing, return the results. :good:
EDIT:
The reason air friction is necessary to be aware of is because it is a constant de-cellaration, as with ArmA's gravity, and hence is a constant problem for you to think about. What else makes the round fall on target?
Your parabola also should look something like this:
Artillery Piece
X intercept (Hitting the Ground)
Round Trace
(http://img256.imageshack.us/img256/8800/paracopyhv1.gif)
Note: Incredibly dramatic air friction there, purely for example purposes. Also disregard the straightness of the lines drawn.
-
Alright. It seems to me that this is going to be more difficult than I had first expected. However, the rewards are more than worth it in my mind. I will continue to report my progress in this thread so you all may point out any significant errors I may fail to notice immediately. Before I begin, here are a few more small questions.
1) What is the measurement for gravity in ArmA? 9.8 m/s? 10 m/s? 9 m/s? Has this been documented?
2) In what unit of measurement are the three different values in the "setVelocity" and "velocity" commands? I first thought that the magnitude of the vector between x and y would be equal to forward speed, or ~1000-1100 kmh. That turned out to be incorrect. This is a rough estimate of the data I was able to gather when the gun fired a shell at a 45 degree angle.
- Z velocity: 550 wtf (whatever-the-*** a velocity array element measures...)
- XY velocity: 275 wtf (in total, I mean. Their readout depends on the direction the gun is fired)
- Magnitude: 615 wtf (To the best of my knowledge, the magnitude of the velocity vector is determined by applying the Pythagorean Theorem to the previous measurements. Am not entirely sure)
Rommel, are implying that I should create a kind of "database" that stores all the data needed for each possible angle? Just in my brief tests with the "flawed" system, there are -significant- differences between one degree and another, even up to the hundreth decimal place! It would take weeks of nonstop testing to find (88 * 10 * 10) or 8800 different degree readouts! That is simply not a possibility. There must be a better way... If I have misinterpreted you, please correct me.
*sigh* Back into the fray,
-dRb
EDIT:
The reason air friction is necessary to be aware of is because it is a constant de-cellaration, as with ArmA's gravity, and hence is a constant problem for you to think about. What else makes the round fall on target?
Could not gravity act as a decent decelleration factor alone? How is air friction in ArmA even modeled? Does it change or is it a constant, fixed number? Would it be providing lateral decelleration and gravity providing vertical decelleration?
-
Gravity decelerates the "ascension" and accelerates the descension, air friction deceletares any movement of the round, even the descension. Imagine you are firing the round inside water and you have water friction instead of air friction.
velocity vector components are in meters per second.
Now, if you want to achieve a more or less "automatically" calculated heading and elevation then the gunner will just hit the target area always with a maximum defined dispersion but no way to predict dispersion or circular error possible from the target. Obviously the initial speed of the round will make it imposible to see by any observer, same when the round hits the ground. So, if it works, it would be the very same as onMapSingleClick effect with a round spawned inside the circular error possible of the round type/gun type combo, that is, the same effect of having an arty calculator but without the calculator.
If not, then it is up to the gunner to set the elevation based on a table of elevations/angles/round types. Lets say you already have the initial muzzle velocities of your round types in m/s (real ones), so you simply intercept the Fired event handler, get the round, remove it and switch by another new one, you set the same vectorDir and upVector as the deleted round and then apply the corresponding velocity vector of the initial muzzle vel, then you record the position of the round until it is null (it hits the ground), and you calculate the distance between this position and the gun. You may set a table with 2 degrees intervals of elevation and show it using a dialog. Then a observer needs to provide the aproximate coords of the target, the gunner needs to calculate distance and set elevation manually based on your tables and then fire (or select round type and fire).
Anything else would be an automatic arty calculator.
-
Okay. Here is how I envisioned the system to work when I first began working on it. Assume we are talking about a multiplayer game and all the participants are players.
1) A forward observer or radio operator would request a fire mission using whatever means he wished (chat/VON)
2) The officer in charge of the artillery battery would have access to an option that, upon clicking on the map once, would offer him data such as the distance to the target in meters, the height difference between the battery site and the target area, the heading needed to be aiming at the target, and the angle at which the guns on the battery line must set themselves for their rounds to land in the right area. Those calculations would be given for EACH gun at the site because the differences between 6+ guns along a large stretch of land would greatly impact their accuracy.
3) The officer would then give all the numbers required to each of the cannons under his command and would be able to monitor their heading and elevation angles on a separate readout.
4) Once all the numbers seem to meet up correctly, he'd give the command to open fire.
The main goals I had hoped to achieve were realism gameplay elements (a three-pronged communication line) and yet also maintain simplicity at the same time. It would be impractical and unfair of me to expect each person who wished to play the role of battery commander to be a highly competent mathematician. Even if I were to factor out air resistance and have the path of the round take the shape of a smooth parabola, height differences between the target location and the gun would make the task rightfully challenging (rounds would land fall short if the target were to be above the cannon on the z-axis, long if the target were below). Additionally, I would not be above formulating a separate angle calculation for high-angle firing (a tactic often used to hit the same area and yet avoid striking land prematurely). I find these sort of compromises to be balanced healthily between the realism and "arcade" polar extremes. Any thoughts from you folks?
Sadly, I do not believe it is possible to derive a parabola solely from an angle-of-launch. I probably -will- have write up a table for all the possible angles instead. *sigh*
- dRb
-
Is this the same as this? (http://www.ofpec.com/forum/index.php?topic=31572.0)
-
In many respects, yes, but I want to take his concept further by predicting the angle-of-launch before hand, not merely by trial and error repetitive firing.
Even though I have found out how to obtain a z velocity from the z vector...
(gunBarrelZVector * 90) * 12.167 = roundZVelocity
... I still am suffering from a terribly annoying contradiction: in order to determine the angle of launch needed to strike a target from a given distance, I must be able to predict what velocity the round will take when it is fired. That velocity is the average of the xy velocities and the z velocity. As just mentioned, though, to find the z velocity, we must look at the current angle of the barrel. How can we figure out what angle to launch at if the data it depends on is always changing? I'll have to try something else. That likely means a large chart/table of with all the possible angles and their corresponding distances.
-dRb
-
Your in need of a table with the following information.
Projectile Position A Coordinates | Velocity Position B Coordinates | Velocity Maximum Coordinates | Velocity XY Vector Z Vector
Round A:
Round B:
Round C:
Round D:
Round E:
Round F:
For measurement purposes, getPosASL (above sea level) measurement is used, and the cannon is fired 1M above sealevel. (Simple getPos above the water). This gives a level playing ground for measurements.
_cannon = _this select 0;
<insert Mando code here>
_round = _this select 1;
_posA = getPosASL _round;
_posAV = velocity _round;
_M = 0;
while {_M <= getPosASL _round select 2} do
{
_M = getPosASL _round select 2;
_MV = velocity _round;
hint format["MAXIMUM: %1",_M];
sleep 0.02;
};
waitUntil {getPosASL _round select 2 < 2};
_posB = getPosASL _round;
_posBV = velocity _round;
hint format ["XY Vector: %1 --- Z Vector: %2 --- PosA: %3 --- PosA Velocity: %4 --- PosB: %5 --- PosB Velocity: %6 --- Maximum: %7 --- Point M Velocity: %8",_xyVector,_zVector,_posA,_posAV,_posB,_posBV,_M,_MV];
Insert Mando code is basically the code to obtain the XY vector of the artillery piece (simple getDir?) and the obtaining of the Z vector (unsure how to).
From these results you can obtain the velocity, determine gravity and air friction.
-
If you are looking for a arty calculator then you have two problems:
- As said, drag already affects the rounds in ArmA, so you cannot use any basic vacuum parabolic formula because low and high resulting angles will always result in a miss. This way you would calculate two angles for a hit position as if you take Rommel92's drawing and mirror the left quadrant trajectory in the right quadrant. This case affects to a basic calculation resulting in two angles based on a range and an initial speed (in this case horizontal speed would keep constant).
- If you want to really force a parabolic trajectory then you need to guide the round all the time until impact to nullify the drag effect setting for each time its correct velocity, dir and up vectors.
-
Well, from the assumption that drag force in Arma behaves like F~v^2 (as in the drag equation) we can derive the trajectory. Here's a 2-dimensional solution with no wind assumed:
x(t)=(-e-k t vx0)/k + vx0/k + x0,
y(t)=-(g t)/k - (e-k t (vy0 + g/k))/k + (vy0 + g/k)/k + y0
The orange bits are integration constants to place the muzzle at (x0, y0) and the red bits to fix the initial velocity to (vx0, vy0). k is a constant that has to be derived experimentally. Happy hunting :)
Edit: Screwed the calculations - no wonder the result was that simple. F~v^2 produces a bit more complicated results; the above is actually for the assumption F~v, which might work as well; at least it creates plausible trajectories.
-
Good happy hunting proposal. :D
-
Did some sloppy experiments. g seems to be around 11,25 and k about 0,18 for G_40mm_HE shell - though I don't know if the munition type affects the results. I really do hope they don't, for simplicity's sake. Keep in mind, though, that I have no idea of the error margins on these results, they could well be significant. I might do some serious experimenting on these at some point.
If you try it out with these constants, do tell about your results :D
Edit: Checking the calculations. Could have some errors here and there.
-
Right. Velocities.
In many respects, yes, but I want to take his concept further by predicting the angle-of-launch before hand, not merely by trial and error repetitive firing.
Even though I have found out how to obtain a z velocity from the z vector...
(gunBarrelZVector * 90) * 12.167 = roundZVelocity
... I still am suffering from a terribly annoying contradiction: in order to determine the angle of launch needed to strike a target from a given distance, I must be able to predict what velocity the round will take when it is fired. That velocity is the average of the xy velocities and the z velocity. As just mentioned, though, to find the z velocity, we must look at the current angle of the barrel. How can we figure out what angle to launch at if the data it depends on is always changing? I'll have to try something else. That likely means a large chart/table of with all the possible angles and their corresponding distances.
-dRb
It's not an average. You can calculate the total speed as v = Sqrt(xy^2 + z^2), but there's a much simpler way - muzzle velocity is a fixed value. Here's an event handler that, when in the init field of a player-controlled MK19 gives the muzzle velocity (which happens to be 240m/s):
this addeventhandler ["fired",{ shell=nearestobject [player, "G_40mm_HE"];vel=(speed shell)/3.6; player sidechat format ["%1",vel]}]
From that speed you can easily determine the velocity vector, as weaponDirection returns a unit direction vector - just multiply the muzzle speed component by component:
let dirv=vehicle player weapondirection "MK19";
muzzlevel=240
which results in velocity shell == [(dirv select 0)*muzzlevel,(dirv select 1)*muzzlevel,(dirv select 2)*muzzlevel].
BTW, the mass of the projectiles do affect their trajectories (but object shape or orientation doesn't seem to matter, which means that the problem is at least solvable).
-
Here is some more information that I have gathered throughout my tests...
When fired at a 45 degree, the default ArmA shell exits the barrel at roughly 3960 km/h (1100 m/s) and reaches a maximum distance of roughly 5800 meters at 0 launch height, 0 impact height. However, I wish for the round to reach a maximum range of 11400 meters (the real max range when using Charge 7). Additionally, I desire the round to completely ignore air friction so that I can easily predict where the round will go before it is fired. I currently have been using these calculations to dictate the velocity of the shell:
_wAng = (((_gun weaponDirection "M119") select 0) atan2 ((_gun weaponDirection "M119") select 1));
_wElev = (((_gun weaponDirection "M119") select 2) * 90);
_spd = 475;
_zVel = (_wElev * 12.06);
while {!(isNull _round)} do
{
_round setVelocity [((_spd * (cos (_wElev))) * (sin (_wAng))),((_spd * (cos (_wElev))) * (cos (_wAng))), _zVel];
_rPos = getPos _round;
_zVel = _zVel - 0.392;
sleep 0.01;
};
This causes the round to descend at an arbitrary rate of 39.2 m/s (four times gravity, or 9.8 m/s to the best of my knowledge) and travel along a realistic looking trajectory. Because I have altered "gravity" in this case, I know my formulae will have to be changed as well, likely substituting all 9.8s with 39.2s. Please correct me if I am wrong here!
Regarding muzzle velocity, The_Mark, is that the same velocity I need to apply to the angle and distance formulas mentioned on the wikipedia article regarding trajectories (IE th = ((asin ((g * d) / v^2)) * 0.5)? I knew that it had to be a constant value, but was completely unsure as to what it was. I tried the magnitude of both the xy and z velocities, as well as the average. Of course, those were not correct as those always change depending on where the gun barrel is aiming. I will definitely have to try out your formula! If that does turn out to be the number I am looking for, all the equations I have been using thus far should begin to function correctly given that air friction has ultimately been eliminated!
I believe we are closing in on a conclusion! Thank you!
-dRb
EDIT: After experimenting with the changes The_Mark supplied, I came up with an entirely new set of numbers to work with. If I keep the constant of 39.2 m/s for z-velocity deceleration, I need a "muzzleVelocity" of 640 for the round to land at ~11400 meters when fired at a 45 degree angle. Current angle prediction formula fails to be accurate when the range falls towards one extreme or the other:
((asin ((39.2 * _dist) / (640 ^ 2))) * 0.5)
And the new setVelocity code:
_wVec = _gun weaponDirection "M119";
_spd = 640;
_xVel = (_spd * (_wVec select 0));
_yVel = (_spd * (_wVec select 1));
_zVel = (_spd * (_wVec select 2));
_round setVelocity [_xVel,_yVel, _zVel];
while {!(isNull _round)} do
{
_round setVelocity [_xVel,_yVel, _zVel];
_zVel = _zVel - 0.392;
sleep 0.01;
};
What is it that I am doing wrong? While I realize I could just fire 85 different rounds, gather the data, and put it all into a table for the player to use, I'd much rather get this formula working correctly. *grumble*
EDIT 2: I have noticed that gravity has always been listed as 9.8 m/s² not just 9.8 m/s. How does that translate into an algorithm for constant vertical deceleration? Specifically, how would that change if I were to intensify the effects of gravity (see the 39.2 previously mentioned)? Also, I still have yet to correctly determine the muzzle velocity I need to plug in to the calculations in order to derive an accurate angle of launch... Anyone have any thoughts?
-
Acceleration (and deceleration, which is just acceleration with a minus in front of it) is defined as the change of speed with regards to time, ie. mps/s; a constant acceleration of +9.8m/s^2 means that an object's speed grows 9.8m/s in one second. 2s*9.8m/s^2=19.6m/s after two seconds, etc. Generally, for a constant acceleration a you can calculate an object's speed at time t, with the initial velocity v0 with the function:
v(t) = a*t + v[sub]0[/sub]
This means that your algorithm for constant vertical deceleration (the new setVelocity code) is valid. It should produce perfect parabolic trajectories, but you have to remember that you can't disable the physics engine in its entirety, and it could create anomalies at range limits - it has a .01 sec window in which to screw things up by accounting for air drag and slowing the shell minutely. Though, this is just a guess; I don't have that deep knowledge of the inner workings of the game. You could, though, use setpos to guide the shell precisely through the trajectory.
Extremely short-range shots will not obey the range equation, by the way - you have a rotating and tilting barrel, the muzzle of which defines the starting point of the trajectory. The starting height will vary according to the angle at which the gun is tilted. Still, that's not a major concern, as you can just aim through the sights in that case :D
As for the launch angle, you can derive it from the Pythagorean theorem and basic trigonometry, provided you have the initial velocity vector. You have to consider that you have the vertical component of velocity, z, and the horizontal components x and y. First, you need the total horizontal component xy = Sqrt (x^2 + y^2) as per Pythagoras, and then you can consider this probably poorly done ASCII drawing of a vector triangle:
xyz
/| z
/ |
/ |
/ |
/ |
/ |
/ a |
------------->
xy
Here you have the horizontal component xy, the vertical component z and the total muzzle velocity xyz (whose magnitude is given by Pythagoras: xyz = Sqrt(x^2 + y^2 + z^2), or from the muzzle velocity recording event handler I presented before) and the angle a, which is the launch angle. Then, from trigonometry you can see that tan a = z/xy, or as well, sin a = z/xyz or cos a = xy/xyz, so you get a = atan z/xy = asin z/xyz = acos xy/xyz.
Then, to show off advanced mathematics my progress at examining ballistics in ArmA: The realistic drag equation, where F~v^2 produces a frickin' complex solution, with an awfully awkward singularity at the limit approaching maximum range and _several_ beyond it. More of that later, first something about the relation F~v. It's solvable, quite easily. It can be tuned to give quite accurate velocity predictions, at least on MK19 at some angles, though dunno about the range, as I have kept at predicting velocities with somewhat simpler equations (yes, I'm lazy). However, my gut feeling about terminal velocity experiments says that F~v^2 is what we should be looking at, but I still have to confirm this properly.
Well, as for the math for F~v^2... it's still a bit beyond me. Fortunately, I've got my trusty Mathematica, and here's a screenshot of my current notebook:
(http://img291.imageshack.us/img291/7375/armalaskuajo1.th.jpg) (http://img291.imageshack.us/my.php?image=armalaskuajo1.jpg)
Yeah. How you like them apples.
-
Well, it seems that no matter how accurate my calculations are, the shell will never land exactly where I want it to because a 0.01 loop is not fast enough. Therefore, I will instead setpos the round to where it needs to be to land where it should. Completely factoring out air resistance in ArmA is an extremely challenging task, and one that I feel is not worth tackling when alternative methods will render the results I am looking for.
I shall post again when the script has been updated. Thank you.
-dRb
-
This problem is solveable by taking a more simple equation and fitting it given data from the game. You need to gather the data in the flattest possible area. Your artillery and your target area should be all the same altitude. Record the position of the artillery. If possible orient the artillery at one of the cardinal directions. Fire your artillery at 5 degree increments in the vertical. At each each increment take at least 5 shots. For each shot, record the muzzle velocity, weapon direction, the position at which the shell hits the ground, the ASL pos at which the shell hits the ground and the wind.
Give me that data and I will fit the equation for you. As well as determine the drag coefficents.
-
That's too easy :D
What we seem to have in ArmA is something where the drag coefficient is a constant for each object (that use ordance-type simulation, at least), regardless of direction - at least that is my hypothesis, yet to be comprehensively tested. Yes, I fail at basic rigor here. Nevermind that, though. Without all the chaotic effects of turbulence, eddies and such we should be able to derive an exact solution for trajectories, even with wind accounted for. Approximations, while being a physicist's tool of choice, get boring after a while.
-
Even with the best equations, in this case you do not know how the engine exactly behaves. Your model will only ever be an approximation of the ArmA engine physics. For each ordnance type you will need to determine the mass and lateral and axial drag coefficents. This is best done by least squares fitting your data to your equations, whatever they may be.
-
We can reasonably assume that the engine bases its ballistics on relatively simple mathematics with as much as possible real physics in it (hence the relation F~v^2 is a more than likely candidate). As for not exactly knowing how the engine exactly behaves, well, that's where physics comes in - trying to figure out how it works, approximately, or, if possible, exactly. With the most preeminent chaotic factor (non-constant drag coefficients) (possibly and likely) eliminated from the engine we do have a chance of finding an exact solution. It's, after all, just a simulation.
As for determining the mass and drag coefficients, they both can be included in a single constant, which I've called k in this thread.
-
*sigh* I guess if all the variables in their entirety were modeled within an algorithm, I could possibly apply ONLY an initial velocity to the round at the beginning of its journey. Letting the game's physics engine take over from there, it would travel the distance it needs to. That does sound terribly complicated, though. You shared with us your understanding of the air drag formula many posts ago, The_Mark, could you again state it but articulate what each variable represents?
I currently am using a method that calculates a distance based on the current launch angle. A position is derived from that distance and the round is setPos-ed overhead, given a sharp negative velocity, and the shell strikes the ground. To make it less of a rifle and more of a howitzer, sway has also been implemented, increasing accordingly with the time the round is "in the air." Lastly, there is also a minimum range to take into consideration, thereby giving the gun pronounced vulnerability.
_spd = 747.3955;
_dist = (((_spd ^ 2) / 49) * (sin (2 * _wElev)));
if (_dist < 2500) then {_dist = 2500};
_time = (((2 * _spd) * (sin _wElev)) / 49);
if (_time < 2) then {_time = 2};
...
_rPos = [((getPos _gun select 0) + (_dist * (sin _wAng)) + ((random (_time * 6)) - (random (_time * 6)))),((getPos _gun select 1) + (_dist * (cos _wAng)) + ((random (_time * 6)) - (random (_time * 6)))),500];
sleep _time;
_round setPos _rPos;
_round setVelocity [0,0,-250];
Suitable for now, but it has one major flaw. It completely bypasses the land between the target and the gun. Not only is this quite unrealistic, it also makes things a little too easy. So, if I -could- somehow interpret ArmA's air friction model, I would again have a more believable trajectory. *ponder*
We'll see,
-dRb
-
Well, from the assumption that the drag force F~v^2 (meaning F behaves like v^2, that is, F = k v^2, where k is some constant), we have the differential equation
a = k*v^2 + g
Where a is the objects total acceleration, k the constant of relativity and g the constant gravitational acceleration (around 11.3 as it would seem in ArmA after a quick experiment). k represents here, among other things, the mass of the object and its drag coefficient - the other factors in it are not important, as they're (probably) constants in ArmA, such as the density of air. k has to be experimentally determined for each projectile (about 0.0006 for G_40mm_HE, needs improvement as well); I haven't got a clue where e.g. the mass of an ArmA object is defined. Next, I'll be determining a more accurate value for k(G_40mm_HE).
The differential equation itself could be easily used to run a quick simulation in a script to determine the impact point from muzzle velocity alone. Running the simulator backwards would be a bit harder, iterating between simulations of shells fired from the gun and ones fired backwards from the impact point. It might converge at the right spot, but not necessarily, I just thought of that. The simulations would require accurate constants, though.
Mind you, if ArmA models any sort of trans- and/or supersonic behaviour, the equations won't work. I doubt that it does.
-
Well,
I plotted du/dt vs u for shots fired straight north, but at a low elevation. u is east-west velocity, wx is east-west wind component. In ArmA the wind stayed constant for the duration of the shots. The end result is very close to du/dt = -k*u*V, where u is the east-west velocity component, V is the magnitude of the total velocity and k is a constant.
Sh_105_HE M119:
trial direction elevation u0 V0 k(x10^-4)
1 -0.0977 2.26974 -1.6063 1088.37 4.3263
2 -0.0977 2.26978 -1.03005 1079.37 4.3384
3 -0.0977 2.26978 -1.23268 1078.38 4.2707
where u0 is the initial east-west velocity, and V0 is the magnitude of the full initial velocity. (I know I need more than three trials for rigor). I would like to fit the trajectory to the formulae for quadratic drag, but I made the mistake or recording z and not z_asl. BTW I used DSTS to record the trajectory data. Great tool!
The drag coefficient was ~(4.3118 +/- 0.036)*10^-4 for Sh_105_HE. Wind was not included in the calculation because it ruined the data fit. So it looks like BI handles dispersion by adding noise to the initial velocity (I guess any addon maker could have told us this?) uses quadratic drag with a constant coefficient and ignores wind. I will take a quick look at the north-south component next and see if it is consistent with these findings. It looks like trajectories can be only be calculated using numerical methods. Use a parabola or linear drag to give the initial guess, and then relax the solution. Or gather more statistics, fit to a linear drag and estimate the errors in k.
edit: drag coefficient for north-south and up-down motion are almost the same as that for east-west. I also fitted for g. ArmA appears to use a value of ~9.7 which is odd. They must have tuned it slightly. So there you go. Now I guess I should also test for some trajectories fired at higher elevations. The winds were also consistently low and constant for this first test.
edit2: tried more cases and the results are consistent. For this second group of tests k = (4.34 +/- 0.02)*10^4. this time the average g was 9.8. Wind seems to have no noticeable effect on trajectory.
So I think we can be conclusive about this for Sh_105_HE. I would imagine all artillery type ordnances have the same drag coefficient. It is possible to estimate the trajectory by integrating forward. The only missing piece of the puzzle is what the artillery muzzle height is as a function of elevation. You might need to add a small vertical offset for the artillery. I don't claim it is bug free, but it should give an idea of how you might proceed. Assuming artillery is on a level surface.
_gun = _this select 0;
_muzzle = _this select 1;
_muzzleOffset = _this select 2;
_muzzleLength = _this select 3; // Length should be such that (_muzzleOffset + _muzzleLength * sin _elev) gives you muzzle height above the ground
_vm = _this select 4; //magnitude of muzzle velocity
_k = _this select 5; //drag coefficient
_dt = _this select 6; // Your time increment. Should be set as small as possible i.e. 0.01 or smaller
_maxRange = _this select 7; // Maximum range. No idea. Maybe 3000-5000? Is 3-5 km reasonable?
_pos = getPosASL _gun;
_vectorDir = _gun weaponDirection _muzzle;
_elev = 90 - acos ((_vectorDir select 2) / sqrt ((_vectorDir select 0)^2 + (_vectorDir select 1)^2 + (_vectorDir select 2)^2));
_vectorDir = _gun worldToModel _vectorDir; //transform to model coordinates
_x = 0;
_y = 0;
_z = _muzzleOffset + (_pos select 2) + _muzzleLength * sin _elev; // ASL height of muzzle
_u = _vm * ( _vectorDir select 0);
_v = _vm * ( _vectorDir select 1);
_w = _vm * ( _vectorDir select 2);
_gl = "logic" createVehicleLocal [0,0,0];
_zASL_Land = -666;
_posw = [];
_lastPos =[];
while {_z > _zASL_Land or _y < _maxRange} do
{
_lastPos = _posw;
_vel = sqrt(_u*_u + _v*_v +_w*_w);
_u = _u - _k *_u*_vel*_dt;
_v = _v - _k *_v*_vel*_dt;
_w = _w - (_k *_w*_vel + 9.8)*_dt;
_x = _x + _u*_dt;
_y = _y + _v*_dt;
_z = _z + _w*_dt;
_posw = _gun modelToWorld [_x, _y, _z];
_posw set [2, 0];
_gl setPos _posw;
_posASL = getPosASL _gl;
_zASL_Land = _posASL select 2;
};
deleteVehicle _gl;
//Now _lastPos holds the shell position just before impact with ground.
-
Commendable work, you two. You both are not only talented practicioners of math, but also quite patient and resourceful. I shall begin experimenting with these equations and such as soon as I can gather up some free time. As a side note, however, I was looking over the Biki entry for CfgAmmo and noticed that there are many settings dictating a round's ballistic characteristics, including air resistance. I was wondering how they could play into your calculations.
http://community.bistudio.com/wiki/CfgAmmo_Config_Reference#sideAirFriction (http://community.bistudio.com/wiki/CfgAmmo_Config_Reference#sideAirFriction)
Source: http://community.bistudio.com/wiki/CfgAmmo_Config_Reference (http://community.bistudio.com/wiki/CfgAmmo_Config_Reference)
I shall return in a while and edit this post so that it contains the sideAirFriction values for "SH_105_HE" (M119 round) and "G_40mm_HE" (M203/Mk19 round)
- dRb
-
I can't see how side friction applies because I got the same drag coefficient no matter what side. Maybe this is more applicable guided missiles? If I get a chance tomorrow, I'll run the same tests on the G40.
What the hell is deflecting ammo?
-
Okay. I came to the same conclusion as you: "sideAirFriction" is applicable only to missiles. As to your question, I believe "deflection" is ArmA's value for the angle a bullet will take when and if it ricochets.
I'll continue to look for some sort of exact value we can give 'K' so your formula will consistently return accurate results regardless of whatever weapon is being fired. Having to test each and every time to find 'K' would be a pain. I do have another question to ask The_Mark, though. If the value of 'a' is the total acceleration of the round with air friction, would that be equal to the muzzle velocity (v) that we plug into our old friend, the algorithm for angle of launch ((asin (gd / v^2)) * 0.5)? That would be excellent if it were. I'd highly prefer allowing ArmA's physics engine to handle the flight of the artillery shell over any other "creative" solution.
Back into the thick of it,
-dRb
EDIT: Woah. Interesting. Here are the "airFriction" values for the two forementioned rounds. That attribute is not listed on the cfgAmmo Biki...
SH_105_HE = -0.00045
G_40mm_HE = -0.0005
The estimated drag coefficient given by Mr.Peanut was ~(4.3118 +/- 0.036)*10^-4 , or ~0.000434. That is extremely close to (4.5 * (10^-4))!
-
Haha,
I wish I had read your post before I went ahead with second test for MK19 G_40mm_HE!
Four shots at four different altitudes:
K = (4.98 +/- 0.08)*10^-4
g = 9.81 +/- 0.02
First velocity measured = 238.71 +/- 0.28
Not bad, eh? Now if you could dig through the weapon configs and get the muzzle velocities we would be set. I can also improve my function to take terrain slope into account.
edit:If the value of 'a' is the total acceleration of the round with air friction, would that be equal to the muzzle velocity (v) that we plug into our old friend, the algorithm for angle of launch ((asin (gd / v^2)) * 0.5)? That would be excellent if it were. I'd highly prefer allowing ArmA's physics engine to handle the flight of the artillery shell over any other "creative" solution.
Um, why not use the code I provided? What you want to do is add and action to the cannon that calls a script. The script exits if it is not the gunner. The script calls my code. You can then display the range of the shot, and the height difference.
If you want to solve for what elevation to hit a given target, forget about it. I mean, it can be done, but it is non-trivial, at least to people without 2 years of undergraduate math or physics. There is also the problem that if your artillery has a large range of elevation and your target is in a certain range there are two solutions, a high angle and a low angle. A numerical solution is guaranteed, but not for the angle you may want. You would want the low angle for flat terrain becuase it hits the target sooner. You would want the high angle if there was terrain in the way.
Here is how it could be done. I don't think the ArmA engine could handle it without a few hiccups.
1) Use parabola to estimate the elevation required to hit the target. It there are two viable elevations keep them both.
2) For each parabola estiamte: plug the elevation into my code and get the impact point. Calculate whether shot went too far or too close. Adjust elevation. Try again. Repeat until within a given tolerance of target.
This is literally called "the shooting method" http://www.physics.louisville.edu/help/nr/bookfpdf/f17-1.pdf (http://www.physics.louisville.edu/help/nr/bookfpdf/f17-1.pdf) and uses an algorithm to decide what the next guesses for elevation should be.
A better solution is to fit a polynomial to the trajectory based on the elevation.
-
Oddly, the entry for the muzzle velocity is listed not in cfgWeapons or cfgAmmo, but rather cfgMagazines. :dry: ::)
_muzzleVelocity = (getNumber (configFile >> "cfgMagazines" >> "30Rnd_105mmHE_M119" >> "initSpeed"));
This happens to be 1100 m/s (or 3960 km/h: the same number I had mentioned briefly a page or so ago).
If you want to solve for what elevation to hit a given target, forget about it. I mean, it can be done, but it is non-trivial, at least to people without 2 years of undergraduate math or physics. There is also the problem that if your artillery has a large range of elevation and your target is in a certain range there are two solutions, a high angle and a low angle. A numerical solution is guaranteed, but not for the angle you may want. You would want the low angle for flat terrain becuase it hits the target sooner. You sould want the high angle if there was terrain in the way.
Ugh. I was so optimistic with our more recent discoveries and the possiblity that we'd be able to use them within the classic formulas. We will not be able to predict the angle of launch prior to firing or calculate the distance the round will travel? If that is true, the current system I have in place will suffice, I guess... *reluctance*
- dRb
-
For a given weapon and muzzle, you can almost exactly fit a 3rd order polynomial to the trajectory with coefficients that appear linear with muzzle elevation. At least this is true for the Mk19 G_40mm_HE combination. Assuming shot is fired straight north from (0,0,0) with elevation _e:
_c3 = (-3.02 * _e - 29.30)*10^-9;
_c2 = (3.71 * _e - 93.733)*10^-6;
_c1 = (1.59 * _e + 0.57)*10^-2;
_c0 = 0.2585 * _e + 14.0383;
_z = ((_c3 * _y +_c2) * _y + _c1) * _y + _c0;
Using this formula will get you within 10m of the target. _z is ASL. It does not make it any easier to solve _e for a given target, but it is a much faster way to get an estimate of the trajectory.
Ugh. I was so optimistic with our more recent discoveries and the possiblity that we'd be able to use them within the classic formulas. We will not be able to predict the angle of launch prior to firing or calculate the distance the round will travel?
The equations involved are non-linear, which means they can not be solved analytically but numerically . In fact, people are still publishing solutions to these equations! So you are out of luck solving for _e.
-
Aww, mr.P you should know better not to post consecutively...
-
What consecutive post? :dunno:
-
The guy from fdf released his ofp script with necessary calculations about 6 months ago , it works very well in arma too.
One major problem is FPS , everything works fine in 1st person and all calculations are spot on, the problem is when you go into map screen or look at the sky and get a massive Fps increase , it seems to effect even the SQF version I.e shell land a lot closer to ya and he is is sleeping his script at .001 intervals , i really hope your method works , i am desperate to blow the crap out of my buildings with some real nice arty.
-
This is not likely to improve matters. Solution requires number crunching.
drbobcat, can you give me the config retrieval snippet for the air drag and ammo?
edit: Okay here is a proof of concept. Fire a shot. After the shell lands, a script calculates the theoretical trajectory and then the impact points are plotted on the map. Green square is you. Red hatches are where you zoom in on map. Zoomed in are a small red and blue circle of 20 and 10m radii. The red circle is where a bullet cam says the shell hit. The smaller blue dot is where the calculation says it landed.
-
Alright.
_airFriction = (getNumber (configFile >> "CfgAmmo" >> "SH_105_HE" >> "airFriction"));
I will now test and experiment with the script you provided, Mr.Peanut. However, it does not seem to lead us very far from the artillery script by Jones that Mandoble hinted at on the first page. I so badly wanted to be able to provide the player either an angle to fire at to reach a certain distance or just the distance his current round will reach. Guessing is quite inefficient. Creating a table is a tiresome chore that'd I rather avoid as a last resort. *grumble*
- dRb
-
I found a bug in my script. Please re-download. I am working on the solution to the problem you describe, but I am now drunk and not making progress. Stay tuned. There is a solution, but it is hard to debug when drunk.
How can I get the muzzle velocity?
_muzzleVelocity = getNumber (configFile >> "cfgMagazines" >> "30Rnd_105mmHE_M119" >> "initSpeed");
A fired eventhandler returns M119 as muzzle. How can I derive the muzzle argument "30Rnd_105mmHE_M119"?
-
The fired event handler does not return the magazine currently in use. Therefore, we'll have to utlize a switch-do database setup that detects the type of weapon being fired and then plugging in a value for the corresponding magazine....
_wep = _this select 1;
_mag = "";
switch (_wep) do
{
case "M119": {_mag = "30Rnd_105mmHE_M119"};
};
...
_muzzleVelocity = getNumber (configFile >> "cfgMagazines" >> _mag >> "initSpeed");
I just realized that we could easily derive the distance prior to firing by simulating the firing of one by using a gamelogic and raised numerical values. What I mean is that we could "fire" a game logic a certain distance based on the current angle of the barrel and the velocity the shell would take were it to be shot. After the logic has completed its journey, we can measure the distance between the gun and the logic. That would roughly yield the same distance the actual round would take once it was fired. Is that reasonable? By raising the numerical values, I am implying that we could raise many of the numbers within the formula by a fixed power so that the calculation does not take 20+ seconds to do.
- dRb
EDIT: A better, more dynamic solution to derive the magazine value would be to use the array equivalent of the getNumber command...
_mag = ((getArray (configFile >> "CfgWeapons" >> _wep >> "magazines")) select 0);
_muzzleVelocity = getNumber (configFile >> "cfgMagazines" >> _mag >> "initSpeed");
-
drbob,
I guess the gamelogic idea would work though I have no idea if a game logic has a drag coefficient.
I have cracked this nut(ow!), but my code is still in a very rough prototype stage. It works, but has no error handling and has not been optimised. Because the code is demanding for the ArmA engine, it would best be run from a little dialog so that the player is sheltered from the stuttering( and perhaps to simulate using a battle computer). I don't do dialogs but my finished code will be flexible. I have a couple ideas to improve the numerical methods used (maybe 50% faster) and also tell the user if the shot is possible i.e. whether terrain blocks the shot. The solution time will then be about 10-20 seconds(I hope).
I will continue to make this mostly a function based solution, with a parameter for sleep time in the main loop.
I need a couple more things. One is the proper case "M119": {_mag = "30Rnd_105mmHE_M119"}; stuff for the mk19 g40 round, the other is how to get at the ammo lifetime values. I will use the lifeTime to calculate the ideal maximum range so that my code can make a graceful exit if the target is out of range. Presently that situation causes a nasty division by zero error.
-
To answer your first question, we'd determine the muzzle velocity of the G_40mm_HE round the same way. I still very much recommend you use the second method I provided, as it is the most efficient and can obtain the initSpeed no matter what weapon we're using.
_wep = _this select 1; // A "fired" eventHandler passes the weapon as its second argument
_mag = ((getArray (configFile >> "CfgWeapons" >> _wep >> "magazines")) select 0);
_muzzleVelocity = getNumber (configFile >> "cfgMagazines" >> _mag >> "initSpeed");
If you must know the magazine name for the Mk19, it is 48Rnd_40mm_MK19.
As for finding the lifetime of a round, that is a property in CfgAmmo. We can again use getNumber to ascertain what that value actually is...
_ammo = _this select 4; // Ammo is passed as the fifth argument by a "fired" eventhandler
_timeToLive = getNumber (configFile >> "cfgAmmo" >> _ammo >> "timeToLive");
Finally, I meant that we'd apply the velocity (including the drag coefficient of the round) of the round yet to be shot to a gameLogic after the player engages an action such as "Determine Range". We now know how to obtain the muzzle velocity of a round, its drag coefficient (k), and its timeToLive. If these were all put into a simulation using a gamelogic, I believe we'd then be safely able to know where the shell would land if it were fired. Your calculations are able to yield a parabolic curve with all the known ArmA variables taken into consideration, are they not? I've yet to encounter any other contributing phenomena in ArmA that'd we would have to wary of in reality, such as wind, changing air pressure, or any others. I am curious as to what you think would be the best method in finding the range?
- dRb
-
My code solves for the range and the flight time. A game logic is not needed in the manner you suggest. One part of the solution is the same as in the demo I posted. It integrates the equations for a given initSpeed, drag and elevation to determine where the shell would land. The second part of the solution involves iterating to find the elevation to hit a given target. It estimates the derivative of distance error by elevation and uses this to guess the next angle based on Newton-Raphson. All this I have done, but as I said, the code does not handle errors gracefully, and there are free parameters relevant to the numerical methods. Finding a good solution in as few iterations as possible depends on having good choices for these parameters. Bad choices either yield no solution or take 5 times as long to find a solution. Add to this the fact that some solutions are unphysical (i.e. elevation can not be greater than 90 degrees or less than ~-5 degrees) and make the solution unstable. This is not a difficult problem, but it is fussy and takes forever to test in-game. But I am very very close.
There are some special conditions:
1) Target is out of range(to close or too far). This leads to a division by zero error. Still to be coded.
2) Target is in range but shot is blocked by terrain. Solved.
3) There are two solutions for targets within a given range. A low elevation and a high elevation. Solved.
4) Getting ordnance data from config. Still to be coded
At the moment my code can determine accuracy within 1-2m, if desired. The ArmA engine slightly randomises the initial speed, so even with an exact solution, you are not always guaranteed a hit. I am pretty happy with the results but the code really is a mess so I am working to clean it a little before I post another demo for your perusal. Thanks for the config info, as using configs makes the script multipurpose. Most users need a script for which they do not have to look up config data.
I'll hopefully have a little time tonight to get up to a second demo. Until then...
edit: Oops! All you want is the range given an elevation? That part is easy! Do you want the "real" range i.e. takes terrain into account? This value can be misleading. Imagine the difference in range between a shell that hits a hilltop and a shell that just barely passes over! The problem is if you ignore terrain, then you have to arbitarily choose when to stop the shell. What I mean is you need to provide an above sea level height. One good choice would be either 0 ASL height, or the ASL of the artillery. The script that calculates range takes less than one second, during which it halts all other arma function, so it would not be good to call it constantly. In fact my traj.sqf script from my first demo already returns the target position, the flight time and the range, and takes terrain into account. The return = [_lastPos, _elapsed, _d]. _elasped is the flight time, _d is the range. If that is all you want then your problem is already solved.
Of course, I can still solve for elevation given a target
-
ArmA already has a command that returns the distance of one thing to another. Therefore, just comparing that against the current range based on launch angle is more than enough. You do not need to concern yourself with determining an accurate angle of launch if the former can just as easily be ascertained. They both achieve the desired result.
Additionally, you must also remember that the splash radius of an artillery shell in ArmA is at least 25 meters. If you can already achieve an accuracy of 1-2 meters, we're more than golden. However, it would be nice if when calculating range, we could take terrain into account. This would allow the gunner to switch to a higher angle in order to strike the same target. That would likely take much, much more time to do, though. As for height differences, perhaps an adjusted distance-to-target could be given so that the gunner can aim closer/further based on the height of the target in relation to the howitzer piece. What I mean is that the more extreme the difference in height, the more extreme the deviation of the round. It would most certainly behave like a triangle, if that makes any sense...
Sorry to be so short with you, Mr.Peanut. Today is just going to be a busy day for me. You've been doing a great job thus far and I am thoroughly impressed. There is no doubt that you will receive a great deal of credit once this resource if complete. Thank you.
- dRb
-
Just considering the specifications in the initial post you may consider the following very basic rules:
- For any elevation and initial muzzle vel you now the time required by the shell to reach 0m/s in the vertical plane just applying gravity + a constant decceleration due drag (which result in a constant negative acceleration).
- Now you have the predicted height of the projectile at 0 m/s which will start its "descension".
- It will need to descend the vertical distance between its predicted maximum height and the height of the target, both ASL.
- With this you know the time required by the shell to travel vertically that distance applying also the gravity - the constant drag for acceleration (with result in a constant positive acceleration).
With these basic calculations, before firing a shell, you already know its flight-time.
Now apply that flight time to a simplifyed horizontal speed calculation where only a constant drag affects the initial horizontal speed of the round. And that will give you the exact range of the round fired at that angle.
Now, to be able to simulate several shell types (with different muzzle vels and drag), you'll need to guide the shell in-flight applying the basic constant acceleration movement formulas for its velocity vector until impact. With this you have a "shell" trajectory simulator, where a script is applying speed, gravity effect and drag to the flying object.
This way you are no providing the elevation required to reach a particular range with the selected shell type, but you are providing the range for each angle of the gun and type of shell used. The gunner would only need to change gun elevation until the desired range is matched with the calculated one (you may show both using a hint or a titleRsc).
-
Why would I have to guide the shell entirely to its destination when I could simply give it a higher initial velocity? The drag coefficient could remain the same regardless of which propellant I am wanting to simulate:
Charge 7 - initial velocity 'x' so that the shell travels roughly 11,400 meters
Charge 8 - initial velocity 'y' zo that the shell travels roughly 14,000 meters
Et cetera, et cetera...
As we already have found out, even a 0.005 (roughly the point at which an ArmA sqf can just barely complete math equations) loop is not fast enough to lead the shell to where it needs to be. However, changing the initial velocity is quite easy.
- dRb
-
Well, if you plan to adjust initial velocity based on range to be reached, then certainly you dont need anything else.
-
drb-cat,
I have misunderstood your requirements. All you need is the script traj.sqf. Just stuff in the initial velocity you want to use and you will get the shell range including all terrain effects. You don't need anything else. I didn't realize that you were going to twiddle with the shell's initial velocity. I was assuming the initial velocity was to be the config value. You will want to use a function in your fired eventhandler to make sure the shell is reassigned a velocity immediately.
-
The only reason why I wanted to get the velocity of the round from the config is for when the mission editor wishes to use addons that may already be utilizing accurate velocities. Also, it makes testing far easier as we were dealing solely with data ArmA gave us. I swayed from many of my initial plans because of their complexity and just because I knew determining the range would be more simple than obtaining the required angle of launch. Anyway, when I return from the errands I must perform today, I will study more closely Mr.Peanut's traj.sqf and try once more inputting everything that must be put in. I'll return here with the complete script in case I do not have any additional questions. *crosses fingers*
Thank you,
-dRb
-
Here is the trajectory function with a minor bug fix, and a commented example of calling it.
Example
//Example of calling trajectory script. In this case for M119. I know a priori that the drag coefficient is 4.5e-4
//so I will not bother pulling it from config
_muzzleOffset = 1.0; // height from ground to axis about which barrel rotates. My estimate for M119.
_muzzleLength = 2.0; // length of barrel from axis about which barrel rotates to the muzzle mouth. My estimate for M119.
_pos = getPosASL _gun;
_vectorDir = _gun weaponDirection _muzzle;
_elev = 90 - acos ((_vectorDir select 2) /
sqrt ((_vectorDir select 0)^2 + (_vectorDir select 1)^2 + (_vectorDir select 2)^2));//barrel elevation i.e. angle
// between horizontal and barrel
_x = _pos select 0;
_y = _pos select 1;
_theta = (360 + (_vectorDir select 0) atan2 (_vectorDir select 1)) mod 360; // compass heading of your barrel
//get ASL pos of muzzle mouth
_x = _x + _muzzleLength * (cos _elev) * (sin _theta);
_y = _y + _muzzleLength * (cos _elev) * (cos _theta);
_z = _muzzleOffset + (_pos select 2) + _muzzleLength * sin _elev; // ASL height of muzzle
//other parameters
_initSpeed = whatever you want; //M119 config value is 1100 m/s.
_drag = 4.5e-4; // drag for M119 shell
_dt = 0.005; // time increment for calculation. Smaller is more accurate but takes longer to calculate.
_maxRange = 5000; //distance at which to stop bothering with calculation.
//call function. takes about 1 second.
_return = [[_x,_y,_z], _vectorDir, _initSpeed, _drag, _maxRange, _dt] call smb_trajectory ;
_npos = _return select 0; // Final position of shell.
_elapsed = _return select 1; // Flight time
_d = _return select 2; // Straight line distance to target.
Trajectory function
private ["_pos", "_vectorDir", "_vm", "_k", "_max", "_dt", "_gl_start","_gl","_posASL", "_x", "_y", "_z",
"_u", "_v", "_w", "_zASL_Land", "_lastPos", "_vel", "_d", "_elapsed"];
_pos = _this select 0;
_vectorDir = _this select 1;
_vm = _this select 2;
_k = _this select 3;
_max = _this select 4;
_dt = _this select 5;
_gl_start = "logic" createVehicleLocal [0,0,0];
_gl = "logic" createVehicleLocal [0,0,0];
_gl_start setPosASL _pos;
_x = _pos select 0;
_y = _pos select 1;
_z = _pos select 2;
_u = _vm * (_vectorDir select 0);
_v = _vm * (_vectorDir select 1);
_w = _vm * (_vectorDir select 2);
_gl_start setPosASL [_x, _y, _z];
_zASL_Land = -666;
_d = -666;
_lastPos =[];
_elapsed = 0;
while {_z > _zASL_Land and _d < _max} do
{
_lastPos = getPos _gl;
_vel = sqrt(_u*_u + _v*_v +_w*_w);
_u = _u - _k *_u*_vel*_dt;
_v = _v - _k *_v*_vel*_dt;
_w = _w - (_k *_w*_vel + 9.8)*_dt;
_x = _x + _u*_dt;
_y = _y + _v*_dt;
_z = _z + _w*_dt;
_gl setPos [_x, _y, 0];
_d = _gl_start distance _gl;
_zASL_Land = (getPosASL _gl) select 2;
_elapsed = _elapsed + _dt;
//hint format ["%1 %2 %3\n%4 %5 %6\n%7 %8",_x,_y,_z,_u,_v,_w,_d,_zASL_Land];
// sleep 0.5;
};
deleteVehicle _gl;
deleteVehicle _gl_start;
if (_d >= _max) then
{
[nil, _elapsed, _d]
}
else
{
[_lastPos, _elapsed, _d]
};
I have used the most primitive means of integrating the trajectory. I also tried a "leap-frog" type scheme similar to RK2(Runge-Kutta 2), but it did not seem to make much difference. I am presently trying a modified RK4 to see if I can keep the same accuracy but increase the function speed. If you can not stand the hiccup that the function causes, test to see if adding a small sleep in the main loop improves things. I can not remember the status of the sleep command in functions. You could also rewrite the function as a script.
-
I think in OFP you couldn't sleep functions, dunno how it applies to ArmA.
But, well, crud. My internet dies for a week and I miss all the fun :(.
-
The fun is not over yet! :P Although drbob does not require it, I am still plugging away at improving the numerical solver for elevation. The biggest computational demand is the trajectory integration, but reducing the number of iterations to solve for elevation is critical. It is a little confusing, because the trajectory is an initial value problem, but when you take the target into consideration, it bcomes a boundary value problem albeit but one for which the derivatives are only know at t=0.
My first stab involved taking the error in distance and solving for the root using Newton-Raphson. However, the solution does not always converge nicely and I had to scale down the derivative to prevent angles greater than 90 or less than -90 which rapidly blow up. Right now I am usually getting convergence in 10-20 iterations, which is not bad, but I think it should converge faster. The advantage of this method is that I can easily specify to search for either the low or high solution without having to provide bounds.
My second attempt is using RK4 for the trajectory integration. I am hoping that I will be able to increase the integration time step, but I do not know offhand how much performance gain I might get. My bastardised RK2 did not make a significant difference much to my dismay. I will then solve for elevation by searching for a minimum, probably using Brent's method, transliterated from Numerical Recipes (not robust but what the hell. free source code if you know F77).
If you have the time to perform a few quick tests in Mathematica to see if any performance can be gained by these methods, that would be great.
edit: tested RK4 integration in Matlab. It can have a timestep 50 times larger and still work!
edit2: okay. the fun is over now. :shhh: Solving for elevation can be approached as either solving a root, or finding the minimum of a function in a given range of angles. Finding the minimum takes on average 30 calls to the integration function. Solving for the root takes 80. However, finding the minimum requires the user to specify an elevation range, which is problematic.
-
Well, I have finished testing out a slightly modified version of Mr.Peanut's trajectory function...
_gun = _this select 0;
_gType = format ["%1",(typeOf _gun)];
_wep = (getArray (configFile >> "cfgVehicles" >> _gType >> "turrets" >> "mainTurret" >> "weapons")) select 0;
_mag = (getArray (configFile >> "cfgWeapons" >> _wep >> "magazines")) select 0;
_ammo = getText (configFile >> "cfgMagazines" >> _mag >> "ammo");
_gPos = [getPosASL _gun select 0, getPosASL _gun select 1, 0];
_wVec = _gun weaponDirection _wep;
_drag = abs (getNumber (configFile >> "cfgAmmo" >> _ammo >> "airFriction"));
_xPos = (getPosASL _gun) select 0;
_yPos = (getPosASL _gun) select 1;
_zPos = ((getPosASL _gun) select 2) + 3;
_mVel = getNumber (configFile >> "cfgMagazines" >> _mag >> "initSpeed");
_xVel = _mVel * (_wVec select 0);
_yVel = _mVel * (_wVec select 1);
_zVel = _mVel * (_wVec select 2);
_tVel = 0;
_dist = 0;
_time = 0;
_zCheck = 0;
_rPos = [];
_rLogic = "logic" createVehicleLocal [0,0,0];
while {(_zPos > _zCheck)} do
{
_rPos = getPos _rLogic;
_tVel = sqrt ((_xVel ^ 2) + (_yVel ^ 2) + (_zVel ^ 2));
_xVel = _xVel - _drag *_xVel*_tVel*0.005;
_yVel = _yVel - _drag *_yVel*_tVel*0.005;
_zVel = _zVel - (_drag *_zVel*_tVel + 9.8)*0.005;
_xPos = _xPos + _xVel*0.005;
_yPos = _yPos + _yVel*0.005;
_zPos = _zPos + _zVel*0.005;
_rLogic setPos [_xPos, _yPos, 0];
_dist = _gPos distance [getPos _rLogic select 0, getPos _rLogic select 1, 0];
_zCheck = (getPosASL _rLogic) select 2;
_time = _time + 0.005;
};
"tMark" setMarkerPos [_rPos select 0,_rPos select 1,0];
deleteVehicle _rLogic;
titleText [(format ["%1 meters\n%2 seconds", _dist, _time]),"plain down"];
... I have come to the conclusion that finding the required launch angle may not be so bad an idea after all. Sure, the current method is fairly quick and accurate, but because it must be ran multiple times as the gunner adjusts his aim, it becomes tiresome having to press the action button over and over again. Additionally, it is far too demanding on performance to call constantly within even a 0.5 seconds loop. An elevation function, conversely, would only need to be executed one time for the current target and propellant in use. I understand it is more challenging to determine, but we can at least ignore the terrain along the way now. Instead, why don't we just return the two angles for the given target, let him scan over the map, and decide whether to take the high one or the low. *rubs chin* In all honesty, it is up to you at this point, Mr.Peanut. Because your mathematical abilities far surpass my own, it is not much of my position to decide whether one idea is more practical than another.
- dRb
-
Yes. That is what I thought you wanted.
Now here is the odd part. My last attempt for "improvements" fared worse than my previous version even though I tested it rigorously in Matlab before implementing. Very strange. In particular it is terrible for low elevations. It keeps bouncing around the solution but will not converge. At this point I am a bit perplexed and wonder if I might be running into precision problems with ArmA's 4 byte reals. I'll be off for an extra long weekend (Canada Day) so it is unlikely I'll have anything to post until late next week. If I have a chance I'll post a demo of the code so far, in all it's ugly glory, when I get home tonight.
EDIT: Okay here is the previous version before my last round of attempted "improvements". No error handling and code is a hard coded mess. This is more of a proof of concept. Arty angles constantly displayed in hint. Use actions to select a Low or High elevation angle search. Final answer is displayed as titletext: theta, elevation +/- error, distance from target, flight time
It is hard-coded to find an angle that will deliver the M119 shell within 2m of target, without displaying whether the shot is blocked by topography(which it can do but is not implemented). If the shot is impossible, the script fails with an error. The shot will fail if the target is not within range of the M119.
Your target is a fuel truck. I've put it in an interesting place that yields both a high and low elevation solution, but try moving it around in the map. When the truck takes damage a hint displays very briefly before being overwritten.
You will need to crank your mouse sensitivity down to zero to get the angles close enough to hit the target.