Writing Joystick Code: Part 3: Difference between revisions

Jump to navigation Jump to search
m
no edit summary
mNo edit summary
(20 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{WIP}}
{{WIP}}


 
{{JoystickScripting Navigation}}


== Special snippets ==
== Special snippets ==
Line 9: Line 9:


-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
To toggle the Parking brake, use this. Entire code-block shown. Non-repeatable button. (You have to specifically set <repeatable> to true
To toggle the Parking brake, use this. Entire code-block shown. Non-repeatable button. (You have to specifically set <repeatable> to true if you want repeatable. It is assumed to be false.)
if you want repeatable. It is assumed to be false.
<syntaxhighlight lang="xml">
   <button n="1">
   <button n="1">
     <desc>Toggle parking break</desc>
     <desc>Toggle parking break</desc>
Line 18: Line 18:
     </binding>
     </binding>
   </button>
   </button>
</syntaxhighlight>
This method can be used to toggle any property that is on/off. Use Debug - Browse Internal Properties to find suitable candidates.
Also see getProp, setProp and setAll below.
   
   
This method can be used to toggle any property that is on/off. Use Debug - Browse Internal Properties to find suitable candidates.
Remember, it ''toggles'' the property, you can't use it to set the property into a specific state.
Also see getProp, setProp and setAll below.
-----------------------------------------------------------------------------
Remember, it ''toggles'' the property, you can't use it to set the property into a specific state.
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
To make condition work with mixture on a lever (axis). This is pertinent to turboprops.
To make condition work with mixture on a lever (axis). This is pertinent to turboprops.
Change
Change
    controls.mixtureAxis()
<pre style="white-space: pre-wrap;
to
white-space: -moz-pre-wrap;
    controls.mixtureAxis();
white-space: -pre-wrap;
    props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
white-space: -o-pre-wrap;
word-wrap: break-word">
  controls.mixtureAxis()
</pre>
to
<pre style="white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word">
  controls.mixtureAxis();
  props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
</pre>
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
To make condition work with mixture on a non-repeatable button.
Make mixture richer NR
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") < 1 ) {
      setprop("controls/engines/engine/mixture", getprop ("controls/engines/engine/mixture") + 0.05);
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
 
Make mixture leaner NR
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") > 0 ) {
        setprop("controls/engines/engine/mixture", getprop("controls/engines/engine/mixture") - 0.05)
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
Make mixture richer R
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") < 1 ) {
      setprop("controls/engines/engine/mixture", getprop ("controls/engines/engine/mixture") + 0.001);
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
 
Make mixture leaner R
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") > 0 ) {
        setprop("controls/engines/engine/mixture", getprop("controls/engines/engine/mixture") - 0.001)
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
This is an alternative method to adjust a property
This is an alternative method to adjust a property
   <repeatable>true</repeatable>
   <repeatable>true</repeatable>
Line 75: Line 52:
     <step type="double">-2.0</step>
     <step type="double">-2.0</step>
   </binding>
   </binding>
This will continously reduce /sim/current-view/goal-pitch-offset-deg in steps of 2 while the button is held in.
This will continuously reduce /sim/current-view/goal-pitch-offset-deg in steps of 2 while the button is held in.
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
 




Line 84: Line 64:
=== if ... then ... else ===
=== if ... then ... else ===


Sometimes you only want to do something if a certain condition is true (or false.) In this case you use an if clause. The general form is
Sometimes you only want to do something if a certain condition is true (or false.) In this case you use an if clause. The general form in most programming languages is
  if (condition) then action endif
 
: '''if''' '''('''condition''')''' '''then''' action '''endif'''


We write then as {, and endif as }.
In Nasal, we write "then" as "{" and "endif" as "}". Each "action" (or statement) is terminated by a semicolon:


Lets say that if x is less than 5 we want to add 2 to it. We write
<syntaxhighlight lang="nasal">
  if (x < 5) { x = x + 2 }
if (condition)
{ # the opening curly brace means THEN
  action;
} # the closing curly brace means ENDIF, i.e. end of this block
</syntaxhighlight>
 
Actions can be arbitrary Nasal expressions, including function calls and other conditional blocks.
 
==== An example ====
Lets say that if x is less than 5 we want to add 2 to it. We write:
<syntaxhighlight lang="nasal">
if (x < 5) { x = x + 2}
</syntaxhighlight>


The more readable way of writing it is
The more readable way of writing it is
  if (x < 5) {
<syntaxhighlight lang="nasal">
    x = x + 2
if (x < 5) {
   }
  x = x + 2;
}
</syntaxhighlight>
 
And we can even omit the braces if there's only one action:
<syntaxhighlight lang="nasal">
if (x < 5) # no opening brace...
   x = x + 2; # ...so the if statement ends at the first semicolon
</syntaxhighlight>


If we also want to add 1 to y if the condition is true, we write
If we also want to add 1 to y if the condition is true, we write
   if (x < 5) {
<syntaxhighlight lang="nasal">
    x = x + 2;
if (x < 5) {
    y + y + 1
   x = x + 2;
  }
  y = y + 1;
}
</syntaxhighlight>
 
A common way to abbreivate such simple expression, is using shorthand operators like:
 
* +=
* -=
* *=
* /=
 
For example:
 
<syntaxhighlight lang="nasal">
if (x < 5) {
  x += 2; # means x = x + 2
  y += 1; # means y = y + 1
}
</syntaxhighlight>
 


Now lets pretend that we still want to increase x by 2 if it less than 5, and if it is 5 or more we want to add 1 to it. We use else.
Now lets pretend that we still want to increase x by 2 if it less than 5, and if it is 5 or more we want to add 1 to it. We use else.
It looks like this
It looks like this
  if (x < 5) {
<syntaxhighlight lang="nasal">
    x = x + 2
if (x < 5) {
  }
  x = x + 2;
  else {
} else {
    x = x + 1
  x = x + 1;
  }
}
</syntaxhighlight>


Now lets say we have the following rules:
Now lets say we have the following rules:
  If x < 5 add 2 to it
* If x < 5 add 2 to it
  If x < 10 add 1 to it
* If x < 10 add 1 to it
  If x is 10 or more add 7 to it. In this case elseif becomes useful.
* If x is 10 or more add 7 to it.


In English our code would be
In English our code would be
  If (x < 5) then (x = x + 2) else if (x < 10) then (x = x + 1). If none of these are true then (x = x + 7). End of if clause.
* If (x < 5) then (x = x + 2) else if (x < 10) then (x = x + 1).
* If none of these are true then (x = x + 7).
* End of if clause.


We test for x < 5 first. If it fails we test for x < 10. If x , 5 succeeds we do (x = x + 2) and then move on past the end of if clause.
We test for x < 5 first. If it fails we test for x < 10. If x , 5 succeeds we do (x = x + 2) and then move on past the end of if clause.
Line 124: Line 147:


Writing our code properly we get
Writing our code properly we get
  if (x < 5) {
<syntaxhighlight lang="nasal">
    x = x + 2
if (x < 5) {
  }
  x = x + 2;
  elseif (x < 10) {
} elsif (x < 10) {
    x = x + 1
  x = x + 1;
  }
} else {
  else {
  x = x + 7;
    x = x + 7
}
  }
</syntaxhighlight>


You can use more than one elseif, but it gets messy. If you have a number of tests then it is better to test for each case individually.
You can use more than one elsif, but it gets messy. If you have a number of tests then it might be better to test for each case individually.
Easier to understand and much less chance of an error.
Easier to understand each condition separately and much less chance of an error.
Our above example would become
Our above example would become
  if (x < 5) {
<syntaxhighlight lang="nasal">
    x = x + 2
if (x < 5) {
  }
  x = x + 2;
  if (x >= 5) and (x < 10) {
}
    x = x + 1
if (x >= 5) and (x < 10) {
  }
  x = x + 1;
  if (x >= 10) {
}
    x = x + 7
if (x >= 10) {
  }
  x = x + 7;
}
</syntaxhighlight>


Because each if is tested we need to have the (x >= 5) otherwise if x = 1 both the first and second if conditions would be satisfied.
Because each if is tested we need to have the (x >= 5) otherwise if x = 1 both the first and second if conditions would be satisfied.
Line 151: Line 176:
But the advantage is that we are forced to write out each condition exactly as it should be tested. Easier to understand and easier to maintain.
But the advantage is that we are forced to write out each condition exactly as it should be tested. Easier to understand and easier to maintain.


==== Another example ====


Lets say that you want something to happen only if the gear is up. But the only property you can read is Gear-Down. Then you write
Lets say that you want something to happen only if the gear is up. But the only property you can read is GearDown. Then you write
  if (!Gear-Down) {
<syntaxhighlight lang="nasal">
    action
if (!GearDown) {
  }
  action;
}
</syntaxhighlight>


The ! means not. So it translates as: If the gear is not down perform action.
The ! means not. So it translates as: If the gear is not down perform action.
Beacuse we use < and > we will need to enclose it all with CDATA.


See the discussion of variables below for an example of the use of if.
See the discussion of variables below for an example of the use of if.
Line 212: Line 237:
   ]]>
   ]]>


You need CDATa because of the less-than sign.
You need CDATA because of the less-than sign.


You can use this for throttle, propeller, mixture and condition.
You can use this for throttle, propeller, mixture and condition.


BTW Here the props in props.setAll is short for property, not propellers.
BTW Here the props in props.setAll is short for property, not propellers.




Line 316: Line 340:


'''Method 1'''
'''Method 1'''
  <data>
<nowiki>  <data>
     <mode type="int">1</mode>  # Initialise mode to 1
     <mode type="int">1</mode>  # Initialise mode to 1
   </data>
   </data>
Line 322: Line 346:
   <nasal>
   <nasal>
     <script>
     <script>
       <nowiki><![CDATA[
       <![CDATA[
  var self = cmdarg().getParent();
  var self = cmdarg().getParent();
  var data = self.getNode("data");
  var data = self.getNode("data");
  var mode = data.getNode("mode");
  var mode = data.getNode("mode");
        get_mode = func { mode.getValue() }
          get_mode = func { mode.getValue(); }
       ]]></script>
       ]]>
    </script>
   </nasal>
   </nasal>
   
   
  var m  # Used whenever mode is accessed</nowiki>
    </nowiki>


This goes right at the top of your xml file, just after </name>.
This goes right at the top of your xml file, just after </name>.


The code for the button (non-repeatable)that changes mode looks like this
The code for the button (non-repeatable)that changes mode looks like this
   m = get_mode();
   var m = get_mode();
   m = m + 1;
   m = m + 1;
   if (m == 4) {
   if (m == 4) {
Line 343: Line 368:


When a button is pressed, the code looks like this
When a button is pressed, the code looks like this
   m = get_mode();
   var m = get_mode();
   if (m == 1) {
   if (m == 1) {
     ground-action
     ground-action
Line 401: Line 426:
of the levers on the quadrant, I call the variable ''SaitekMultiMode''. Unlikely to be used by the FG programmers, or anybody else.
of the levers on the quadrant, I call the variable ''SaitekMultiMode''. Unlikely to be used by the FG programmers, or anybody else.


=== perIndexAxisHandler ===
This only works with ''real'' axes.
You have a quadrant with 3 levers. Normally they are assigned to controls.throttleAxis(), controls.PropellerAxis() and controls.mixtureAxis().
Now let's say you are flying an aircraft with 2 propeller-engines, it has mixture control, but not propeller-feathering (or you are prepared to use buttons for this).
Now you want the first 2 levers to control the throttles of each engine independently,and the last to do the mixture of both. In this case you would
leave lever 3 assigned to controls.mixtureAxis(), but would use perIndexAxisHandler for the first 2.


-----------------------------------------------------------------------------
It works like this: controls.perIndexAxisHandler(function, engine-number).


The values for function are
  0 Throttle
  1 Mixture
  2 Propeller


Engine-number goes from 0 to 9.


peraxisindexhandler
So in our example, lever 1 would be assigned
  controls.perIndexAxisHandler(0,0)
and lever 2
  controls.perIndexAxisHandler(0,1)


-----------------------------------------------------------------------------


See [http://forum.flightgear.org/viewtopic.php?f=24&t=17851 this post] for an example of using this.


Go back to [[Writing Joystick Code: Part 2]]


Go forward to [[Writing Joystick Code: Part 4]]
You can assign a n axis to more than one engine. Let's say you have 4 engines. You only have 3 levers on the quadrant. You want the
first lever to do the port-outer engine throttle , lever 3 to do the starboard-outer engine throttle, and the middle lever to do the two inboard engine throttles.
So you need to assign the middle lever a list of engines.
Remembering that engines are numbered from 0 upwards for furthest left towards the right, the lever assignments from left to right are
  controls.perIndexAxisHandler(0,0)
  controls.perIndexAxisHandler(0,[1, 2])
  controls.perIndexAxisHandler(0,3)
Notice the list of engine numbers between [ and ] for the middle lever.




Line 419: Line 467:




Any complaints/suggestions/questions/kudos can be posted [http://flightgear.org/forums/viewtopic.php?f=24&t=17892 here].
Any complaints/suggestions/questions/kudos can be posted [http://forum.flightgear.org/viewtopic.php?f=24&t=17892 here].




6

edits

Navigation menu