-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Input Guide
Torque 2D features some very easy and expansive ways to interpret input interaction. They can be categorized into the following:
- Input Event Callbacks
- ActionMaps
- Peripherals
- Motion Controls (iOS/Android only)
These methods handle input interaction from keyboards, mice, joysticks, and touch-based devices. This reference covers and explains these types of interactions and includes basic examples in utilizing them.
An input event callback is simply the response from the T2D engine when an input event has happened.
In T2D, input events are directly handled by the GuiControl which receives them. The most common GuiControl that you will interact with is the SceneWindow, which is the actual GUI object that your game is displayed on. Because of this, the SceneWindow has many functions of its own specifically related to input events. This also lets you can handle multiple SceneWindows in different ways if needed. So when you move, drag, click, release, enter the screen, or leave the screen with your mouse or finger, it will send what is referred to as a "callback" to the SceneWindow. Callbacks are basically preset function names that the engine will call when events happen. This allows you to interact with the engine upon events. Not only does this interaction trigger the callback method, but it passes plenty of useful data to the function, such as the position where the event happened, the click counts if the mouse was pressed as well as any other information that could be useful.
Here is the syntax for an input event callback, which handles left mouse button pressing for Windows and OSX along with touch input on iOS:
function SceneWindow::onTouchDown(%this, %touchID, %worldPosition, %mouseClicks)
{
}
There are three values passed to these callbacks, the first is called "%touchID" which is a unique, numeric ID of the finger touching this object. The "%worldPosition" represents the world position of the mouse or touch event. The final value is fairly evident as well, "%mouseClicks" which represents the number of mouse clicks used. The amount of platforms (Windows, OSX, iOS) your game supports will determine how useful each of these values are.
The following callbacks are available to use:
- onTouchDown
- onTouchUp
- onTouchMoved
- onTouchDragged
- onTouchEnter
- onTouchLeave
- onMiddleMouseDown
- onMiddleMouseUp
- onMiddleMouseDragged
- onRightMouseDown
- onRightMouseUp
- onRightMouseDragged
- onMouseWheelUp
- onMouseWheelDown
- onMouseEnter
- onMouseLeave
As a reminder for the Windows and OSX platforms, left mouse functions are covered with the onTouch... callbacks.
Input Listeners
An input listener is an object that receives any input events that the SceneWindow also receives now or in the future. You can allow any SimObject to be an input listener as follows:
%obj = new ScriptObject()
{
class="ExampleListener";
};
SceneWindow.addInputListener(%obj);
With this, the specific object can now receive the same callbacks as the SceneWindow. If the object is deleted then the object is automatically removed as a listener. If you want an object to stop receiving inputs explicitly:
SceneWindow.removeInputListener(%obj);
You can now define methods for the callback events you need, the same predefined names as the SceneWindow list apply. For example:
function ExampleListener::onTouchUp(%this, %touchID, %worldPosition)
{
}
function ExampleListener::onTouchDown(%this, %touchID, %worldPosition)
{
}
There is no limit to the amount of objects that can act as listeners.
Note: if you have defined the same callback with the SceneWindow and an input listener, both functions will be executed. (i.e. SceneWindow::onTouchDown and ExampleListener::onTouchDown)
Examples
The sandbox and toys that are included in the Torque 2D repository use the input listener system to provide a layered approach to input event handling. The sandbox defines a generic way to pan the scene or manipulate objects that many toys share. Some toys need more specific input event handling though, and those callbacks are then defined within that toy only.
You can find examples of input event callbacks within the following script files:
- Sandbox: manipulation.cs
- CompositeSpriteToy: main.cs
- CompoundObjectsToy: main.cs
- DeathBallToy: main.cs
- MoveToToy: main.cs
- PickingToy: main.cs
- PointForceControllerToy: main.cs
- RotateToToy: main.cs
- ScrollerToy: main.cs
- TruckToy: main.cs
The examples in the previous section showed how to use callbacks at the SceneWindow level. It is also possible to process input events on a per-object basis. This means that individual sprites or other scene objects can react to defined input events. Touching or clicking within that object's bounding box will result in the callback being run.
To turn on SceneWindow event passing:
// Turn on input events for scene objects.
SceneWindow.setUseObjectInputEvents(true);
As a second step, each individual object needs its ability to receive input events turned on. This can be done with .setUseInputEvents():
%object = new Sprite() {class = "ButtonClass";};
%object.setUseInputEvents(true);
You can then use the same callback list as the SceneWindow to create object specific functions.
function ButtonClass::onTouchDown(%this, %touchID, %worldPosition)
{
}
function ButtonClass::onTouchUp(%this, %touchID, %worldPosition)
{
}
Note: the onMouseEnter/onTouchEnter and onMouseLeave/onTouchLeave callbacks are not implemented on SceneObjects.
Consider the following example:
// Make sure that you have turned on object event passing for both the SceneWindow and each individual object
function SceneWindow::onTouchDown(%this, %touchID, %worldPosition)
{
echo("This is the SceneWindow");
}
function AreaX::onTouchDown(%this, %touchID, %worldPosition)
{
echo("This is Area X");
}
function AreaY::onTouchDown(%this, %touchID, %worldPosition)
{
echo("This is Area Y");
}
Referencing the picture above, clicking or touching anywhere in the blue area of the SceneWindow will call the SceneWindow::onTouchDown method. Within the yellow scene object "X", both the SceneWindow::onTouchDown and AreaX::onTouchDown are run. A click or touch within the red scene object "Y" will give you the echo statements in the console for SceneWindow::onTouchDown and AreaY::onTouchDown only. If you delete the SceneWindow::onTouchDown function, you will only receive a callback if you touch or click within either the yellow or red areas. Keep this in mind when writing response callbacks for your input events - Torque 2D gives you complete control over how global or local those callbacks should be.
An ActionMap is an object that you can define from script. These ActionMaps hold key/input binds that give you commands when certain input events happen. These events can be anything from pressing the space bar, up arrow, or "w" key to moving the mouse horizontally or vertically.
At the base of all input is a GlobalActionMap where the bindings are defined which will be used throughout the program. The GlobalActionMap's bindings cannot be overriden by other ActionMaps. For example, a standard GlobalActionMap would allow users to press ~ to bring up the console, press F5 to take a screenshot, etc. These keys would then be unavailable for use by other ActionMaps.
You can "push" to enable these other ActionMaps and "pop" to disable them. This works almost as a layer system, that way when multiple ActionMaps specify commands to be triggered on an event, the most recently pushed one will be the event triggered. Multiple ActionMaps can be enabled at the same time and you can "bind" input commands to an ActionMap in one of three different ways.
The process of using an ActionMap is very simple:
- Create an ActionMap
- Bind a function to a device and an event
- Write the function that uses the input
- Push the ActionMap when you want to use it, pop it when you are done with it
Creating an ActionMap is the same as creating any other object in TorqueScript. You must always provide a unique name for the ActionMap, since you will use it throughout your project. The following code is a quick walkthrough of the process:
Creating a new ActionMap:
To start:
// Create a new ActionMap for input binding
new ActionMap(moveMap);
In the following examples, the unique name is called moveMap. In your own projects, you can name an ActionMap however you wish.
Binding a function to an event:
There are three different ways to bind a device to an event and have it call a function.
moveMap.bind(DEVICE, EVENT, COMMAND);
or
moveMap.bindCmd(DEVICE, EVENT, MAKECMD, BREAKCMD);
or
moveMap.bindObj(DEVICE, EVENT, COMMAND, OBJECT);
The code samples above are a "template". For each input, you need to replace the DEVICE, EVENT, COMMAND, etc variables with the following:
- Device: keyboard, mouse0, touchdevice
- Event: "u", xaxis, touchdown, touchmove or touchup
- Command: A string that contains the name of the function you wish to call
- MakeCmd: A string that contains the name of the function you wish to call (i.e. key pressed)
- BreakCmd: A string that contains the name of the function you wish to call (i.e. key released)
- Object: The explicit object
Activate an ActionMap:
When you are ready to use the action map and its bindings, you need to push it to the system.
moveMap.push();
Deactivating an ActionMap:
If you need to disable the current input scheme, you can call the "pop" function to disable the action map. This does not delete it, but it does stop capturing input.
moveMap.pop();
Removing an ActionMap:
When you are ready to cleanup your game for a shutdown, you can delete the action map.
moveMap.delete();
Using the .bind() Command:
For keyboard events:
moveMap.bind(keyboard, "u", toggleU);
Now we have bound the "u" key to our moveMap with the "toggleU" function to be called when the event is processed. We can set up the toggle function like this:
function toggleU(%val)
{
if(%val)
{
echo("u key down");
} else
{
echo("u key up");
}
}
One very important thing to note is that this toggleU function is set to receive a "%val" value. This is very important, when using the .bind() command you must always set your functions up like this. In the case of a key (like our "u" key example) the %val will only ever be either a 1 (representing the key was pressed) or a 0 (representing the key was released). As you can see we can separate the functionality with a simple if statement.
An non-key example of using .bind() would be this:
moveMap.bind(mouse0, "xaxis", horizontalMove);
This time we aren't binding an event from the "keyboard", instead we chose "mouse0" which represents the first mouse (since T2D can handle input from multiple mice). The event we're binding is "xaxis" and we simply call a "horizontalMove" function. The "xaxis" basically represents the mouse movement in the x-axis. We can set up the horizontalMove function like this.
function horizontalMove(%val)
{
echo("xaxis = " @ %val);
}
In this case we are simply displaying what the value passed to this horizontalMove function is. In the case of our mouse xaxis event the value passed can be a wide range of numbers from negative to positive, in this specific case the value represents the distance moved on this event since the last event, so if you move your mouse suddenly you will get large numbers sent and then it will become small, while if you move it softly then you will only get small numbers. This gives you an extremely accurate way to measure mouse actions (if you aren't using your mouse callbacks on your SceneWindow). In most cases you won't need to bind the mouse actions to an ActionMap, mainly just keys.
Binding touch input to functions:
// Bind core touch reactions to functions
// Function names are completely up to you
moveMap.bind(touchdevice, touchdown, fingerDown);
moveMap.bind(touchdevice, touchmove, fingerMove);
moveMap.bind(touchdevice, touchup, fingerUp);
- touchdown: Activated when a user touches the screen
- touchmove: Activated when a user drags a finger across the screen
- touchup: Activated when a user lifts a finger off the screen
When you write the functions, they should always have the same three parameters:
- %touchIDs - A list of finger IDs, starting at 0
- %touchesX - A list of coordinates in the X-axis, respective to the %touchIDs
- %touchesY - A list of coordinates in the Y-axis, respective to the %touchIDs
For example, let's say three fingers touched down at once. %touchIDs will contain "0 1 2". Depending on where the touches occur, %touchesX will contain coordinates similar to "128 32 480". %touchesY might contain "45 120 320". The first finger, ID 0, uses the coordinates 128 and 45. The second finger uses 32 and 120. The third finger uses 480 and 320.
The following code shows the basic structure of the functions you can use. It is important to note that you can name the functions whatever you like, so long as they have the same parameters.
function fingerDown(%touchIDs, %touchesX, %touchesY)
{
}
function fingerMove(%touchIDs, %touchesX, %touchesY)
{
}
function fingerUp(%touchIDs, %touchesX, %touchesY)
{
}
Using the .bindCmd() Command:
This function works fairly similar to the bind() function, except instead of setting a single function to be called when the event is processed, you set two functions. One will be called when the event is activated (on key down), while the other is activated when the event is broken (on key release). In this case you don't pass just a "function" though; you pass a string that represents script to be run when you activate that event. That means that you can pass a single line if you want to do a single script action, though generally you will simple pass a function call in a string. You can set up the same "u" key like this.
moveMap.bindCmd(keyboard, "u", "onUDown();", "onUUp();");
Notice we wrapped the functions in a string as well as making it a full script call to that function. Now we can have one function do what needs to be done when the "u" key is pressed, while the other can do what is needed when the "u" key is released.
function onUDown()
{
echo("u key down");
}
function onUUp()
{
echo("u key up");
}
Using the .bindObj() Command:
This function follows the format of .bind() almost exactly, except that we have an extra value for the object we are assigning the bind to. A typical use for this is within behaviors:
function moveBehavior::onBehaviorAdd(%this)
{
if (!isObject(moveMap))
return;
moveMap.bindObj(keyboard, "up", moveUp, %this);
moveMap.bindObj(keyboard, "down", moveDown, %this);
moveMap.bindObj(keyboard, "left", moveLeft, %this);
moveMap.bindObj(keyboard, "right", moveRight, %this);
}
Similar to .bind() is the toggle function that is required. Again, we must pass a %val value to this function.
function moveBehavior::moveUp(%this, %val)
{
%this.up = %val;
echo(%this.up);
}
You can use joysticks, gamepads, Xbox360 controllers, or the Leap Motion in your game by applying the same concepts as described above, while keeping in mind a few small differences.
Note that the Windows implementation of gamepad support works correctly as it uses DirectInput. Unfortunately, OSX support for gamepads is not fully implemented at this time.
During the initialization of your game code, make sure to add the following lines to your script files
$enableDirectInput=true;
activateDirectInput();
enableJoystick();
For the Xbox360 controller, add the following lines of script to the startup methods of your game code:
enableXinput();
$enableDirectInput=true;
activateDirectInput();
- All 4 Xinput controllers/devices are referred to in script as gamepad0, gamepad1, etc.
-
When mapping axes (xaxis, yaxis, zaxis, sliders) to either a bind or bindObj function, the corresponding functions' %val will return a value between -x and +x, where x represents the maximum range of the control in either direction. a %val of 0 means the stick is centered.
-
Triggers on an Xbox360 controller (or on other controllers which have "analog" triggers) are a special case.
-
- A %val of 0 means that both triggers are untouched.
-
- A %val of -x means the Left trigger is completely pressed down
-
- A %val of x means the Right trigger is completely pressed down
- You can find all valid controllers and binding strings in ActionMap
Before proceeding with the Torque 2D way of using the accelerometer, it might be very useful to read through Apple's documentation on accessing and using motion data. Specifically, Torque 2D uses the Core Motion system, which was introduced in iOS 4.0.
It is a short read, but very important if you want to know about what kind of data Torque 2D will be giving you. Click here to read more on iOS Motion Controls
Now that you know a little bit more about the iOS motion system, you can make use of it in Torque 2D. The first step is to turn on the iOSMotionManager. Because gathering motion data is CPU intensive and sometimes a battery drain, it is not turned on by default.
To enable the motion system, you can use the following global functions:
// Initialize the iOSMotionManager
initMotionManager();
// Start polling the device motion
startDeviceMotion();
// If the gyroscope is available, enable it
// (only on iPhone 4, iPad 2 and iPod 4th gen and above)
if(isGyroAvailable())
{
// Toggle gyroscope processing
enableGyroscope();
}
// Toggle accelerometer processing, supported by all devices
enableAccelerometer();
Rather than forcing you to write the code from scratch, Torque 2D maps motion input to an ActionMap device. See the ActionMap section of this guide for more information on how to set an ActionMap up.
Binding a function to an event:
moveMap.bind(DEVICE, EVENT, COMMAND);
The above code is a "template". For motion input, you need to replace the DEVICE, EVENT and COMMAND variables with the following:
Accelerometer bindings
- Device: accelerometer
- Event: accelx, accely, accelz, gravityx, gravityy or gravityz
- Command: A string that contains the name of the function you wish to call
Gyroscope bindings
- Device: gyroscope
- Event: gyrox, gyroy, gyroz, yaw, pitch or roll
- Command: A string that contains the name of the function you wish to call
When creating a motion binding, the first parameter should always be accelerometer or gyroscope. The second parameter represents the motion event, such as acceleration or gravity. The third parameter is the name of the function to call.
Accelerometer Events:
- accelx - Instantaneous acceleration in the X-axis
- accely - Instantaneous acceleration in the Y-axis
- accelz - Instantaneous acceleration in the Z-axis
- gravityx - Constant gravity value in the X-axis
- gravityy - Constant gravity value in the Y-axis
- gravityz - Constant gravity value in the Z-axis
Gyroscope Events:
- gyrox - Instantaneous rotation rate on the X-axis
- gyroy - Instantaneous rotation rate on the Y-axis
- gyroz - Instantaneous rotation rate on the Z-axis
- yaw - Constant device yaw, in radians
- pitch - Constant device pitch, in radians
- roll - Constant device roll, in radians
The functions that catch the data can be named whatever you want, but they should always contain a single parameter for the data being passed. For example, the following code shows how to bind the three acceleration events of an accelerometer to simple functions. These functions merely print the data to the console:
Binding motion input to functions:
// Bind core touch reactions to functions
// Function names are completely up to you
moveMap.bind(accelerometer, accelx, "accelerateX");
moveMap.bind(touchdevice, accely, "accelerateY");
moveMap.bind(touchdevice, accelz, "accelerateZ");
Empty bound functions:
function accelerateX(%value)
{
echo("Acceleration on the X-axis is: " @ %value);
}
function accelerateY(%value)
{
echo("Acceleration on the Y-axis is: " @ %value);
}
function accelerateZ(%value)
{
echo("Acceleration on the Z-axis is: " @ %value);
}
NOTE: The only way you can test the motion events is on an actual iOS device. Windows, OS X and iOS simulator do not send the events. Additionally, gyroscope is only available on the following devices:
- iPhone 4 and above
- iPod Touch 4th generation and above
- iPad 2 and above
- iPad mini