control(key id, integer held, integer change)
Once a script has permission to take control inputs from the avatar with the key id (see llRequestPermissions), and has taken control of the desired set (see llTakeControls), this event will be used to pass the state of taken controls to the script.
The held parameter is a bitfield indicating which controls are held down. The change parameter is a bitfield indicating which controls changed their state since the last control event. The combination of these two parameters can be used to determine the various states of any taken controls. The easiest way to do this is by comparing the parameters to pre-defined control constants.
Here are the available control constants:
Constant name | Value | Hex Value | Description |
CONTROL_FWD | 1 | 0x00000001 | Tests for avatar forward control |
CONTROL_BACK | 2 | 0x00000002 | Tests for avatar back control |
CONTROL_LEFT | 4 | 0x00000004 | Tests for avatar move left control |
CONTROL_RIGHT | 8 | 0x00000008 | Tests for avatar move right control |
CONTROL_UP | 16 | 0x00000010 | Tests for avatar up control |
CONTROL_DOWN | 32 | 0x00000020 | Tests for avatar down control |
? | 64 | 0x00000040 | ? |
? | 128 | 0x00000080 | ? |
CONTROL_ROT_LEFT | 256 | 0x00000100 | Tests for avatar rotate left control |
CONTROL_ROT_RIGHT | 512 | 0x00000200 | Tests for avatar rotate right control |
CONTROL_LBUTTON | 268435456 | 0x10000000 | Tests for avatar left button control |
CONTROL_ML_LBUTTON | 1073741824 | 0x40000000 | Tests for avatar left button control with the avatar in mouselook |
The held and change parameters can each have information on multiple controls in any received event. If a control is pressed down the corresponding bit (matching the control constant) in the held parameter will be set to 1. If the state of the control just changed from up to down or down to up, the corresponding bit in the change parameter will be set to 1.
Since we tend to think more of presses, holds and releases, it’s useful to know how they relate. If the bit for a given control is unset (0) in both parameters then it hasn’t been touched and it is up. If the bit in both the held and change parameters is set (1), then the control was just pressed. If held is set and change is not, then the button is being held down. As long as the button is held down additional control events will be generated with the control’s held bit set. If the held bit is unset and the change bit is set, then the control was just released.
So if a ‘1’ indicates the presence of the desired control constant in the parameters (across the top) for each control state (down the side) then:
held | change | |
button not held | 0 | 0 |
button press | 1 | 1 |
button held down | 1 | 0 |
button release | 0 | 1 |
Detecting Controls
There are a few ways you can test for controls in your code. Remembering your bitwise operators you’ll note that & is the intersection. So only bits that are set on both sides of the operator will remain set in the result. This is handy for seeing if the same bit is set in two values. Or(the ‘pipe’ operator |) is the union. All bits set in both arguments are set in the result. This can be useful for combining control constants to check for combinations. Be careful though, checking for multiple bits is tricky (see below).
The following code looks for an initial press of the forward button by and’ing held and change (if it’s held and changed that’s a press) and the desired control constant. Since only a single bit representing forward is set in the CONTROL_FWD constant, and’ing that constant with the two parameters will only result in a TRUE value if that bit is also set in both the held and change parameters (i.e. the button was pressed).
if (held & change & CONTROL_FWD) llOwnerSay("forward pressed");
This code looks for a button release:
if (~held & change & CONTROL_FWD) llOwnerSay("forward released");
The ~ operator is bitwise NOT, i.e. it flips all the bits from 0 to 1 or 1 to 0. If the CONTROL_FWD bit is set in ~held it was unset in the original held, which means it is up (not depressed).
This code looks for a button being held down (and will include the initial press since it’s true then as well)
if (held & CONTROL_FWD) llOwnerSay("forward held");
An alternative style that might be easier for some to understand maps more directly to the control state table above and looks like so:
if ((held & CONTROL_LBUTTON) && (change & CONTROL_LBUTTON)) llOwnerSay("mouse press");
if ((~held & CONTROL_LBUTTON) && (change & CONTROL_LBUTTON)) llOwnerSay("mouse release");
If there’s a 0 in the row you want for one of the parameters put a tilde ~ in front of it in the if statement. If it’s a 1, leave it normal.
Handling Multiple Controls
A single callback to this function can contain information on the status of multiple controls so they can be combined for more detailed actions. This is tricky because you may want to insure that all the bits you’re looking for are set if you’re looking for a “combo” move of more than one key. A statement like the following won’t work as expected:
if (held & change & (CONTROL_FWD | CONTROL_LEFT)) llOwnerSay("forward and/or left"); // Might not be what you want!
The problem is a case where only one of the specified controls is pressed, the forward button for example. Looking at the table at the top, forward has a value of 1. Left has a value of 4 (100 binary). So CONTROL_FWD | CONTROL_LEFT has a value of 5 (101 binary). If the forward key is pressed alone, both held and change will have a value of 1. In this case 1 & 1 & 101 evaluates to 1 because the first bit (1) is set in all 3 arguments and since one is TRUE the combo will be detected.
This is fine if you are looking for any combination of the specified controls, but if you want to make sure that all the desired controls are pressed, your code needs to work like the following:
integer secret_style = CONTROL_DOWN | CONTROL_UP | CONTROL_FWD; if ((held & change & secret_style) == secret_style) { llOwnerSay("five point palm exploding heart technique"); }
This will only evaluate to TRUE if all the bits set in secret_style are present in both held and change. This method allows additional keys
beyond the ones you are looking for to be pressed and the combo will still be detected. If you want to be even more strict use code like this:
if ((held & change) == secret_style) llOwnerSay("very strict");
This will only evaluate to TRUE if exactly the specified controls are pressed and no others.
Asking someone to make sure they press 3 keys at the same instant to get them in one control event may be a bit much. An example that will make sure at least one was pressed and the correct 3 are down looks like:
if ((held & change) && (held == secret_style)) llOwnerSay("ouch");
This is saying, “If any key has been pressed and the keys that are down are exactly the right ones, then cause some pain.”
Parameter Conversion
Dealing with held and change can be hard to keep track of in complex scripts. If you’d rather use the ideas of pressed, down and released you can use code like this to figure out all the logic for you:
control(key id, integer held, integer change) { integer pressed = held & change; integer down = held & ~change; integer released = ~held & change; integer inactive = ~held & ~change; if (pressed & CONTROL_LBUTTON) llOwnerSay("click"); }
Keep in mind that down will not contain keys that have just been pressed even though they are “down”. If you don’t want that just use held.
Mouselook Mode vs. Normal Mode
In mouselook, the default movement keys act a bit different compared to “normal” mode. In ML mode the left mouse button triggers CONTROL_ML_LBUTTON instead of CONTROL_LBUTTON. The left/right keys are now used for strafing, so instead of CONTROL_ROT_LEFT and CONTROL_ROT_RIGHT(for turning), CONTROL_LEFT and CONTROL_RIGHT are triggered.
In “normal” mode CONTROL_LEFT and CONTROL_RIGHT can be triggered by holding down shift and using the left/right keys.
Example
default { state_entry() { llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS); // get permission to take controls } run_time_permissions(integer perm) { // permissions dialog answered if (perm & PERMISSION_TAKE_CONTROLS) { // we got a yes llTakeControls(CONTROL_UP | CONTROL_DOWN, TRUE, FALSE); // take up and down controls } } control(key id, integer held, integer change) { // something happened to one of our controls if (held & CONTROL_UP) { // the "fly up" key is held llSetPos(llGetPos() + <0, 0, 0.25>); // move up } else if (change & held & CONTROL_DOWN) { // the "fly down" key was pressed llSetPos(llGetPos() + <0, 0, -0.25>); // move down } } }
A: If you hold down the shift key and use left or right when NOT in mouselook, or if you move left or right in mouselook. (Shift-A/D when not in mouselook, A/D in mouselook)