I would like to know how to detect and differentiate between touches in a multi-touch view. I have read about a "hash" code, but I don't understand how to use it. I want to know when two of my Sprites are touched at the same time, like as if pressing a chord on two keys of a piano.
[EDIT] Here is an example of what I have for my ccTouchesBegan:
- (void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
NSSet *allTouches = [event allTouches];
int validTouchCount = 0;
for (UITouch* touch in allTouches) {
BOOL touchIsValid = FALSE;
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
if (CGRectContainsPoint(_fourButtonsRect, convertedLocation)) {
NSLog(@"Touch is within four buttons");
touchIsValid = TRUE;
}
_playerDidAction = 0;
NSLog(@"before the loop");
if (touchIsValid) {
validTouchCount++;
NSLog(@"Within ValidTouches loop");
CGPoint validLocation = [touch locationInView: [touch view]];
CGPoint convertedValidLocation = [[CCDirector sharedDirector] convertToGL:validLocation];
if (CGRectContainsPoint(_redButtonSprite.boundingBox, convertedValidLocation)) {
_redButtonStatus = TRUE;
[_redButtonSprite setTexture:_redButtonLit];
if (validTouchCount == 1) {
_playerDidAction = 1;
}
}
else if (CGRectContainsPoint(_blueButtonSprite.boundingBox, convertedValidLocation)) {
_blueButtonStatus = TRUE;
[_blueButtonSprite setTexture:_blueButtonLit];
if (validTouchCount == 1) {
_playerDidAction = 2;
}
}
else if (CGRectContainsPoint(_greenButtonSprite.boundingBox, convertedValidLocation)) {
_greenButtonStatus = TRUE;
[_greenButtonSprite setTexture:_greenButtonLit];
if (validTouchCount == 1) {
_playerDidAction = 3;
}
}
else if (CGRectContainsPoint(_yellowButtonSprite.boundingBox, convertedValidLocation)) {
_yellowButtonStatus = TRUE;
[_yellowButtonSprite setTexture:_yellowButtonLit];
if (validTouchCount == 1) {
_playerDidAction = 4;
}
}
if (validTouchCount > 1) {
if (_redButtonStatus && _blueButtonStatus) {
_comboRB = TRUE;
_playerDidAction = 5;
}
else if (_redButtonStatus && _greenButtonStatus) {
_comboRG = TRUE;
_playerDidAction = 6;
}
else if (_redButtonStatus && _yellowButtonStatus) {
_comboRY = TRUE;
_playerDidAction = 7;
}
else if (_blueButtonStatus && _greenButtonStatus) {
_comboBG = TRUE;
_playerDidAction = 8;
}
else if (_blueButtonStatus && _yellowButtonStatus) {
_comboBY = TRUE;
_playerDidAction = 9;
}
else if (_greenButtonStatus && _yellowButtonStatus) {
_comboGY = TRUE;
_playerDidAction = 10;
}
}
}
}
}
And here is the beginning of my ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
if (CGRectContainsPoint(_redButtonSprite.boundingBox, convertedLocation)) {
_redButtonStatus = FALSE;
[_redButtonSprite setTexture:_redButtonNormal];
}
if (CGRectContainsPoint(_blueButtonSprite.boundingBox, convertedLocation)) {
_blueButtonStatus = FALSE;
[_blueButtonSprite setTexture:_blueButtonNormal];
}
if (CGRectContainsPoint(_greenButtonSprite.boundingBox, convertedLocation)) {
_greenButtonStatus = FALSE;
[_greenButtonSprite setTexture:_greenButtonNormal];
}
if (CGRectContainsPoint(_yellowButtonSprite.boundingBox, convertedLocation)) {
_yellowButtonStatus = FALSE;
[_yellowButtonSprite setTexture:_yellowButtonNormal];
}
}
}
Could you maybe give me an example of how you would capture touches that began on a sprite and ended on a sprite? I have been struggling and can't get the hash code to work - just not understanding how the hash code can be used to reference a touch later. I guess what I'm trying to make would be called a hash tracker?
I am sure there is a much less convoluted way to do it using the hash codes and less state variables. I haven't fleshed out the ccTouchesEnded method with the other state variable effects because I was hoping to find a simpler way (I know I still need to make the ccTouchesMoved and Canceled methods too).
Suppose you have two touches. You get a touch down event for the two, say at (1,1) and (2,2). Let's say the user the sides both fingers, you then get another event, but this time maybe at (3,3) and (4,4).
The question is, did (1,1) move to (3,3) and (2,2) move to (4,4) - or did the opposite happen - where (1,1) moved to (4,4) and (2,2) moved to (3,3).
This is what the "Hash Code" is used for - to give each touch a "name" - so you can tell what happens to that particular touch as subsequent events are generated for it.
So for your case, you would get the touch events - look at the coordinates and determine which key is pressed for each. You then keep track of the hash for each touch, to determine when they touch (i.e. the particular piano key) is released.
You can't use the coordinates for this, because to coordinates can change as fingers are slid.
Here is how I implemented this in case anyone else is trying to do this (I have limited it to 2-button combos but I could easily extend the logic to 3 & 4-button combos as well). I chose to handle each touch individually using the ccTouchBegan/Ended/Moved instead of using the ccTouchesBegan/Ended/Moved because I just couldn't get it to work with the hash code. Any alternative ideas would be welcome.
spuButton.h (a CCSprite Subclass)
#import <Foundation/Foundation.h>
#import "cocos2d.h"
typedef enum tagButtonState {
kButtonStatePressed,
kButtonStateNotPressed
} ButtonState;
typedef enum tagButtonStatus {
kButtonStatusEnabled,
kButtonStatusDisabled
} ButtonStatus;
@interface spuButton : CCSprite <CCTargetedTouchDelegate> {
@private
ButtonState buttonState;
CCTexture2D *buttonNormal;
CCTexture2D *buttonLit;
ButtonStatus buttonStatus;
}
@property(nonatomic, readonly) CGRect rect;
+ (id)spuButtonWithTexture:(CCTexture2D *)normalTexture;
- (void)setNormalTexture:(CCTexture2D *)normalTexture;
- (void)setLitTexture:(CCTexture2D *)litTexture;
- (BOOL)isPressed;
- (BOOL)isNotPressed;
- (void)makeDisabled;
- (void)makeEnabled;
- (BOOL)isEnabled;
- (BOOL)isDisabled;
- (void)makeLit;
- (void)makeNormal;
- (void)dealloc;
@end
spuButton.m
#import "spuButton.h"
#import "cocos2d.h"
@implementation spuButton
- (CGRect)rect {
CGSize s = [self.texture contentSize];
return CGRectMake(-s.width / 2, -s.height / 2, s.width, s.height);
}
+ (id)spuButtonWithTexture:(CCTexture2D *)normalTexture {
return [[[self alloc] initWithTexture:normalTexture] autorelease];
}
- (void)setNormalTexture:(CCTexture2D *)normalTexture {
buttonNormal = normalTexture;
}
- (void)setLitTexture:(CCTexture2D *)litTexture {
buttonLit = litTexture;
}
- (BOOL)isPressed {
if (buttonState== kButtonStateNotPressed) return NO;
if (buttonState== kButtonStatePressed) return YES;
return NO;
}
- (BOOL)isNotPressed {
if (buttonState== kButtonStateNotPressed) return YES;
if (buttonState== kButtonStatePressed) return NO;
return YES;
}
- (void)makeDisabled {
buttonStatus = kButtonStatusDisabled;
buttonState= kButtonStateNotPressed;
[self makeNormal];
}
- (void)makeEnabled {
buttonStatus = kButtonStatusEnabled;
buttonState= kButtonStateNotPressed;
[self makeNormal];
}
- (BOOL)isEnabled {
if (buttonStatus== kButtonStatusDisabled) return NO;
if (buttonStatus== kButtonStatusEnabled) return YES;
return NO;
}
- (BOOL)isDisabled {
if (buttonStatus== kButtonStatusEnabled) return NO;
if (buttonStatus== kButtonStatusDisabled) return YES;
return YES;
}
- (void)makeLit {
[self setTexture:buttonLit];
}
- (void)makeNormal {
[self setTexture:buttonNormal];
}
- (id)initWithTexture:(CCTexture2D *)aTexture {
if ((self = [super initWithTexture:aTexture]) ) {
buttonState = kButtonStateNotPressed;
buttonStatus = kButtonStatusEnabled;
}
return self;
}
- (void)onEnter {
if (buttonStatus == kButtonStatusDisabled) return;
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:NO];
[super onEnter];
}
- (void)onExit {
if (buttonStatus == kButtonStatusDisabled) return;
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)containsTouchLocation:(UITouch *)touch {
return CGRectContainsPoint(self.rect, [self convertTouchToNodeSpaceAR:touch]);
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if (buttonStatus == kButtonStatusDisabled) return NO;
if (buttonState== kButtonStatePressed) return NO;
if ( ![self containsTouchLocation:touch] ) return NO;
buttonState= kButtonStatePressed;
[self makeLit];
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
// If it weren't for the TouchDispatcher, you would need to keep a reference
// to the touch from touchBegan and check that the current touch is the same
// as that one.
// Actually, it would be even more complicated since in the Cocos dispatcher
// you get NSSets instead of 1 UITouch, so you'd need to loop through the set
// in each touchXXX method.
if (buttonStatus == kButtonStatusDisabled) return;
if ([self containsTouchLocation:touch]) return;
buttonState= kButtonStateNotPressed;
[self makeNormal];
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
if (buttonStatus == kButtonStatusDisabled) return;
buttonState= kButtonStateNotPressed;
[self makeNormal];
}
- (void)dealloc {
[buttonNormal release];
[buttonLit release];
[super dealloc];
}
@end
HelloWorldScene.m (Just my tick: method to keep my other functions from confusing the example)
-(void)tick:(ccTime)dt {
if ([[_otherControlsArray objectAtIndex:0] wasPressed]) {
[[_otherControlsArray objectAtIndex:0] setWasPressed:NO];
[self removeChild:[_otherControlsArray objectAtIndex:0] cleanup:YES];
[self addChild:[_otherControlsArray objectAtIndex:1]];
NSLog(@"Play");
_gameHasNotBeenPlayedYet = NO;
Snarfle_s_PowerUPAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate makeNotPaused];
[self gameLogic];
}
if (_gameHasNotBeenPlayedYet) {
return;
}
if (_buttonsPressedAndReleased > 0) { //respond to button(s) released and reset
NSLog(@"Buttons Pressed and Released-->%d",_buttonsPressedAndReleased);
if ([self checkButtons:_buttonsPressedAndReleased]);
_buttonsPressed = 0;
_buttonsPressedAndReleased = 0;
return;
}
if (_buttonsPressed <= 4) { // two buttons have not already been pressed
for (spuButton *aButton in _fourButtonsArray) {
if ([aButton isNotPressed]) continue; //this button is not pressed
if (_buttonsPressed == 0) { //this button is pressed and no other buttons have been pressed
_buttonsPressed = aButton.tag;
continue;
}
//this button is pressed while another has been pressed
//figure out which two buttons have been pressed
if (_buttonsPressed == 1) { //red plus another
switch (aButton.tag) {
case 2: //blue
_buttonsPressed = 5;
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 3: //green
_buttonsPressed = 6;
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 4: //yellow
_buttonsPressed = 7;
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
break;
default:
_buttonsPressed = 1;
break;
}
}
if (_buttonsPressed == 2) { //blue plus another
switch (aButton.tag) {
case 1: //red
_buttonsPressed = 5;
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 3: //green
_buttonsPressed = 8;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 4: //yellow
_buttonsPressed = 9;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
break;
default:
_buttonsPressed = 2;
break;
}
}
if (_buttonsPressed == 3) { //green plus another
switch (aButton.tag) {
case 1: //red
_buttonsPressed = 6;
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 2: //blue
_buttonsPressed = 8;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
break;
case 4: //yellow
_buttonsPressed = 10;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
break;
default:
_buttonsPressed = 3;
break;
}
}
if (_buttonsPressed == 4) { //yellow plus another
switch (aButton.tag) {
case 1: //red
_buttonsPressed = 7;
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
break;
case 2: //blue
_buttonsPressed = 9;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
break;
case 3: //green
_buttonsPressed = 10;
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
break;
default:
_buttonsPressed = 4;
break;
}
}
if (_buttonsPressed > 4) break; //more than one has been pressed and identified
}
}
//now we know what buttons have been pressed now check to see if they have been released
//if more than one has been pressed disable the other two
//also if more than one has been pressed and one of them gets released disable the released one but keep it lit
switch (_buttonsPressed) {
case 1: //red
if ([[_fourButtonsArray objectAtIndex:0] isNotPressed]) _buttonsPressedAndReleased = 1;
break;
case 2: //blue
if ([[_fourButtonsArray objectAtIndex:1] isNotPressed]) _buttonsPressedAndReleased = 2;
break;
case 3: //green
if ([[_fourButtonsArray objectAtIndex:2] isNotPressed]) _buttonsPressedAndReleased = 3;
break;
case 4: //yellow
if ([[_fourButtonsArray objectAtIndex:3] isNotPressed]) _buttonsPressedAndReleased = 4;
break;
case 5: //red & blue
if (([[_fourButtonsArray objectAtIndex:0] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:1] isNotPressed])) _buttonsPressedAndReleased = 5;
else {
if ([[_fourButtonsArray objectAtIndex:0] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:0] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:1] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:1] makeLit];
}
}
break;
case 6: //red & green
if (([[_fourButtonsArray objectAtIndex:0] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:2] isNotPressed])) _buttonsPressedAndReleased = 6;
else {
if ([[_fourButtonsArray objectAtIndex:0] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:0] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:2] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeLit];
}
}
break;
case 7: //red & yellow
if (([[_fourButtonsArray objectAtIndex:0] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:3] isNotPressed])) _buttonsPressedAndReleased = 7;
else {
if ([[_fourButtonsArray objectAtIndex:0] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:0] makeDisabled];
[[_fourButtonsArray objectAtIndex:0] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:3] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeLit];
}
}
break;
case 8: //blue & green
if (([[_fourButtonsArray objectAtIndex:1] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:2] isNotPressed])) _buttonsPressedAndReleased = 8;
else {
if ([[_fourButtonsArray objectAtIndex:1] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:1] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:2] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeLit];
}
}
break;
case 9: //blue & yellow
if (([[_fourButtonsArray objectAtIndex:1] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:3] isNotPressed])) _buttonsPressedAndReleased = 9;
else {
if ([[_fourButtonsArray objectAtIndex:1] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:1] makeDisabled];
[[_fourButtonsArray objectAtIndex:1] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:3] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeLit];
}
}
break;
case 10: //green & yellow
if (([[_fourButtonsArray objectAtIndex:2] isNotPressed]) && ([[_fourButtonsArray objectAtIndex:3] isNotPressed])) _buttonsPressedAndReleased = 10;
else {
if ([[_fourButtonsArray objectAtIndex:2] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:2] makeDisabled];
[[_fourButtonsArray objectAtIndex:2] makeLit];
}
if ([[_fourButtonsArray objectAtIndex:3] isNotPressed]) {
[[_fourButtonsArray objectAtIndex:3] makeDisabled];
[[_fourButtonsArray objectAtIndex:3] makeLit];
}
}
break;
default:
_buttonsPressedAndReleased = 0;
break;
}
}
来源:https://stackoverflow.com/questions/4061473/multi-touch-detecting-differentiating-cocos2d-for-iphone