2015/04/16, by @TapirLiu

Introduction

Any Rotation Gesture ( github ) is an one-stoke simple gesture recognition library written in ActionScript 3. I made it mainly for porting my web game Color Infection series to a mobile game Infect . The control for the web version is to destroy some small breakable shapes by clicking them. For example, in following screenshot, the pinks are breakable.

But for touch device, it is hard to tap on the small shapes accurately and often mis-tap on wrong shapes. Even worse, some removable shapes may move at a high speed so that it is hard to catch them. So I needed to find a new control method for the mobile version. Finally I got this idea: draw different gestures to remove corresponding removable shapes. For example, draw a / to remove the left most shape, draw a | to remove the right one, draw a circle to remove the bottom one.

The start of the following video shows how the new control method works:

Google didn't find a lib satisfying my need. But it gave me one for reference , which is an English letter recognition lib. It uses the Levenshtein algorithm and uses 2D space vectors as input data. There are 26 sample standards, each for one letter. The input data will be compared with each of sample standards. The one with the smallest difference/error will be viewed as the output gesture. It will recognize M and W as two different gesture. If we draw a 3 or a Σ, it will be viewed as neither M nor W.

My lib also uses the same Levenshtein algorithm. But it views M and W and 3 and Σ as one gesture, one gesture with different rotations. It uses 1D delta rotations as input. It can recognize the following gestures shown at the top the following demo, whatever rotations of the gestures are. This lib can also recognize if a gesture is painted in the direction of CW or CCW. In other words, my lib will output three values (GestureType, GestureRotation, CW/CCW). But the GestureRotation and CW/CCW values are only meaningful for part of these geatures.

The lib is still some rough, but it satisfies the mobile version of Color Infection well.

The meaningfulness table for GestureRotation and CW/CCW:

Is GestureRotation meaningful? Is CW/CCW meaningful? Line Yes No LineArrow Yes No LineZigzag Yes No Arrow Yes Yes Zigzag Yes Yes (always CW) MirrorZigzag Yes Yes (always CCW) Wave Yes Yes Pool Yes Yes Triangle No No Circle No No FivePointStar No No LongPress No No

Usage

import flash.system.Capabilities; import flash.utils.getTimer; import com.tapirgames.gesture.GestureAnalyzer; import com.tapirgames.gesture.GesturePoint; import com.tapirgames.gesture.GestureSegment; ... ... private var _ga:GestureAnalyzer; private function OnMouseDown (event:MouseEvent):void { _ga = new GestureAnalyzer (Capabilities.screenDPI * minGestureSize, Capabilities.screenDPI * 0.02); _ga.RegisterPoint (event.stageX, event.stageY, getTimer ()); } private function OnMouseMove (event:MouseEvent):void { _ga.RegisterPoint (event.stageX, event.stageY, getTimer ()); } private function OnMouseUp (event:MouseEvent):void { _ga.RegisterPoint (event.stageX, event.stageY, getTimer ()); _ga.Finish (); // only needed for LongPressed gesture var result:Object = _ga.Analyze (); _ga = null; if (result.mGestureType == null) { trace ("Too weird to recognize."); return; } if (result.mGestureType == GestureAnalyzer.kGestureName_LongPress) { trace ("Long pressed."); return; } switch (result.mGestureType) { case GestureAnalyzer.kGestureName_Line: break; case GestureAnalyzer.kGestureName_LineArrow: break; case GestureAnalyzer.kGestureName_LineZigzag: break; case GestureAnalyzer.kGestureName_Arrow: break; case GestureAnalyzer.kGestureName_Zigzag: break; case GestureAnalyzer.kGestureName_MirrorZigzag: break; case GestureAnalyzer.kGestureName_Wave: break; case GestureAnalyzer.kGestureName_Pool: break; case GestureAnalyzer.kGestureName_Triangle: break; case GestureAnalyzer.kGestureName_Circle: break; case GestureAnalyzer.kGestureName_FivePointStar: break; default: { trace ("???????????????????"); return; } trace ((result.mIsClockWise ? "CW" : "CCW"); trace ("angle = " + result.mGestureAngle); // degrees, [0, 360); } }