franze 196 Posted December 14, 2013 I've gotten questions from a lot of people on how to incorporate an interactive cockpit into their addon that I've determined that it's about time to make a tutorial explaining the mechanics behind it. At it's core it's not that complex, it just requires that the script is able to run faster than the virtual machine scheduler runs most scripts and so must be run on a per frame handler - you can use ArmA3's functionality, CBA, or a custom map mechanic (note that if you're doing this for ArmA2 you can only use CBA or the custom map mechanic; their usage is outside of the scope of this tutorial). I do recommend making some changes to your model's memory LOD which I'll cover here, but I'll also cover mechanics to work without needing to do that. First step: memory LOD in your model. To get more precision and make it easier to track your controls, I recommend creating a set of points to reference key positions in the cockpit. Place your point where you want the control to be in memory LOD and give them a unique name - I used the prefix ctrlref_ for all of my points - and repeat for all the controls you want. Alternative: If you don't want to or cannot make points in the model, you can use an alternative scripting mechanic, which I'll get into later. Second step: The script is the next part and it determines what controls you have, what you want them to do, and what conditions for the interaction. Here is a variation of the script: //if the player is in a vehicle then we don't run this if(vehicle player iskindof "Man") then { _nearestvehs = nearestobjects [player, ["car","tank","boat","plane","helicopter"], 10]; //check for these vehicle classes if(count _nearestvehs < 1) exitwith {}; if(isNil "button_clicked") then {button_clicked = 0;}; //keeps our key from constantly having an effect - that is, press it once and it won't do anything again until released and pressed again _nearestveh = _nearestvehs select 0; _getinpos = _nearestveh modelToWorld (_nearestveh selectionposition "pos driver dir"); //the vehicle's driver point _getinpos = worldtoscreen _getinpos; //convert the above to screen coordinates if(count _getinpos < 2) then {_getinpos = [-100,-100];}; //if the point is outside of our FOV then set it to something ridiculous _getinpos2 = _nearestveh modelToWorld (_nearestveh selectionposition "pos gunner dir"); _getinpos2 = worldtoscreen _getinpos2; if(count _getinpos2 < 2) then {_getinpos2 = [-100,-100];}; _getinpos3 = _nearestveh modelToWorld (_nearestveh selectionposition "pos commander dir"); _getinpos3 = worldtoscreen _getinpos3; if(count _getinpos3 < 2) then {_getinpos3 = [-100,-100];}; _getinpos4 = _nearestveh modelToWorld (_nearestveh selectionposition "pos cargo dir"); _getinpos4 = worldtoscreen _getinpos4; if(count _getinpos4 < 2) then {_getinpos4 = [-100,-100];}; _hinttext = "No Action"; //basic hint to indicate what we're looking at if(_getinpos distance [0.5,0.5] < 0.4) then {_hinttext = "Get in as Driver";}; //change hint to driver if the match is for the driver point if(_getinpos2 distance [0.5,0.5] < 0.4) then {_hinttext = "Get in as Gunner";}; if(_getinpos3 distance [0.5,0.5] < 0.4) then {_hinttext = "Get in as Commander";}; if(_getinpos4 distance [0.5,0.5] < 0.4) then {_hinttext = "Get in as Cargo";}; if(!(_hinttext == "")) then {hintsilent format ["%1",_hinttext];}; //set our hint text if(_hinttext == "No Action") exitwith {}; //if no action available then we don't need to do anything so quit //check to see if our action is within the right parameters and that the key has been pressed, then do something if(inputaction "User20" > 0.5 && button_clicked == 0 && _getinpos distance [0.5,0.5] < 0.4) then { player action ["getInDriver", _nearestveh]; button_clicked = 1; }; if(inputaction "User20" > 0.5 && button_clicked == 0 && _getinpos2 distance [0.5,0.5] < 0.4) then { player action ["getInGunner", _nearestveh]; button_clicked = 1; }; if(inputaction "User20" > 0.5 && button_clicked == 0 && _getinpos3 distance [0.5,0.5] < 0.4) then { player action ["getInCommander", _nearestveh]; button_clicked = 1; }; if(inputaction "User20" > 0.5 && button_clicked == 0 && _getinpos4 distance [0.5,0.5] < 0.4) then { player action ["getInCargo", _nearestveh]; button_clicked = 1; }; //clear our key press if it's not being pressed if(inputaction "User20" < 0.5) then { button_clicked = 0; }; }; In this particular script, interaction is only for man class units and allows one to get into a particular vehicle position based upon distance to a vehicle and whether they are looking at a relevant point on the model (in this case, pos * dir points). It is called by placing the following in an init line: my_clickaction = compile preprocessFileLineNumbers "pfh_click.sqf"; ["my_clickid", "onEachFrame", "my_clickaction"] call BIS_fnc_addStackedEventHandler; Now, instead of a basic getin/getout script for all vehicles, what if you want a cockpit interaction script instead? Similar mechanics apply, we simply change the conditions and results. //if the player isn't in a helicopter then we don't run this if(vehicle player iskindof "Helicopter") then { _heli = vehicle player; if(isNil "button_clicked") then {button_clicked = 0;}; //keeps our key from constantly having an effect - that is, press it once and it won't do anything again until released and pressed again _controlpos = _heli modelToWorld (_heli selectionposition "ctrlref_p_rtrbrake"); //our desired control point _controlpos = worldtoscreen _controlpos; //convert the above to screen coordinates if(count _controlpos < 2) then {_controlpos = [-100,-100];}; //if the point is outside of our FOV then set it to something ridiculous _hinttext = "No Action"; //basic hint to indicate what we're looking at if(_controlpos distance [0.5,0.5] < 0.4) then {_hinttext = "Toggle Engine";}; //change hint to engine on if(!(_hinttext == "")) then {hintsilent format ["%1",_hinttext];}; //set our hint text if(_hinttext == "No Action") exitwith {}; //if no action available then we don't need to do anything so quit //check to see if our action is within the right parameters and that the key has been pressed, then do something if(inputaction "User20" > 0.5 && button_clicked == 0 && _controlpos distance [0.5,0.5] < 0.4) then { if(isengineon _heli) then { player action ["EngineOff", _heli]; } else { player action ["EngineOn", _heli]; }; button_clicked = 1; }; //clear our key press if it's not being pressed if(inputaction "User20" < 0.5) then { button_clicked = 0; }; }; This script is called the same way as the previous one. Now, say we don't have points in the memory LOD we can use. What then? We can use modelToWorld to select an arbitrary model position and use that as a positional reference instead. This method is difficult because you have to trial and error til you get your control to the approximate position you want it in. //if the player isn't in a helicopter then we don't run this if(vehicle player iskindof "Helicopter") then { _heli = vehicle player; if(isNil "button_clicked") then {button_clicked = 0;}; //keeps our key from constantly having an effect - that is, press it once and it won't do anything again until released and pressed again _controlpos = _heli modelToWorld [0,2,0]; //our desired control point tailored for AH-9 _controlpos = worldtoscreen _controlpos; //convert the above to screen coordinates if(count _controlpos < 2) then {_controlpos = [-100,-100];}; //if the point is outside of our FOV then set it to something ridiculous _hinttext = "No Action"; //basic hint to indicate what we're looking at //player sidechat format ["%1",_controlpos]; //debugging - shows position of a control on screen coordinates; the closer to 0.5,0.5, the closer it is to the center of the screen if(_controlpos distance [0.5,0.5] < 0.4) then {_hinttext = "Toggle Engine";}; //change hint to engine on if(!(_hinttext == "")) then {hintsilent format ["%1",_hinttext];}; //set our hint text if(_hinttext == "No Action") exitwith {}; //if no action available then we don't need to do anything so quit //check to see if our action is within the right parameters and that the key has been pressed, then do something if(inputaction "User20" > 0.5 && button_clicked == 0 && _controlpos distance [0.5,0.5] < 0.4) then { if(isengineon _heli) then { player action ["EngineOff", _heli]; } else { player action ["EngineOn", _heli]; }; button_clicked = 1; }; //clear our key press if it's not being pressed if(inputaction "User20" < 0.5) then { button_clicked = 0; }; }; This is all the pure basics of the interaction mechanic we have on the Apache; further items like onscreen hint text, cursors, mouse capability, etc. hinge upon the demand for tutorials of those items. Happy scripting! 1 1 Share this post Link to post Share on other sites
[aps]gnat 28 Posted December 15, 2013 AWESOME Thanks Franze, a timely tutorial :D Share this post Link to post Share on other sites
progamer 14 Posted December 15, 2013 (edited) Thank you for sharing this with us! This should be pinned. But can this be used on other vehicle types? Vehicles like marines could benefit from stuff like this. Edited December 15, 2013 by ProGamer Share this post Link to post Share on other sites
franze 196 Posted December 15, 2013 There's no limitation on what type of vehicle it can be used with; the first example works with all man class units. For the latter two examples you can just replace the first "Helicopter" with "AllVehicles" and it will work with all vehicle types. Share this post Link to post Share on other sites
Redphoenix 1540 Posted December 15, 2013 Franze, I love you. Share this post Link to post Share on other sites
Tisor 17 Posted December 15, 2013 Franze, thank you very much for sharing it with the community. Your work is amazing. We will try to implement that on the Spanish Army Mod, you'll be on the credits for sure. Thank you!!! Share this post Link to post Share on other sites
bakerman 247 Posted December 15, 2013 Thank you so very much! :D Share this post Link to post Share on other sites
abs 2 Posted December 15, 2013 Wahoo! Thanks for sharing your wonderful creation. I'll be using this in my mod as well. :) Abs Share this post Link to post Share on other sites
LCpl Aaron 11 Posted December 16, 2013 would it be possible to modify this to instead of just clicking an action on and off you could control a animation? Im working on a Landing Craft Utility and i want to driver of the boat to able to control the entire duration of the ramps movement so if the terrain is a little more steep, or less, it wouldn't just clip through the ground of raise the LCU out of the water. Share this post Link to post Share on other sites
franze 196 Posted December 16, 2013 would it be possible to modify this to instead of just clicking an action on and off you could control a animation? Im working on a Landing Craft Utility and i want to driver of the boat to able to control the entire duration of the ramps movement so if the terrain is a little more steep, or less, it wouldn't just clip through the ground of raise the LCU out of the water. Yes, that's what this section does: if(inputaction "User20" > 0.5 && button_clicked == 0 && _controlpos distance [0.5,0.5] < 0.4) then { //do whatever we want the click action to do here// button_clicked = 1; }; It's all in how you want the key or key(s) to function. Share this post Link to post Share on other sites
Tisor 17 Posted December 16, 2013 Yes, that's what this section does: if(inputaction "User20" > 0.5 && button_clicked == 0 && _controlpos distance [0.5,0.5] < 0.4) then { //do whatever we want the click action to do here// button_clicked = 1; }; It's all in how you want the key or key(s) to function. I think what Sw4l means is if it is possible to keep press a button on a loop, maybe like: while {button_pressed} do { _phase = _veh animationPhase "ramp"; _phase = _phase + 0.05; _vehicle animate ["ramp", _phase]; } I'm not sure if your code will be able to recognized a button pressed Share this post Link to post Share on other sites
franze 196 Posted December 17, 2013 Yup, all done in that section - a constant loop would be like this: if(inputaction "User20" > 0.5 && _controlpos distance [0.5,0.5] < 0.4) then { _boat animate ["ramp",(_boat animationphase _ramp + 0.01)]; button_clicked = 1; }; Share this post Link to post Share on other sites
teshub 0 Posted December 19, 2013 Hello, After spent / waste almost 6 months of my free time to complete my aircraft A7 CorsairII,I'm falling in love with the Key Interaction/cockpit tutorial. I follow the tut, but I didn't understand how it works: In my aircraft INIT.sqs I put the below line: A7E_clickaction = compile preprocessFileLineNumbers "CHANGEVOUGHT\script\InteractionButton_click.sqf"; //InteractionButton_click = the second script show in tutorial) and ["plane", "onEachFrame", "A7E_clickaction"] call BIS_fnc_addStackedEventHandler; but not works. I've also problem to show the hinttext/worldtoscreen. Help appreciated. Best regards. teshub Share this post Link to post Share on other sites
franze 196 Posted December 19, 2013 Hello,After spent / waste almost 6 months of my free time to complete my aircraft A7 CorsairII,I'm falling in love with the Key Interaction/cockpit tutorial. I follow the tut, but I didn't understand how it works: In my aircraft INIT.sqs I put the below line: A7E_clickaction = compile preprocessFileLineNumbers "CHANGEVOUGHT\script\InteractionButton_click.sqf"; //InteractionButton_click = the second script show in tutorial) and ["plane", "onEachFrame", "A7E_clickaction"] call BIS_fnc_addStackedEventHandler; but not works. I've also problem to show the hinttext/worldtoscreen. Help appreciated. Best regards. teshub For calling from an addon, you need a \ to the script path. The examples given are within a mission framework. So instead of: A7E_clickaction = compile preprocessFileLineNumbers "CHANGEVOUGHT\script\InteractionButton_click.sqf"; You should have: A7E_clickaction = compile preprocessFileLineNumbers "\CHANGEVOUGHT\script\InteractionButton_click.sqf"; Share this post Link to post Share on other sites
teshub 0 Posted December 20, 2013 Thank you sir. :) Share this post Link to post Share on other sites
franze 196 Posted January 4, 2014 I thought I'd update this as I've come across a big error with this mechanic: If your simulation entry is one of the new ArmA3 types - such as helicopterx or planex - there is a positional error induced with the application of velocity to a vehicle. I am not entirely sure the reason for this, but model coordinates are apparently being offset somehow when the velocity of a vehicle changes. I do not know by how much, but it is present and is difficult to correct for. If your simulation type for a vehicle is one of the old types such as helicopter or plane, the error goes away. I assume this is some kind of interference with the PhysX engine since the X at the end of the simulation type most likely indicates PhysX applied to a vehicle. I will try to come up with a velocity correction routine for the future, but just be aware if you're having issues during motion that it has something to do with the simulation types used. Share this post Link to post Share on other sites
progamer 14 Posted January 4, 2014 I thought I'd update this as I've come across a big error with this mechanic:If your simulation entry is one of the new ArmA3 types - such as helicopterx or planex - there is a positional error induced with the application of velocity to a vehicle. I am not entirely sure the reason for this, but model coordinates are apparently being offset somehow when the velocity of a vehicle changes. I do not know by how much, but it is present and is difficult to correct for. If your simulation type for a vehicle is one of the old types such as helicopter or plane, the error goes away. I assume this is some kind of interference with the PhysX engine since the X at the end of the simulation type most likely indicates PhysX applied to a vehicle. I will try to come up with a velocity correction routine for the future, but just be aware if you're having issues during motion that it has something to do with the simulation types used. You should create a feedback tracker ticket for this issue with a repro mission. Share this post Link to post Share on other sites
[aps]gnat 28 Posted January 4, 2014 ....If your simulation entry is one of the new ArmA3 types - such as helicopterx or planex - there is a positional error induced with the application of velocity to a vehicle.... Interesting .... but the first thing I thought of is the pilot animations. Is it really the vehicle or is it distortion caused by the character animation simulating acceleration or deceleration ? If thats a possibility, it is possible to remove those animations (in ArmA2 it was anyway), at least for testing purposes. Share this post Link to post Share on other sites
franze 196 Posted January 5, 2014 The positional error induced by the animations doesn't affect these as the control positions are relative to the model - that is, even if your head moves shifts in X, Y, and Z, their position remains the same. For example, even with 6DOF in TKOH the controls stay in the same place. This is some error induced by the new simulation classes in ArmA3 - if you use the old helicopter, car, plane, etc. simulation types then the error is not present. Create a mission with a MH-9 named heli and a 100cm sphere helper object named cone1, then put this in a radio command: myconepos = { helippos = heli modeltoworld (heli selectionposition "rotor_center"); cone1 setpos helippos; }; ["myconeid", "onEachFrame", "myconepos"] call BIS_fnc_addStackedEventHandler; Observe that the sphere remains centered on the rotor mast until the velocity changes. Then the sphere will start moving in the direction of the velocity instead of maintaining a static position at the rotor mast. Note however that if one uses attachTo, the object attached to the helicopter will maintain it's position; this leads me to believe that there is distortion between model space and world space due to velocity that the modeltoworld command does not account for - when using the x class simulations. ETA: It is possible to correct for some of the velocity distortion, however much precision is still lost using this. _helivel = velocity heli; _helivelcorx = (_helivel select 0) * 0.03334; _helivelcory = (_helivel select 1) * 0.03334; _helivelcorz = (_helivel select 2) * 0.03334; _helippos = heli modeltoworld (heli selectionposition "rotor_center"); _helippos = [(_helippos select 0) - (_helivelcorx),(_helippos select 1) - (_helivelcory),(_helippos select 2) - (_helivelcorz)]; cone1 setpos _helippos; Share this post Link to post Share on other sites
dezkit 28 Posted January 21, 2014 (edited) Very nice tutorial. Is there a way to assign inputaction to primary mouse button? Inputaction "Fire" is only assigned to ctrl+prim mouse btn for some reason. Edited January 21, 2014 by dezkit Share this post Link to post Share on other sites
saine 19 Posted April 8, 2014 I'm new to this and i try to understand this Script since days. 1. my_clickaction = compile preprocessFileLineNumbers "pfh_click.sqf"; ["my_clickid", "onEachFrame", "my_clickaction"] call BIS_fnc_addStackedEventHandler; -- where does this id come from? 2. Maybe it belongs to another thread but i ask here anyway. How do i get an Aimpoint/Crosshair or sth. like that? I tried to find out how Peral did it. But it looks like hieroglyphs for me. 3. I renamed '_controlpos' to '_engine' and added a few more positions but without an indicator wher they are, the positioning is like playing blind man's bluff alone. Thats why i thought about taking this one '_controlpos' and place the buttons with screen coords. I didnt tried it yet and not sure how to realise it. I have so many questions. It would be great to have someone german speaking I can squeeze out :D Share this post Link to post Share on other sites
teeha 22 Posted April 9, 2014 Thanks Franze! Ill definitely have to give this a go once I get a little free time. Share this post Link to post Share on other sites