I have a slightly different solution. But it depends on what you mean by “button”.
Buttons typically do their action when they are released (not when they are pressed). Touch a button and slide off of it and if you release off of the button then that button wont do anything. That is how the MenuItem class works.
But I think you might be talking about something else. Something that I call a “touch pad”.
For my touch pad controls, I want the touch area that is touched to be active, regardless of where the finger started the touch.
And for a touch area that is not touched, I don’t want it to be active.
Taking this approach eliminates the issue of starting on a touch pad and ending off of it and the touch control wrongfully staying active. It also solves another problem that you did not mention. When the touch moves/slides from touch pad A to touch pad B. Shouldn’t that active control then transition from A to B. I think that is what users would expect.
e.g.
For things like gas pedal and brake pedal, this makes since. Sometimes the user will lift their finger to go from gas to brake. This will trigger a touch end and touch start. Easy to detect.
Sometimes they will slide their finger from gas to brake. This will just trigger a touch moved a bunch of times depending on how fast they move their finger and how far it moves.
But, in either case, the user expects a transition to occur since they were touching the gas and are now touching the brake. Make sense? It would be really frustrating as a user if you slide from gas to brake and the gas keeps on going
To implement touch pads that solve the above issues, I do the following in a HUD class.
Create a C array of a custom structure to track the touches (shown below).
Since the ptr to the touch is consistent over the phases of the touch (began, moved, ended, canceled), I can capture a pointer to the Touch. But I never directly access the touch through that pointer. That could be problematic and Apple states you should not do that. Probably the same on all platforms. But on iOS, Android and WP8.x it is ok to compare the pointer of touches in touch events that I receive to see if they match one of the active touches in the array of custom structs that I define below called _hudMultipleTouches.
Then in the HUD init method, I cycle through the five _hudMultipleTouches and set touch = nullptr.
typedef struct {
cocos2d::Vec2 pStart;
cocos2d::Vec2 pLastMove;
cocos2d::Vec2 pEnd;
cocos2d::Touch * touch;
tTouchPhase phase;
} tTouchObject;
const static int NUMBER_OF_TOUCH_TRACKING_BINS = 5;
Class HeyaldaHUD : public cocos2d::Layer
{
private:
tTouchObject _hudMultipleTouches[NUMBER_OF_TOUCH_TRACKING_BINS];
}
For processing touches I do the following.
onTouchesBegan: Then for each touch that begins, cycle through _hudMultipleTouches and pick one of the empty bins, e.g. that has touch == nullptr and assign the touch location to pStart and pLastMove and assign its pointer to touch in the struct object that .
onTouchesMoved: When the touch moves, for each touch, cycle through the touches in _hudMultipleTouches if one has a pointer that is equal to touch in the stuct, update the pLastMove.
onTouchesEnded and onTouchesCanceled: When the touch ends / is canceled, for each touch, cycle through the touches in _hudMultipleTouches and if one has a pointer that is equal to touch in the struct, update the pEnd, do any end processing you want, and set touch = nullptr in the _hudMultipleTouches bin that ended. That frees that bin in the custom struct for reuse.
Then, for each of the events above, call a method called userTouchesChanged.
In that method, set local flags at the start of the method for your touch pads that assume none are touched.
Then cycle through the five touches in _hudMultipleTouches and for any that have touch != nullptr, use the pLastMove as the point to compare to see if it is in one of your touch pad rects.
If it is in one of your touch rects, then set the local flag for it, e.g. gasPedalTouched = true.
Then move on to the next touch bin until all five are processed.
Then at the end of that userTouchesChanged method, call your HUD delegates to tell them e.g. gasPedalTouched = false and brakePedalTouched = true.
I have been using this technique on iOS for about 5 years and on Android and WP8 for a little over two years now in multiple games and it works good.