Touch Events
Again, I will not repeat how to handle events. I assume you have read the previous posts about handling the input devices.
There are two different touch event listeners, one for single touches and one for multi touches.
EventListenerTouchOneByOne // For single touch events EventListenerTouchAllAtOnce // For multi touch events
Each listener has four events to use to get information about the touches. Quite similar but the multi touch gets a vector of touches instead of just one touch.
// Events for single touch bool onTouchBegan (Touch *touch, Event *event) void onTouchMoved (Touch *touch, Event *event) void onTouchEnded (Touch *touch, Event *event) void onTouchCancelled (Touch *touch, Event *event) // Events for multi touch void onTouchesBegan (const std::vector<Touch*>& touches, Event *event) void onTouchesMoved (const std::vector<Touch*>& touches, Event *event) void onTouchesEnded (const std::vector<Touch*>& touches, Event *event) void onTouchesCancelled (const std::vector<Touch*>& touches, Event *event)
All of these events are quite self explanatory except the onTouchCancelled. It might seem that it is quite useless, but it is not. Apparently this event happens if suddenly a pop up dialog shows up. In the case of the multi touch, I noticed it also happens when more than five touches are touching the screen!
Also, you can make the touches to be swallowed and not passed to the other layers once you receive them. To do that you have these methods in the event listeners:
void setSwallowTouches (bool needSwallow) bool isSwallowTouches ()
One last thing is that if you are on an iOS device, you should change a line in the RootViewController.mm to activate the multi touch!
Change this line:
[eaglView setMultipleTouchEnabled:NO];
To:
[eaglView setMultipleTouchEnabled:YES];
Lets try these in code…
I will not list the header file and the code that we repeat every time in the cpp! I will just include the init() method here and the rest comes in the source that you can download. Also I have removed all of the printing code from here so that it doesn’t distract you.
Single Touch Example
In this example, first we make a sprite and make it invisible. Every time that a touch begins, we move the invisible sprite to the location of the touch and make it visible. If the touch moves, we update the position of the sprite and if the touch ends or is cancelled, we simply make the sprite invisible again.
TouchScene.cpp
bool TouchScene::init() { auto circle = Sprite::create("circle.png"); circle->setVisible(false); this->addChild(circle); auto listener = EventListenerTouchOneByOne::create(); // Create a single touch listener listener->onTouchBegan = [circle] (Touch *touch, Event *event) -> bool { auto id = touch->getID(); auto pos = touch->getLocation(); auto loc = touch->getLocationInView(); circle->setVisible(true); circle->setPosition(pos); return true; }; listener->onTouchMoved = [circle] (Touch *touch, Event *event) { auto id = touch->getID(); auto pos = touch->getLocation(); auto loc = touch->getLocationInView(); circle->setPosition(pos); }; listener->onTouchEnded = [circle] (Touch *touch, Event *event) { auto id = touch->getID(); circle->setVisible(false); }; listener->onTouchCancelled = [circle] (Touch *touch, Event *event) { auto id = touch->getID(); circle->setVisible(false); }; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); return true; }
Here is how it will look after running it on an iPhone. When you touch, the circle appears under your finger and if you move your finger, the circle moves too. It disappears when you remove your finger from the screen.
Multi Touch Example
In this multi touch example, every time we get a touch began event, we go through the list of the touches and create a sprite for each one that is positioned at the touch’s location. When I am adding the sprite to the parent, I use the touch id as the tag for the sprite, so that I can retrieve it later when another event is received for that touch.
Same as the single touch example, I move the sprites around with the move event and remove them with ended or cancelled events.
Also, I haven’t printed any data for this example in the code.
TouchScene.cpp
bool TouchScene::init() { auto listener = EventListenerTouchAllAtOnce::create(); // Create a single touch listener listener->onTouchesBegan = [this] (const std::vector<Touch*>& touches, Event *event) { for(auto& touch : touches) { auto sprite = Sprite::create("circle.png"); sprite->setPosition(touch->getLocation()); this->addChild(sprite, 0, touch->getID()); } return true; }; listener->onTouchesMoved = [this] (const std::vector<Touch*>& touches, Event *event) { for(auto& touch : touches) { auto sprite = this->getChildByTag(touch->getID()); sprite->setPosition(touch->getLocation()); } }; listener->onTouchesEnded = [this] (const std::vector<Touch*>& touches, Event *event) { for(auto& touch : touches) this->removeChildByTag(touch->getID()); }; listener->onTouchesCancelled = [this] (const std::vector<Touch*>& touches, Event *event) { for(auto& touch : touches) this->removeChildByTag(touch->getID()); }; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); return true; }
Here is looks after running and putting some fingers on the screen. Believe me, it wasn’t easy to capture the screen for this one on the iPhone 😉