Tracking Multi-touch Gestures [Tutorial]

Discussion in 'iOS Development' started by Whiteninga, Jan 22, 2010.

  1. Whiteninga

    Whiteninga New Member

    Joined:
    Jan 22, 2010
    Messages:
    2
    Likes Received:
    0
    Device:
    iPod touch
    I've recently had to develop some multi-touch controls for a video game, and after searching the internet for an example of how to do such a thing, all I could find were pinch/zoom examples. I've decided to post my solution in hopes that others may find it useful.

    The problem I had was that simple "distance between 2 touches" wasn't good enough. I needed to track the entire movement of fingers and then parse the gestures afterwards.

    Since a UITouch doesn't have any way of distinguishing itself from another UITouch, the only way I could think of to keep track of which touch is which is by storing the memory address. I cringe to do this, but if someone has a better solution let me know...

    All of the code I use is in C++ except for the stuff I use to interface with the view.

    Code:
    /*
     *  iTouch.h
     *
     *  This is the class I use to keep track of a finger from conception
     * through movement till it is released.
     */
    
    class iTouch
    {
    public:
    	
    	/** The memory address of the UITouch */
    	int ID;
    	
    	/** The current x-location of this finger */
    	int x;
    	
    	/** The current y-location of this finger */
    	int y;
    	
    	/** The current time-stamp of this finger */
    	float time;
    	
    	iTouch(int ID, float x, float y, float time)
    	{
    		this->ID = ID;
    		this->x = x;
    		this->y = y;
    		this->time = time;
    	}
    };
    The iTouch will hold the data for one finger. Pretty straightforward.

    Code:
    /*
     *  TouchTracker.h
     *
     *  Keeps track of all touches by the user through all phases.
     */
    
    class TouchTracker
    {
    public:
    	
    	std::list<iTouch *> touches;
    	
    	void handleTouchBegin(int ID, float x, float y, float time);
    	void handleTouchMoved(int ID, float newX, float newY, float newTime);
    	void handleTouchEnded(int ID, float newX, float newY, float newTime);
    	
    	iTouch* getFirstFinger();
    	iTouch* getSecondFinger();
    	
    	void AddTouch(iTouch *touch);
    	void RemoveTouch(iTouch *touch);
    	
    	int getNumTouches();
    };
    The TouchTracker is what I use to interface to the touchesBegan and other system functions.

    Code:
    /*
     *  TouchTracker.cpp
     */
    
    #include "TouchTracker.h"
    
    void TouchTracker::handleTouchBegin(int ID, float x, float y, float time)
    {
    	iTouch * t = NULL;
    	
    	for (
    		 std::list<iTouch *>::iterator i = touches.begin();
    		 i != touches.end() && t == NULL;
    		 ++i)
    	{
    		iTouch *thisTouch = *i;
    		
    		if (thisTouch->ID == ID)
    		{
    			t = *i;
    		}
    	}
    	
    	if (t == NULL)
    	{
    		iTouch *touch = new iTouch(ID, x, y, time);
    		AddTouch(touch);
    	}
    }
    
    void TouchTracker::handleTouchMoved(int ID, float newX, float newY, float newTime)
    {
    	iTouch * t = NULL;
    	
    	for (
    		 std::list<iTouch *>::iterator i = touches.begin();
    		 i != touches.end() && t == NULL;
    		 ++i)
    	{
    		iTouch *thisTouch = *i;
    		
    		if (thisTouch->ID == ID)
    		{
    			t = *i;
    		}
    	}
    	
    	if (t != NULL)
    	{
    		t->x = newX;
    		t->y = newY;
    		t->time = newTime;
    	}
    }
    
    void TouchTracker::handleTouchEnded(int ID, float newX, float newY, float newTime)
    {
    	//printf("touch ended (id = %d)\n", ID);
    	
    	iTouch * t = NULL;
    	
    	for (
    		 std::list<iTouch *>::iterator i = touches.begin();
    		 i != touches.end() && t == NULL;
    		 ++i)
    	{
    		if ((*i)->ID == ID)
    		{
    			t = *i;
    		}
    	}
    	
    	if (t != NULL)
    	{
    		RemoveTouch(t);
    	}
    
    }
    
    int TouchTracker::getNumTouches()
    {
    	return touches.size();
    }
    
    iTouch* TouchTracker::getFirstFinger()
    {
    	return touches.front();
    }
    
    iTouch* TouchTracker::getSecondFinger()
    {
    	int index = 0;
    	
    	iTouch * t = NULL;
    	
    	for (
    		 std::list<iTouch *>::iterator i = touches.begin();
    		 i != touches.end() && t == NULL;
    		 ++i)
    	{
    		if (index == 1)
    		{
    			t = *i;
    		}
    		
    		index++;
    	}
    	
    	return t;
    }
    
    void TouchTracker::AddTouch(iTouch *touch)
    {
    	touches.push_back(touch);
    }
    
    void TouchTracker::RemoveTouch(iTouch *touch)
    {
    	touches.remove(touch);
    	delete touch;
    }
    Right now I include getter functions for the first and second touches being tracked. That is only because my game only requires 2. You can add your own if you need more.

    From here i just create a touchTracker object in my view and call it like thus:

    Code:
    // Handles the start of a touch
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    	for( UITouch *touch in touches )
    	{
    		// Grab the location of the touch
    		m_TouchStart = [touch locationInView:self];
    		
    		touchTracker->handleTouchBegin((int)(void*)touch, m_TouchStart.x, m_TouchStart.y, touch.timestamp);
    	}
    }
    
    // Handles the continuation of a touch.
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
    	for( UITouch *touch in touches )
    	{
    		// Grab the location of the touch
    		CGPoint touchLocation = [touch locationInView:self];
    
    		touchTracker->handleTouchMoved((int)(void*)touch, touchLocation.x, touchLocation.y, touch.timestamp);
    	}
    }
    
    // Handles the end of a touch event.
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    	for( UITouch *touch in touches )
    	{
    		// Grab the location of the touch
    		CGPoint touchLocation = [touch locationInView:self];
    		
    		touchTracker->handleTouchEnded((int)(void*)touch, touchLocation.x, touchLocation.y, touch.timestamp);
    	}
    }
    Now you can create an input class where you check the current state of the touchTracker and react accordingly. If you had no touches last frame and this frame you have 1, that means a finger was pressed. If you have 1 down and there is still one down the next frame, check to see if it moved, etc.

    I can post the rest of the code for input state handling and events when I get it cleaned up a little bit. Hope this helps get you started!
  2. lauNchD

    lauNchD Well-Known Member

    Joined:
    Jan 27, 2008
    Messages:
    1,844
    Likes Received:
    261
    Device:
    iPhone 5 (Black)
    Why would you want to use a C++ approach when there's so much unnecessary glue (IMO; just curious)?
    It's a nice tutorial anyway, and should help people getting started with touch tracking.
  3. Whiteninga

    Whiteninga New Member

    Joined:
    Jan 22, 2010
    Messages:
    2
    Likes Received:
    0
    Device:
    iPod touch
    The game is a port from a different platform that was written in C++, so I have a wrapper to talk to the OS calls. I admit there is unnecessary glue and steps that need to be taken this way, but I also find myself much more comfortable in c++ anyways...

Share This Page