jwllorens 43 Posted July 25, 2016 In ArmA 3, bullets inherit the velocity of the vehicle that fires them. This means that when you are in the turret of a fast moving aircraft, it can be pretty frustrating to hit a target on the ground. To start with, your crosshairs are swinging wildly with the motion of the aircraft. Combine this with the fact that the bullets fired from the turret wont hit where the crosshairs are aiming, and it's a real party. If you have played on the new Tanoa King of the Hill map and been a gunner in a Blackfish, you know what I am talking about. If the Blackfish is doing anything but hovering, you wont hit your target. Forget loitering in a circle. Now, I am no military expert, but I am sure that advanced modern fire control computers can account for this and adjust the crosshairs. I may be wrong, but either way, being able to hit stuff is more fun that not being able to. So I wrote a little function and thought I would share it. It works perfectly. No more shells hitting 6 miles off to the right of where you were aiming. You still need to get your zeroing correct though. /*disables projectiles inheriting vehicle velocity to allow for easier aiming of aircraft turrets while vehicle is moving. arguments: vehicle(obj), enabled(bool) return: true Can be turned on and off by passing true or false in the second argument. Useful if you want to dynamically enable or disable this functionality. Reccomended for use on Blackfish, Blackfoot, Kajman, and Xi'an gunships. */ params [ ["_veh", objNull, [objNull], 1], ["_act", false, [true], 1] ]; _var = _veh getVariable "JWL_vars_disableInheritedVelocity"; if (_act) then [{ _ind = _veh addEventHandler ["Fired",{ _ammo = (_this select 4); _path = (configFile >> "CfgAmmo"); if ((_ammo isKindOf ["BulletCore", _path]) || (_ammo isKindOf ["ShellCore", _path])) then { (_this select 6) setVelocity ((velocity (_this select 6)) vectorDiff (velocity (_this select 0))); }; }]; _veh setVariable ["JWL_vars_disableInheritedVelocity",_ind,true]; },{ if (!(isNil "_var")) then { _veh removeEventHandler ["fired",_var]; _veh setVariable ["JWL_vars_disableInheritedVelocity",nil,true]; }; }]; true To use it, simply pass it a vehicle object. Bullets fired from the vehicle will now hit what they are aimed at. For example: [myVehicle, true] call DisableInheritedVelocity.sqf; If anyone wants to take a crack at optimizing it, please do. 2 Share this post Link to post Share on other sites
johnnyboy 3797 Posted July 26, 2016 Slick. I like it! Share this post Link to post Share on other sites
jwllorens 43 Posted July 26, 2016 Alright I polished it up a bit and fixed some bugs. I tested it in several vehicles. It works AMAZINGLY WELL in gunships. Instead of guessing where the rounds will fall (especially if you have an AI pilot that likes to fly low and fast over the targets) you simply aim where you want them to hit. This script will simply subtract out the velocity of the vehicle from the initial velocity of each projectile, meaning it travels with a muzzle velocity as if the vehicle was stationary when it was fired. The bullets still travel completely normal, and it looks perfectly believable. The only difference is that the bullets land wherever the crosshair was pointed when it was fired. Is it realistic? No, because in real life bullets inherit the velocity of the firing platform. But it still looks very believable and achieves the same effect as an imaginary advanced ballistic computer using an accelerometer to adjust the crosshairs on the fly. I just started this scripting stuff not too long ago so I had to try my hand at making this function a simple toggle on/off. I think I did alright with the use of setVariable to store the index of the event handler. You can call the function from different scripts on the same object and it will always keep track of whether it has already been enabled or not and be able to disable it even if it was enabled earlier somewhere else. I do have a question about event handlers and object locality. Will this event handler fire on all computers? My understanding is that in multiplayer, bullets and shells are not updated across the network, instead they are just "duplicated" by creating a different bullet on every PC and giving them the same starting position and velocity. As such, I want to make sure the event handler in this script is firing on all PCs so that the gunner isn't getting direct hits while the target is getting killed by shells that he is seeing land miles away. Share this post Link to post Share on other sites
kremator 1065 Posted July 28, 2016 Would it be possible to put this into an addon? I'd love to have it as one of my defaults :) Share this post Link to post Share on other sites
theend3r 83 Posted July 28, 2016 Seems like a pretty bad solution (although a solution nonetheless). I love what they did with the Wipeout - providing you with a indicator of expected hit location. A function like that for all weapons would be incredible, not only for vehicles but also for training with snipers, RPGs and much more. Share this post Link to post Share on other sites
jwllorens 43 Posted July 28, 2016 Seems like a pretty bad solution (although a solution nonetheless). I love what they did with the Wipeout - providing you with a indicator of expected hit location. A function like that for all weapons would be incredible, not only for vehicles but also for training with snipers, RPGs and much more. The CCIP (continuously computed impact point) given to aircraft weapons is nice because it is hardcoded in the engine. However, a scripted version of this has a LOT more overhead because it must run on a loop, hence the name "continuously computed." CCIPs are pretty math heavy too, you have to keep getting the gun direction, then know the muzzle velocity and air resistance (which in ArmA is modeled as a constant acceleration I believe, rather than a function of velocity, which means you can avoid the calculus but it is still very math heavy). Then you calculate an estimated time of flight by assuming an impact position, use that to calculate an actual impact point, then use that impact point to calculate time of flight again, ect. Making an accurate, smooth CCIP is tough. It is certainly possible, however. The reason I like this approach is because it is simple, the only code that gets run repeatedly and often is one single line, and 99.9% of people will see it in action and never know that it is "breaking physics." In fact, I can't think of another game that doesn't do it like this anyways because the CPU time required to have realistic ballistic physics AND still give the player a good experience by giving them the tools to hit what they aim at is just too much and isn't visually worth it. ArmA is the only game I know of that does have projectiles inherit the velocity of the firing vehicle. Anyways, I updated the function, and broke it into two parts. I don't know much about multiplayer and can't test some of this stuff since I am going at it alone. But I wasn't sure if event handlers could only be removed by their index on the PC where they were added. So I rewrote the script to use a new function that I made that keeps track of event handlers added through it and makes sure they stay on the computer where the object they are added to is local, and nowhere else. This is particularly useful for enabling or disabling some functions that rely heavily on adding and removing event handlers dynamically, and being able to always turn them on or off from anywhere even if the object has changed locality. But again, I don't know if it is necessary, maybe you can call addEventhandler on one PC and call removeEventhandler on another and it will work and so all this is pointless. It does work in single player just fine though. fn_disableInheritedVelocity.sqf (ex: usage, then the function code) [_object, true] RemoteExecCall [JWL_fnc_disableInheritedVelocity, _object]; //or to disable the effect: [_object, false] RemoteExecCall [JWL_fnc_disableInheritedVelocity, _object]; params [ ["_obj", objNull, [objNull], 1], ["_act", true, [true], 1] ]; if (_act) then [{ [_obj,"JWL_vars_disableInheritedVelocity","ADD","Fired",{ _ammo = (_this select 4); _path = (configFile >> "CfgAmmo"); if ((_ammo isKindOf ["BulletCore", _path]) || (_ammo isKindOf ["ShellCore", _path])) then { (_this select 6) setVelocity ((velocity (_this select 6)) vectorDiff (velocity (_this select 0))); }; }] call JWL_fnc_migratoryEventHandler; },{ [_obj,"JWL_vars_disableInheritedVelocity","REMOVE","Fired"] call JWL_fnc_migratoryEventHandler; }]; fn_migratoryEventHandler.sqf (ex. usage, then function code) [_object, _uniqueEventHandlerName, "ADD", _eventHandlerType, _eventHandlerCode] call JWL_fnc_migratoryEventHandler; //or to remove the event handler: [_object, _uniqueEventHandlerName, "REMOVE", _eventHandlerType] call JWL_fnc_migratoryEventHandler; params [ ["_obj", objNull, [objNull], 1], ["_name", "JWL_vars_DefaultMigratoryEH", [""], 1], ["_act", "REMOVE", [""], 1], ["_type", nil, [""], 1], ["_code", {}, [{}], 1] ]; if (!(isNil "_type")) then [{ switch (_act) do { case "ADD": { _var = _obj getVariable _name; if (isNil "_var") then [{ _idx0 = _obj addEventHandler [_type,_code]; _idx1 = _obj addEventHandler ["Local",(compile (format [" if (!(_this select 1)) then { _obj = (_this select 0); _var = _obj getVariable ""%1""; _obj setVariable [""%1"",nil]; _prms = _var select 0; _idxs = _var select 1; _obj removeEventHandler [(_prms select 0),(_idxs select 0)]; _obj removeEventHandler [""Local"",(_idxs select 1)]; [(_this select 0),""%1"",""ADD"",(_prms select 0),(_prms select 1)] remoteExecCall [""JWL_fnc_migratoryEventHandler"",_obj]; }; ",_name]))]; _obj setVariable [_name,[[_type,_code],[_idx0,_idx1]]]; },{ diag_log (format ["JWL_fnc_migratoryEventHandler ERROR: Migratory event handler name ""%1"" already in use.", _name]); }]; }; case "REMOVE": { _var = _obj getVariable _name; if (!(isNil "_var")) then [{ _obj removeEventHandler [_type,((_var select 1) select 0)]; _obj removeEventHandler ["Local",((_var select 1) select 1)]; _obj setVariable [_name,nil]; },{ diag_log (format ["JWL_fnc_migratoryEventHandler ERROR: Migratory event handler name ""%1"" does not exist. Cannot remove.", _name]); }]; }; default { diag_log "JWL_fnc_migratoryEventHandler ERROR: Must specify ""ADD"" or ""REMOVE""."; }; }; },{ diag_log "JWL_fnc_migratoryEventHandler ERROR: Must specify migratory event handler ""TYPE"" to add or remove."; }]; I don't know if it is necessary to do this but I like the peace of mind of just calling disableInheritedVelocity like that from the server, and it will always add or remove the event handlers on the computer where the object is local, and the event handlers automatically move to the new machine if ownership changes (as it does with vehicles often). I think doing it this way might save on some network traffic as well compared to using addeventHandlerMP. Share this post Link to post Share on other sites