Tutorial: Multi-user Multitouch Move, Rotate & Scale
Introduction
Learn how to dynamically drag, rotate and scale multiple objects on stage using Touch Drag, Gesture Rotate and Gesture Scale events to create a multi-gesture, multi-user application. In this tutorial, we’ll place two photos on stage and attach event listeners to each, allowing both objects to be scaled, rotated & dragged simultaneously. This tutorial requires Adobe Flash CS4 and GestureWorks (download a free trial).
Creating Your Document Class
The document class also known as the “Application” class is the root object class for your application. The primary purpose of the document class is to extend the main timeline; it does this in the form of a custom MovieClip. This means that all other objects in your application are placed within it.
To set the document class for your application, open mainApp.fla. Then, in the main menu, go to File > Publish Settings. In the Flash tab, select the Script “settings” button. Then, in the “Advanced Actionscript 3 Settings” window, set the “Document Class” to “Main“. Press the “OK” button and apply the changes to publishing settings.
In the main menu, go to: File > New. Then, in the “New Document” window, select “Actionscript File” and press the “OK” button. Save the file as “Main.as” and make sure it is saved in the same folder as your application file.
In the first line of your ActionScript file, add the following code to open a new package structure:
1 | package { |
This defines the beginning of the root package which defines the code used in your root class “Main”. This tells Flash what script to include when extending “Main”.
Importing Flash Libraries
In this example, we’re going to load an image into our Flash application. To do this, we need to import the Loader class library into the package of our root class. Essentially this allows Flash to correctly reference and retrieve all the object resources that may be needed when using the Loader class.
2 | import flash.display.Loader; |
As is the case with loading other media into flash, the loader class uses the URLRequest class, so the library files for that class must also be imported.
3 | import flash.net.URLRequest; |
Since we will be using the Loader class to load external media, we will need the ability to track events that occur within the Loader object. To do this, we need to import “Event” libraries, which will allow us to attach listeners to our Loader and find out when loading is complete.
4 | import flash.events.Event; |
Importing GestureWorks Libraries
Importing the “Application” package instantiates libraries and builds a framework of control structures for managing multitouch events and methods for object tracking within Flash. To edit these settings, open “How to Customize Application.xml”
6 | import id.core.Application; |
A display level class must be set up to be used for stage objects. To do this, we import the TouchSprite class libraries. TouchSprite builds multitouch onto the Sprite class. This means that TouchSprite inherits all the properties of Sprite and adds new event targeting features that act systematically to determine multitouch event ownership at the object level.
7 | import id.core.TouchSprite; |
Since we are going to be using touch events and gesture events, we’ll need to import these classes into our file.
8 | import gl.events.TouchEvent |
As with importing other packages, importing the TouchEvent and GestureEvent packages provides a reference to all available touch and gesture analysis modules via their class names.
9 | import gl.events.GestureEvent |
Creating Your App
In order to build our multitouch application, we are going to create what is essentially a custom class in Flash. We begin this process by extending the “Application” class & setting up our new class to automatically inherit a multitouch framework that will manage touch events behind the scenes.
11 | public class Main extends Application { |
In most cases, the document class should be defined as public so that it can be called (and instances created) from outside the package.
13 | public function Main() { |
Next we need to create a new TouchSprite object to hold our loaded image. This is done to ensure that our image correctly responds to touch.
15 | var orchidTouchSprite = new TouchSprite(); |
In order to load our image into our flash application, we are going to use the Loader class. To do this, we are first going to create a new variable of type Loader and instantiate it as a Loader() object.
16 | var orchidLoader = new Loader(); |
Before the Loader object can do anything, it must be pointed to a source file of some kind. To do this, we can use the URLRequest class to load the text that points the loader to our local JPG file. (Note:The loader class can also load PNG, GIF or SWF files into flash).
17 | orchidLoader.load(new URLRequest("orchid.jpg")); |
We need to know when the loader has finished loading our image, so we can attach an event listener to call a function when loading is “complete”.
18 | orchidLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,loaderComplete); |
The touchSpirte is then positioned at a random location on stage. In this example the stage size is 900×600. The horizonal position of the TouchSprite is set between 0 and 900px, then the vertical value is set between 0 and 600px.
20 21 | orchidTouchSprite.x = Math.random()*stage.stageWidth; orchidTouchSprite.y = Math.random()*stage.stageHeight; |
The orientation of the touchsprite is also set to a random angle between 0 and 360 degrees. This method makes the images appear randomly positioned and oriented when they appear on stage, as if thrown.
22 | orchidTouchSprite.rotation = Math.random()*360 |
To make sure that all touch points placed over the container object are explicitly associated with that object, we need to set the “blobContainerEnabled” property to true.
23 | orchidTouchSprite.blobContainerEnabled = true; |
Next we attach the TouchEvent listeners to the touchSprite() object so that it can be dragged around the stage area. The first listener calls the function “startDrag_Press()” each time a TOUCH_DOWN event is detected and the second calls “stopDrag_Release()” when a TOUCH_UP event is detected.
25 26 | orchidTouchSprite.addEventListener(TouchEvent.TOUCH_DOWN, startDrag_Press); orchidTouchSprite.addEventListener(TouchEvent.TOUCH_UP, stopDrag_Release); |
Attach a GESTURE_ROTATE event listener to your TouchSprite() object. This calls the function “gestureRotateHandler()” each time a GESTURE_ROTATE event is detected on “orchidTouchSprite”.
27 | orchidTouchSprite.addEventListener(GestureEvent.GESTURE_ROTATE, gestureRotateHandler); |
Since we want to be able to change the scale of our image, we must listen for a “scale” or “pinch” gesture on the object and call a specific function that can handle scale changes when the scale event is triggered. To accomplish this, we simply add a “GESTURE_SCALE” listener to the “container” TouchSprite. This listener will call the function “gestureScaleHandler” each time a “scale” gesture occurs on the container object.
28 | orchidTouchSprite.addEventListener(GestureEvent.GESTURE_SCALE, gestureScaleHandler); |
Next, we nest the “orchidLoader” object inside the TouchSprite() using the addChild() method.
29 | orchidTouchSprite.addChild(orchidLoader); |
We then add the container “orchidTouchSprite” to the stage so that it is added to the display list and all associated nested content is drawn on screen at runtime.
30 | addChild(orchidTouchSprite); |
In this example, we are creating a multi-user multi-touch application. To create multiple independent touchable objects in Flash, we simply create more TouchSprite objects in same way as the first.
Initialize the container objects.
32 33 | var beeTouchSprite = new TouchSprite(); var beeLoader=new Loader(); |
Prepare the loader object to load a specified image file.
34 | beeLoader.load(new URLRequest("bee.jpg")); |
Add the load “COMPLETE” listener to “bbLoader”.
35 | beeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,loaderComplete); |
Set the position and orientation of “beeTouchSprite” when it is initialized on stage.
36 37 38 | beeTouchSprite.x = Math.random()*stage.stageWidth; beeTouchSprite.y = Math.random()*stage.stageHeight; beeTouchSprite.rotation = Math.random()*360; |
Enable local blob containment on “beeTouchSprite”.
39 | beeTouchSprite.blobContainerEnabled = true; |
Add the touch and gesture listeners to “beeTouchSprite” object.
41 42 43 44 | beeTouchSprite.addEventListener(TouchEvent.TOUCH_DOWN, startDrag_Press); beeTouchSprite.addEventListener(TouchEvent.TOUCH_UP, stopDrag_Release); beeTouchSprite.addEventListener(GestureEvent.GESTURE_ROTATE, gestureRotateHandler); beeTouchSprite.addEventListener(GestureEvent.GESTURE_SCALE, gestureScaleHandler); |
Nest “beeLoader” inside “beeTouchSprite”.
45 | beeTouchSprite.addChild(beeLoader); |
Add the “beeTouchSprite” to the stage.
46 | addChild(beeTouchSprite); |
In this example, we load two objects onto the stage. Each object has been set to call the function “loaderComplete()” when its image data has been loaded into its loader object. This simplifies the code by using a single function that can be re-used as many times as is needed. Once an image is loaded, it is shifted so that the reference point is in the center of the image.
When the loading of our image file is complete, the function “completedImageLoad” shifts the image so that the point of origin/reference is in the middle of the image.
49 50 51 52 | private function loaderComplete(e:Event):void { e.target.loader.x = -e.target.width/2; e.target.loader.y = -e.target.height/2; } |
Using the “one function for many objects” model, we’re also able to use one function to control what happens when a user touches either of our TouchSprite objects. To enable touch drag, each time the TOUCH_DOWN event is triggered the “startTouchDrag()” method is called.
When TOUCH_DOWN is dispatched, we want to begin dragging our object on stage. To do this we enable startTouchDrag(). This method behaves in a similar way to the startDrag() method; however, this touch method works by updating the position of the object every time the touch point above it moves.
54 55 56 | private function startDrag_Press(e:TouchEvent):void { e.target.startTouchDrag(); } |
The same logic is then applied to releasing the objects. When a TOUCH_UP event is triggered on one of the TouchSprite objects, the “stopTouchDrag()” method is called.
57 58 59 | private function stopDrag_Release(e:TouchEvent):void { e.target.stopTouchDrag(); } |
Gesture events can be treated the same way as touch events. Both TouchSprite objects call the same function “gestuerRotateHandler()” each time a GESTURE_ROTATE event is dispatched. Every time a GESTURE_ROTATE event is dispatched from your TouchSprite, a value is returned which measures the calculated change in orientation (rotation) of the cluster of touch points on your touch object. This value is used to incrementally change the angle of orientation of “container”, adding the change to the previous value each time the event is detected.
61 62 63 | private function gestureRotateHandler(e:GestureEvent):void { e.target.rotation += e.value; } |
In addition to rotate, each time the “scale” gesture is detected on our image, and therefore on the “container” object, a GESTURE_SCALE event is fired. This is detected by the gesture listener we placed on the container object and calls the function “gestureScaleHandler”. Each time this occurs, a value for the change in scale is taken from the event and used to incrementally change the scale value of the target (in this case the “container”.)
65 66 67 68 | private function gestureScaleHandler(e:GestureEvent):void { e.target.scaleX += e.value; e.target.scaleY += e.value; } |
In summary, this custom class creates two TouchSprite objects each with an image from the library nested inside and then randomly placed and oriented on the stage. Each one of the TouchSprite objects can be independently dragged, dropped, rotated and scaled by multiple users. In this example, the center of rotation and center of scale of the touch object is determined dynamically by the relative position of the points used in the gesture.
Complete Example Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package { import flash.display.Loader; import flash.net.URLRequest; import flash.events.Event; import id.core.Application; import id.core.TouchSprite; import gl.events.TouchEvent; import gl.events.GestureEvent; public class Main extends Application { public function Main() { var orchidSprite = new TouchSprite(); var orchidLoader=new Loader(); orchidLoader.load(new URLRequest("orchid.jpg")); orchidLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,loaderComplete); orchidSprite.x = Math.random()*stage.stageWidth; orchidSprite.y = Math.random()*stage.stageHeight; orchidSprite.rotation = Math.random()*360 orchidSprite.blobContainerEnabled = true; orchidSprite.addEventListener(TouchEvent.TOUCH_DOWN, startDrag_Press); orchidSprite.addEventListener(TouchEvent.TOUCH_UP, stopDrag_Release); orchidSprite.addEventListener(GestureEvent.GESTURE_ROTATE, gestureRotateHandler); orchidSprite.addEventListener(GestureEvent.GESTURE_SCALE, gestureScaleHandler); orchidSprite.addChild(orchidLoader); addChild(orchidSprite); var beeSprite = new TouchSprite(); var beeLoader=new Loader(); beeLoader.load(new URLRequest("bee.jpg")); beeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,loaderComplete); beeSprite.x = Math.random()*stage.stageWidth; beeSprite.y = Math.random()*stage.stageHeight; beeSprite.rotation = Math.random()*360 beeSprite.blobContainerEnabled = true; beeSprite.addEventListener(TouchEvent.TOUCH_DOWN, startDrag_Press); beeSprite.addEventListener(TouchEvent.TOUCH_UP, stopDrag_Release); beeSprite.addEventListener(GestureEvent.GESTURE_ROTATE, gestureRotateHandler); beeSprite.addEventListener(GestureEvent.GESTURE_SCALE, gestureScaleHandler); beeSprite.addChild(beeLoader); addChild(beeSprite); } private function loaderComplete(e:Event):void { e.target.loader.x = -e.target.width/2; e.target.loader.y = -e.target.height/2; } private function startDrag_Press(e:TouchEvent):void { e.target.startTouchDrag(); } private function stopDrag_Release(e:TouchEvent):void { e.target.stopTouchDrag(); } private function gestureRotateHandler(e:GestureEvent):void { e.target.rotation += e.value; } private function gestureScaleHandler(e:GestureEvent):void { e.target.scaleX += e.value; e.target.scaleY += e.value; } } } |
Testing Your Application
There are various ways to test your multitouch application. If you have a multitouch imput device, events can be directly streamed to your Flash application via TUIO. If you do not have a device that can output multitouch events, you can use the built-in simulator in GestureWorks. To test your app in the main menu, go to Control > Test Movie or press “Ctrl + Enter” on the keyboard. Alternatively, you can publish your application as a self-contained executable by following these steps:
- In your open Flash application “myApp.fla“, go to File > Publish Settings.
- Then in the Formats tab, select “Windows Projector” or “Macintosh Projector,” then press the “Publish” button.
- The application will be published to your appfolder; from there, you can simply double click on myApp.exe (Windows) or myApp.app (Mac) and run your multitouch application.
NOTE - To build and test these tutorials on a computer without a touchscreen display, pressing “Shift” or “Control” while clicking on an object with the mouse will create additional touch points within the simulator. To remove touch points, simply click them.
Other Tutorials In The “Getting Started With Gestures” Series
These tutorials cover the basics of how to integrate touch and gesture events into your Flash applications:
- Flash
- How To Create An Application Using Touch Tap
- How To Create An Application Using Touch Drag
- How To Create An Application Using The Rotate Gesture
- How To Create An Application Using The Zoom Gesture
- How To Create An Application Using The Flick Gesture
- How To Create A Multi-User Application Using Drag, Rotate and Scale
- How To Create A Google Maps Application with Move, Double Tap, Rotate, Scale and Tilt
- How To Create A Multitouch Twitter Application
- Flex
- How To Create a Flex Application Using the Multitouch Gesture Scroll




