Sunday, July 21, 2013

OpenFL gestures using roxlib gesture library

When I work with NME before , I used a gesture library called roxlib  for gesture handling. This roxlib library is greats! it's not dependent on native features so I can use it on flash also (for the sake rapid prototyping of course). now that this library is updated to support OpenFL (thanks rockswang!) I can happily tell everyone how awesome this library is. This library is great for basic mobile touch screen gestures, it supports :
  • pinch
  • pan
  • swipe 
  • long press
  • tap 
  • rotation
All the gesture that is common usage in the mobile environment nowadays.

The only problem with this library is the lack of documentation and how cluttered it's code with so many unnecessary code (the "how to use" piece of code could be separated from the library itself I guess). But after a while I figured out how this library works. So I'll write a little tutorial for those who need to use gestures on their games :)

Gesture with Roxlib

First we must download the roxlib library from the github page and extract it somewhere. 

Then copy the roxlib library into your project, this is necessary because it's not a haxelib project so we need to import it manually into the project. 

copy library to project

There's not only gesture package inside the library, but we can remove the others without any problem (once again thanks roxswang for making it independent from another package).

only gesture package left

now for the coding part, all we need to do is create a RoxGestureAgent for the gesture event handler, and some sprite used for the target of gesture , the register the gesture event handler to the target sprite using RoxGestureEvent.

var image:Sprite = new Sprite();
image.addChild(new Bitmap(Assets.getBitmapData("img/rock.png")));
image.x = 300;
image.y = 200;
image.scaleX = 2;
image.scaleY = 2;

var roxAgent = new RoxGestureAgent(image, RoxGestureAgent.GESTURE);

image.addEventListener(RoxGestureEvent.GESTURE_SWIPE, onSwipe);
image.addEventListener(RoxGestureEvent.GESTURE_PAN, onPan);
image.addEventListener(RoxGestureEvent.GESTURE_PINCH, onPinch);
image.addEventListener(RoxGestureEvent.GESTURE_ROTATION, onRotation);
image.addEventListener(RoxGestureEvent.GESTURE_TAP, onTap);
image.addEventListener(RoxGestureEvent.GESTURE_LONG_PRESS, onLongPress);

then for each callBack method, as it receive RoxGestureEvent as parameter, the 2 necessary properties to remember from it are the target and extra properties. The target properties contain the target sprite used as gesture target, and extra properties contain different thing for different actions :

  • extra on Pinch contain scale factor (float)
  • extra on Rotation contain rotation angle (float)
  • extra on Pan/Swipe contain pan/swipe speed (Point)
One thing to note about Pan and Swipe difference is that Pan action called continously when you move your finger across the screen, and swipe is only called when you removed your hand from the screen. 

The below codes is the callback I use for this tutorial, you can create your own version. I'll not really explain too much about each callback but here's a brief explanation about each callback method 

When tap gesture called the target will be scaled up:

 private function onTap(e:RoxGestureEvent):Void
  var sp:DisplayObject = cast(, DisplayObject);
  var scX = sp.scaleX; //current scaleX factor
  var scY = sp.scaleY; //current scaleY factor 
  Actuate.tween(sp, 0.5, { scaleX: scX + 0.1, scaleY:scY + 0.1 } ).ease(Bounce.easeOut);

When long press gesture called the target will be scaled down:

 private function onLongPress(e:RoxGestureEvent):Void
  var sp:DisplayObject = cast(, DisplayObject);
  var scX = sp.scaleX; //current scaleX factor
  var scY = sp.scaleY; //current scaleY factor 
  Actuate.tween(sp, 0.5, { scaleX: scX - 0.3, scaleY:scY - 0.3 } ).ease(Bounce.easeOut);

When pinch called the target will be resized:

private function onPinch(e:RoxGestureEvent):Void
 var sp = cast(, DisplayObject);
 var scale: Float = e.extra;
        var spt = sp.parent.localToGlobal(new Point(sp.x, sp.y));
        var dx = spt.x - e.stageX, dy = spt.y - e.stageY;
        var angle = Math.atan2(dy, dx);
        var nowlen = new Point(dx, dy).length;
        var newlen = nowlen * scale;
        var newpos = Point.polar(newlen, angle);
        newpos.offset(e.stageX, e.stageY);
        newpos = sp.parent.globalToLocal(newpos);
        sp.scaleX *= scale;
        sp.scaleY *= scale;
        sp.x = newpos.x;
        sp.y = newpos.y;

When pan called the object will move following the mouse/finger movement:

private function onPan(e:RoxGestureEvent):Void
 var sp = cast(, DisplayObject);
 var pt = cast(e.extra,Point);
        sp.x += pt.x;
        sp.y += pt.y;

When swipe called the target will move using the swipe speed

private function onSwipe(e:RoxGestureEvent):Void
 var sp= cast(, DisplayObject);
 var pt = cast(e.extra, Point);
        Actuate.tween(sp,  0.5, { x:sp.x + pt.x, y:sp.y + pt.y });

And here's the result of the example:
Note : I have only tested this on flash, android, and windows and works without problem . If anyone can try it on iOS or blackberry and tell me the result I would be grateful!
Note : When I tested this on html5 ,swipe and pan didn't work. maybe anyone can figure out why? 

That's all. 

I hope you can make use of this awesome library. as for this is the best library I found for simple mobile gestures. again, all thanks for rockswang for this awesome gesture library :D