Page 1 of 1

Line Based Touch Gesture Input!

PostPosted: Thu Jan 19, 2012 10:26 pm
by Jagmaster
Image

Welcome!

With the Ios engine for Ge, and the Android engine on it's way, I thought it'd be cool to explain how to set up some somewhat dynamic controls.

By the end of this tutorial you will have built a simple gesture system using one of two methods. The first setup will be straight line input, or a “Flick” or “Swipe” motion on a touchscreen using very simple geometry. We'll tell an actor to measure the direction of the line, and do an action based on that. Using the same logic of the first, the second will measure the degrees of an angle drawn on the screen, and do an action accordingly.

Lets start out with the first system.

This setup will be similar to the controls used in the “Temple Run” Iphone game.

First off, before we start decide whether or not you want the line on the screen to be visible while drawing, after drawing, or not at all. If you chose the first, you should view the “Drawing Functions” tutorial if you don't know how to do so. (You can find it in Game Editor by clicking Help>> Script>> Drawing Functions.)

To start out the input system, we will create a filled region actor and assign view to be it's parent. I'm going to call mine ClickBox. You should probably extend it to the dimensions of the view. The next step is to declare some variables. If you want to display the values in a text actor for debugging purposes, you'd want to make them global. Otherwise they can be declared locally in the ClickBox draw actor before the rest of the script. The only ones that must be global are State, because that will be our final output, and Draw, because that will tell our canvas to draw, as well as xStart and yStart.

(To avoid confusion in the examples, I made these global using the variables tab.)
Code: Select all
int xStart;
int xMid;
int xEnd;
int yEnd;
int yMid;
int yStart;
int Draw;
int TimeDrawn;
int State;
int ang;
int FinalAng;
int arm1;
int arm2;


For the first system we'll only use xStart, yStart, State, Draw, and TimeDrawn.

The next step we'll do is record the xmouse and ymouse when the ClickBox is clicked, and make Draw equal 1. Like so:
Mouse Button Down (Left) >>
Code: Select all
xStart=xmouse;
yStart=ymouse;
Draw =1;   //We are drawing now.


Okay, pretty easy so far. Next, on Mouse Button UP (Left) >>
Code: Select all
Draw =0;   //We aren't drawing.


Moving right along! Now we go to Draw Actor on ClickBox >>
Code: Select all
xStart*=Draw;      //We tell these vars to multiply themselves
yStart*=Draw;      //by Draw continually. Since Draw will either
TimeDrawn*=Draw;      // be 1 or 0, the vars will either equal 0 or themselves!

if(Draw==1)
{
TimeDrawn++;
if(TimeDrawn>=20)
{
   Draw=0;
}
if( distance(xStart, yStart, xmouse, ymouse) >=200 )//If distance between start and stop is 200.
{
   State = round(direction(xStart, yStart, xmouse, ymouse)/45); //State will be in increments
   Draw=0;                         //of 45 degrees.
}
}


The first three lines of code are ensuring that xStart, yStart, and TimeDrawn will equal 0 if we're not drawing, and themselves if we are. Next, if we are drawing, we'll increase TimeDrawn by one each frame. If we have been drawing for 20 frames or over, Draw will be reset. This procedure is optional; it basically allows enough time for a straight swipe. It only takes this long to make a straight swipe across a 640x480 screen, you'll need to adjust this to suit your needs. Next, if the distance from our start position and our current position is 200 or over, we take the direction from start to end, and round it to the nearest 45 degree increment. Rounding it like this is a practical thing to do for most games, because the player isn't necessarily going to be measuring the angle of the stroke to make sure he flicked upward at exactly 90 degrees. Especially if he's playing a game like Temple Run.

As of now, we can't see what's happening. We should set up an actor to move in the direction of the flick. Let's make an actor called player. You could make the view follow the player if you want.
In Player's Draw Actor>>
Code: Select all
angle=State*45;
directional_velocity=2;


To convert the angle back into real degrees, multiply State by 45.

Alright! Click Game Mode to see the results! 0 is right, 4 is left, 2 is up, and 6 is down. One could do a switch to execute different actions based on what direction of swipe is done. For instance:

switch(State)
{
case 0: // 0 and 8 are the same position approximately.
case 8:
//Move right.
break;

case 4:
//Move left.
break;

case 2:
//Jump.
break;
//And so on.
}

Now that wasn't too bad was it?

This concludes the first system. The next is quite similar and we will build off of what we already have.
Using the same document, We'll replace the Draw Actor on ClickBox with this>>

Code: Select all
xStart*=Draw;         //We tell these vars to multiply themselves
yStart*=Draw;         //by Draw continually. Since Draw will either
xMid*=Draw;           // be 1 or 0, the vars will either equal 0 or themselves!
yMid*=Draw;
xEnd*=Draw;
yEnd*=Draw;
TimeDrawn*=Draw;

if(Draw==1)
{
TimeDrawn++;
if(TimeDrawn>=20)
{
   Draw=0;
}
if(distance(xStart, yStart, xmouse, ymouse)>=100&&xMid==0)//If distance between start and
{                                                           //stop is 100 and only one line has
                                                            //been drawn.
    xMid=xmouse;
    yMid=ymouse;
 
 
}
if(distance(xMid, yMid, xmouse, ymouse)>=100&&xEnd==0&&xMid!=0)
{
    xEnd=xmouse;
    yEnd=ymouse;
    arm1 = direction(xMid, yMid, xStart, yStart); //angles will be in increments
    arm2 = direction(xMid, yMid, xEnd, yEnd);     //of 45 degrees
    ang = round(abs(arm1 - arm2)/45);
    FinalAng = min(ang, abs(ang-8));                  //Keeps Final Angle below 180
 
if(FinalAng==4)//If we have a straight line, change player's direction.
{
State = round(direction(xStart, yStart, xEnd, yEnd)/45); //Dir will be in increments of 45

}
else // Change color if not straight.
{
    switch(FinalAng)
    {
        case 0:
        Player.r=255; //Reset Actor color.
        Player.g=255;
        Player.b=255;
        break;
 
        case 1:
        Player.r=0;  //Cyan if acute angle
        Player.g=255;
        Player.b=255;
        break;
 
        case 2:
        Player.r=255; //Magenta if right angle
        Player.g=0;
        Player.b=255;
        break;
 
        case 3:
        Player.r=255; // Yellow if obtuse angle
        Player.g=255;
        Player.b=0;
        break;
    }
 
}
Draw=0; //Erase Canvas
}

}


Lets dissect this shall we? The first parts should be familiar. We have Start, Mid, and End x&y values set to 0 when draw is 0. This is important, because it resets the vars when the mouse button is released or the line is finished. Next, like before we have a 20 frame timer. The line will be erased after 20 frames.

The next part is a little different. We check to see if the distance between the start and current position is 100 (notice this is half of our previous distance check) and we check to see if there is a midpoint value already (since it's unlikely that our user will hit 0x or 0y on the screen, I'm using this value for checking. If you wanted to eliminate any possible loopholes, go ahead and define a few more vars to check whether a midpoint was defined). When this condition is met, we mark xMid and yMid.

The Next block of code checks the distance between the midpoint and the current position similar to it's predecessor. In the same if statement we check to see if there is an endpoint value already AND if we DO have a midpoint value. After this statement is true, we get the direction between midpoint and startpoint and we assign it to lineang1, and we get the direction between midpoint and endpoint and assign it to lineang2. We round the absolute value of lineangle1 minus lineangle2 to the nearest 45 degree increment and assign that value to ang.

Now, the next line of code is a bit weird, but it's important. You see, if this was left out, you would have to draw your line in a certain direction or else it would register a different value. This is because in the current state, we are measuring reflex angles along with the rest. Someone playing a game isn't going to be thinking about whether the angle is a reflex or obtuse. To fix this, we use the min fuction, and within it we put ang, and the absolute value of ang -8 and assign it to FinalAng. Now, FinalAng will either equal 0, 1, 2, 3, or 4 no matter what direction the angle is drawn. Since 4 would be a straight line, we can use the same direction formula as the fist example. This would make the program respond the exact way it did in the first example. Next, we go to else, (meaning if the the angle is not 4) and within that I set up a switch to change the color of the player based on the measure of the angle.

You can test it out to see what happens! Adjust the distance lines form 100 to whatever you like if you think the lines drawn are too short.

Now, with this approach, it may be a good idea to have a canvas draw a line where the mouse position is on the canvas. Like I said earlier, you can view the tutorial on that, but for all simplicity’s sake, I'll attach the script here. (I named my canvas actor “Canvas”)

Canvas Draw Actor >>
Code: Select all
/Line Drawing Functions
//________________________________________________________________________________
screen_to_actor(&xmouse, &ymouse);//Convert mouse coordinates to real coordinates
if(Draw==1)//If we are drawing
{
    setpen(0, 255, 0, .5, 3);
    lineto(xmouse, ymouse); //Draw line
}
else//If we are not drawing
{
    erase(0, 0, 0, 1);//Erase Canvas
 
}
//________________________________________________________________________________


To make this work when we click the ClickBox, we'll send an activation event to the canvas.

Add this line to the moue button down event for the ClickBox>>
Code: Select all
SendActivationEvent("Canvas");


Then, on the Canvas actor we add an event to catch the activation event from our ClickBox.

Activation Event (ClickBox)>>
Code: Select all
screen_to_actor(&xmouse, &ymouse);
moveto(xmouse, ymouse);


This will move the pen to the mouse position when the ClickBox is clicked.

Now, if you haven't been saving periodically like a good little humanoid, now would be a good time to save your work (naturally).

If you'd like to see a working example here's a .ged document to play around with.
Gesture Input.ged
(6.76 KiB) Downloaded 665 times


I'll possibly post some other examples of this system with different canvas variations in the near future.

This pretty much concludes the tutorial. Let me know if there's anything unclear or anything I should explain further. Any other questions and feedback is also appreciated.

I just hope it was helpful to you all!

Re: Line Based Touch Gesture Input!

PostPosted: Thu Jan 19, 2012 10:32 pm
by master0500
good work, +1

Re: Line Based Touch Gesture Input!

PostPosted: Sat Jan 21, 2012 6:43 pm
by SuperSonic
Nice tutorial :wink:

Re: Line Based Touch Gesture Input!

PostPosted: Sun Jan 29, 2012 3:48 pm
by Jagmaster
Thanks, hope it was helpful. :D

Re: Line Based Touch Gesture Input!

PostPosted: Wed Jun 20, 2012 10:53 am
by lcl
Hey this tutorial is very good!
You've made it simple and explained your code well. Good work! :) +1

Re: Line Based Touch Gesture Input!

PostPosted: Wed Mar 26, 2014 2:11 am
by ValerieCasady
This is very nice and interesting. Thanks!