Writing Joystick Code: Part 1
Before you start, read Input device.
All the examples below assume that you have obtained the correct axes and button numbers as described in the above article. You will have to substitute the correct axis and button numbers in these code examples.
Note the indent in all examples. The indents have no effect on operation, they just make the code so much more readable.
Here is some code to explain a few important basic concepts of xml files. (I have numbered the lines to aid the explanation below. In your xml file the line numbers will not be there.)
<button n="4"> <desc>Gear up</desc> <repeatable>false</repeatable> <binding> <command>nasal</command> <script>controls.gearDown(-1)</script> </binding> <mod-up> <binding> <command>nasal</command> <script>controls.gearDown(0)</script> </binding> </mod-up> </button>
Ignoring the actual meaning of the code, let's got through it line by line.
Line 1: <button> specifies that this is the code for a button, and the n="4" means that this is the code for the button which your system reports as number 4.
Line 2: <desc> A (short) description of what the button does.
Line 3: <repeatable>true</repeatable> means that as long as the button is held in the following action will be executed. If repeatable is false, the button action is one-shot, and won't execute again until the button is released and pressed again.
Notice the </repeatable> after the value true. Each tag (<word> must have a closing tag </word>.
Line 4: <binding> This is the start of the code which defines what happens when the button is pressed. Notice that there can be a few lines before the </binding> tag.
Line 5: <command>nasal</command> means that the following line(s) is(are) written in the programming language nasal. That is all you need to know!!
Line 6: <script>controls.gearDown</script>. The text between <script> and </script> are the nasal commands to be performed. Here there is only one command, but there can be many.
Line 7: </binding> The end of what happens when the button is pressed.
Line 8: <mod-up> (Modification up.) This is the start of the section to handle what happens when the button is released. The up part co9mes from the idea of pushing a button down, and it moves up when you take your finger off it. Not all button actions need a <mod-up> section.
Lines 9 to 13: Same as above, except that it refers to what happens when the button is released.
Line 14: </button> The end of the button section. Not necessary to specify the button number again, as you can't use <button> </button> inside another <button> </button> section.
Axes are the controls on a joystick which con be moved over a whole arc of operation.
Even though the hat hat-switch is classed as an axis, for the purpose of writing code for it, it is a set of buttons. It is, in fact, just a set of buttons.
Aileron, elevator and rudder
The code for these looks like this. The lines in italics are optional. The other lines are compulsory.
<axis n="1"> <desc>Elevator</desc> <dead-band type="double">0.0</dead-band> <binding> <command>property-scale</command> <property>/controls/flight/elevator</property> <offset type="double">0.0</offset> <factor type="double">-1.0</factor> <power type="int">3</power> </binding> </axis>
<desc>Elevator</desc> let's us know that this axis controls the elevator. For human purposes only. All the other lines are for the benefit of FG.
The optional lines need quite a bit of explanation. For a start, all three these axes have a center-zero action. At this point, they have a value of 0. As you move them to their extreme positions, their value gradually increases (decreases) to +1.0 or -1.0, depending on the direction of movement.
<dead-band ..... : You may want a small movement around the center of the axis not to do anything. Or your joystick might be getting old, and it jitters around zero. Jittering is small, continuous value changes in the value. You can then supply a value for dead-band that will eliminate this. And it will be small. A dead-band of 1.0 means that the axis does nothing, a dead-band of 0.25 means that the axis does nothing for the first 25% of its movement.
<offset ..... : If you find that in its center position the axes outputs a non-zero value, you can put that same value here, with opposite + or - sign, to counter-act this. The HUD will show you if you have an offset problem, and can be used to see if you have solved it.
<factor ...... : If you find that moving an axis results in the aileron, elevator or rudder moving in the wrong direction, change the -1.0 to 1.0 or the 1.0 to -1.0. This will make it work the other way round.
<power ...... : Sometimes the axes are too sensitive - a slight touch on aileron makes the plane barrel-roll. You can reduce this sensitivity by making the value of power more than 1. (It is a decimal number, so you can make small adjustments, but small adjustments are usually not enough to be noticible.) Never mind the maths, but the larger the value of power the less sensitive the aircraft will be to axis movement.
Remember: (property^power + offset) * factor = result. See Bindings, property-scale.
|Note In some joystick binding files, you'll find:
This is equivalent to:
and not faster in any way, therefore I prefer using power since it's then easier to change the value if needed.
For aileron and rudder you replace >/controls/flight/elevator with /controls/flight/aileron and /controls/flight/rudder. Don't forget to change the the name between <desc> and </desc>.
Throttle, propeller-pitch, mixture and carburetor-heat.
These do not have optional settings, after all you want a smooth linear response. This is the code for the throttle control, for the others, just replace throttleAxis with propellerAxis, mixtureAxis or carbHeatAxis.
<axis n="2"> <desc>Throttle</desc> <binding> <command>nasal</command> <script>controls.throttleAxis()</script> </binding> </axis>
Here, the "instructions" we give are in the Nasal language, so we have
<command>nasal</command> to let the interpreter know.
The actual "instruction" is between the <script> and </script> tags.
There is almost no end to what we can do with buttons, limited usually by the number of buttons on the joystick, what we need, and what we can remember of the allocation of button actions.
There are two categories of buttons, repeatable and non-repeatable. With non-repeatable buttons, when we push it, a single action is carried out, and nothing else happens. An example of its use would be a button to lower the landing-gear. We press the button, the gear goes down. With repeatable buttons, when we hold the button in, the same action is carried out over and over, until we release the button. An example is zoom-in. We press the button, and the view keeps zooming in. When we have the view we want, we release the button, and zooming stops. An important point to remeber is that if a button is repeatable, even if you just give it the briefest of touches, the instruction will be carried out a few times, thanks to the repeat-rate of the system. Setting a button to repeatable or non-repeatable is part of the basic definition of the button (like its number), it cannot be changed willy-nilly. So when you decide on the use for joystick buttons, you decide which will be repeatable and which not.
The shell of a button definition looks lke this
<button n="0"> <desc>What the button does</desc> <repeatable>false</repeatable> <binding> <command>nasal</command> <script>Your code goes here</script> </binding> <mod-up> <binding> <script>Code for what happens when the button is released goes here</script> </binding> </mod-up> </button>
This is a non-repeatable button
To make a button repeatable, replace false with true.
You can use this shell for all your buttons. Just use the correct button number, give the correct <desc>, decide repeatable or non-repeatable, and then type the correct code in place of Your code goes here and Code for what happens when the button is released goes here. Not all actions need a <mod-up> section, but you will soon get the hang of what does and what doesn't.
If your code has more than one command, then do this
<script> Command number 1; Command number 2; Command number 3 </script>
Note that all lines except the last have a ; at the end. This lets the interpreter know that you haven't finished yet.
A note on <mod-up>
I have been having a good look at the controls.nas file that comes with FG. With most of them, where you would use
to do something, you are allowed to use
in <mod-up>, but it does nothing. Xml files are full of such code, which is totally, unnecessary. It does no harm, just wastes time, albeit very little time. In fact, the only time a <mod-up> is needed is when a non-repeatable button starts an ongoing process, and we want to stop that process when we release the button. In Part2 and Part 3 you will see almost no <mod-up> code at all.
One advantage of leaving out unnecessary <mod-up> code is that it makes the code easier to read. (And saves a couple of trees when the files are printed.)
There is a limit to the number of buttons on the joystick and lots of things we'd like to do with them. Fortunately, FG makes it easy for us. It lets us know if any or more than one of the Shift, Alt and Ctrl buttons are pressed. We can thus modify the action of a button with the <mod-shift>, <mod-ctrl> and <mod-alt> tags.
Here is an example of what you need to modify the action of a button when the Shift key is held in when you press the button. I have deliberately included <mod-up> tags so that you can see where they must go, should they be necessary.
<button n="0"> <desc>Normal action/Shift action</desc> <repeatable>false</repeatable> <binding> <command>nasal</command> <script>Normal: button pressed</script> </binding> <mod-up> <binding> <script>Normal: button released</script> </binding> </mod-up> <mod-shift> <binding> <command>nasal</command> <script>Shift: button pressed</script> </binding> <mod-up> <binding> <script>Shift: button released</script> </binding> </mod-up> </mod-shift> </button>
Notice that the <repeatable> tag is right at the top. You cannot make the normale action repeatable and the modified action non-repeatable.
Now let's go crazy. We want:
1. Normal action. 2. Action when Ctrl is pressed. 3. Action when Ctrl and Alt are pressed.
Looks like this.
<button n="0"> <desc>Normal action/Ctrl action/Ctrl-Alt action</desc> <repeatable>false</repeatable> # ***** Normal ***** <binding> <command>nasal</command> <script>Normal: button pressed</script> </binding> <mod-up> <binding> <script>Normal: button released</script> </binding> </mod-up> # ***** Ctrl ***** <mod-ctrl> <binding> <command>nasal</command> <script>Ctrl: button pressed</script> </binding> <mod-up> <binding> <script>Ctrl: button released</script> </binding> </mod-up> # ***** Ctrl-Alt ***** <mod-alt> <binding> <command>nasal</command> <script>Ctrl & Alt: button pressed</script> </binding> <mod-up> <binding> <script>Ctrl & Alt: button released</script> </binding> </mod-up> </mod-alt> </mod-ctrl> </button>
Notice four things:
1. The <mod-alt> selection is between the <mod-ctrl> and </mod-ctrl> tags. This means that the code is for Ctrl and Alt, not just Alt. 2. I have spaced the sections with empty lines for readability. 3. I have used comments # ***** Comment ***** to make it even more readable. 4. By using comments and open lines, the block of code is a lot less daunting. In fact, once you have grasped the basics, it is quite easy to read.
Anything that starts with # is ignored by the interpreter, even if it comes after some code. Such as
<script>controls.throttleAxis</script> # Controls the throttle axis
The code will work and we humans have an explanation of the code.
To write long comments you can do this
<!-- Lots and lots and even more lots of lines of comments. -->
You can also use the function keys as modifiers: <mod-F1>, <mod-F2>, etc., but remember that with all the combinations of Shift, Ctrl and Alt you get 7 options, and that is a lot of options to remember for each button.
In a later section I will show how to use buttons on the joystick as modifiers.
If your code (or even comments) contains <, >, & or --, it can cause havoc with your xml file. Any area that contains any of these must be enclosed between
<![CDATA[ and ]]>
Any complaints/suggestions/questions/kudos can be posted here.