Search code examples
iphonecocos2d-iphonedetectionhashcodemulti-touch

In cocos2d how do you implement a touch hash code and reference it later?


How do you implement a touch hash code and reference it later? 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.

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).

Any advise would be very appreciated!


Solution

  • Here is how I implemented this [I DID NOT USE A HASH CODE because I could not get it to work] 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;
     }
    }