For the scope of this game, we will observe just a few basic gestures. These are defined by an enum EGestureType
in GameGlobals.h
as follows:
enum EGestureType { E_GESTURE_NONE = 0, E_GESTURE_TAP, E_GESTURE_SWIPE_UP, E_GESTURE_SWIPE_DOWN, E_GESTURE_SWIPE_LEFT, E_GESTURE_SWIPE_RIGHT, };
As you can see, we will observe the player's touches for taps and swipes in four directions: up, down, left and right. A lot of developers have written great code that observes multiple taps, pinch-zoom gestures, panning gestures, and so on. I encourage you to plug them into this class and increase its capabilities.
Without further ado, let's take a look at the class declaration of GestureLayer
from the GestureLayer.h
file:
class GestureLayer : public CCLayer { public: GestureLayer(); ~GestureLayer(); static GestureLayer* create(CCObject* target, SEL_CallFuncO handler); virtual bool init(CCObject* target, SEL_CallFuncO handler); // touch listeners virtual void ccTouchesBegan(CCSet* set, CCEvent* event); virtual void ccTouchesMoved(CCSet* set, CCEvent* event); virtual void ccTouchesEnded(CCSet* set, CCEvent* event); // accessors and mutators inline CCPoint GetTouchStart() { return touch_start_; } inline CCPoint GetTouchEnd() { return touch_end_; } inline EGestureType GetGestureType() { return gesture_type_; } protected: void HandleTouch(); // target to pass the gesture event to CCObject* target_; // function to call when gesture event occurs SEL_CallFuncO handler_; // member variables bool is_touch_active_; CCPoint touch_start_; CCPoint touch_end_; EGestureType gesture_type_; };
Our GestureLayer
class publicly inherits from CCLayer
, and needs a target object and a callback function to be created. The target object will be the node that will be listening for gestures, and the callback function is the function that must be called when a gesture is detected. Before we move further, let me show you how this class is created in the CreateGame
function of GameWorld.cpp
:
void GameWorld::CreateGame() { . . . // create & add the gesture layer gesture_layer_ = GestureLayer::create(this, callfuncO_selector(GameWorld::OnGestureReceived)); addChild(gesture_layer_); . . . }
While creating the GestureLayer
object, we pass in a reference to GameWorld
as the target and a function callback to the OnGestureReceived
function of GameWorld
. Now, let's take a look at the touch callback functions of GestureLayer.cpp
:
void GestureLayer::ccTouchesBegan(CCSet* set, CCEvent* event) { CCTouch* touch = (CCTouch*)(*set->begin()); CCPoint touch_point = touch->getLocationInView(); touch_point = CCDirector::sharedDirector()->convertToGL(touch_point); // first reset variables gesture_type_ = E_GESTURE_NONE; touch_end_ = CCPointZero; // start observing touch is_touch_active_ = true; // save first touch point touch_start_ = touch_point; } void GestureLayer::ccTouchesMoved(CCSet* set, CCEvent* event) { CCTouch* touch = (CCTouch*)(*set->begin()); CCPoint touch_point = touch->getLocationInView(); touch_point = CCDirector::sharedDirector()->convertToGL( touch_point); // save subsequent touch touch_end_ = touch_point; HandleTouch(); } void GestureLayer::ccTouchesEnded(CCSet* set, CCEvent* event) { CCTouch* touch = (CCTouch*)(*set->begin()); CCPoint touch_point = touch->getLocationInView(); touch_point = CCDirector::sharedDirector()->convertToGL( touch_point); // save subsequent touch touch_end_ = touch_point; HandleTouch(); // stop observing touch is_touch_active_ = false; }
As you can see, these functions are run-of-the-mill touch functions that store the initial and subsequent touches while calling the HandleTouch
function. The HandleTouch
function recognizes all the gestures:
void GestureLayer::HandleTouch() { // don't do anything if not observing touch if(is_touch_active_ == false) { return; } // check for a single tap if(ccpFuzzyEqual(touch_start_, touch_end_, 1)) { gesture_type_ = E_GESTURE_TAP; (target_->*handler_)(this); is_touch_active_ = false; return; } // calculate distance between first and last touch CCPoint touch_difference = ccpSub(touch_end_, touch_start_); // horizontal swipe if(fabs(touch_difference.x) > MIN_GESTURE_DISTANCE) { gesture_type_ = (touch_difference.x > 0) ? E_GESTURE_SWIPE_RIGHT : E_GESTURE_SWIPE_LEFT; (target_->*handler_)(this); is_touch_active_ = false; return; } // vertical swipe if(fabs(touch_difference.y) > MIN_GESTURE_DISTANCE) { gesture_type_ = (touch_difference.y > 0) ? E_GESTURE_SWIPE_UP : E_GESTURE_SWIPE_DOWN; (target_->*handler_)(this); is_touch_active_ = false; return; } }
The first type of gesture we check is a single tap. Quite simply, this would happen if the touch start and end points are at the same point. Yet we've given the comparison a slight tolerance.
In the swipe gestures, we fetch the distance between the first and last touch and compare with a predefined constant, MIN_GESTURE_DISTANCE
(defined as 10
pixels in GameGlobals.h
). If the distance is greater, we have a swipe. The code that follows that condition basically narrows it down to one of four directions.
All these conditionals call the same callback function that is passed to this class at the time of creation. Also, the callback function is passed a reference to this GestureLayer
class as an argument, since this was a callback of type SEL_CallFuncO
. This sums up our gesture recognition
class, but we still have to implement the spawning of towers based on these gestures. Before we move forward, let's take a quick look at the function passed to GestureLayer
when GameWorld
created the OnGestureReceived
function:
void GameWorld::OnGestureReceived(CCObject* sender) { GestureLayer* gesture_layer = (GestureLayer*)sender; // call the respective gesture's handler switch(gesture_layer->GetGestureType()) { case E_GESTURE_TAP: HandleTap(gesture_layer->GetTouchStart()); break; case E_GESTURE_SWIPE_UP: HandleSwipeUp(); break; case E_GESTURE_SWIPE_DOWN: HandleSwipeDown(); break; case E_GESTURE_SWIPE_LEFT: HandleSwipeLeft(); break; case E_GESTURE_SWIPE_RIGHT: HandleSwipeRight(); break; } }
The OnGestureReceived
function first casts from CCObject
to GestureLayer
, so we have all the information we require to handle the gesture detected. Based on the type of gesture, we have a function to handle each type. Rest assured, we shall discuss them in detail in our next section when we write the logic to spawn our towers.
3.17.79.20