The GestureLayer class

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.79.20