Мультисенсорное распознавание и распознавание - Cocos2d для iPhone
Я хотел бы знать, как выявлять и различать прикосновения в режиме мультитач. Я читал о хэш-коде, но не понимаю, как его использовать. Я хочу знать, когда касаются двух моих спрайтов одновременно, как будто нажимаешь аккорд на двух клавишах пианино.
[EDIT] Вот пример того, что у меня есть для моего 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;
}
}
}
}
}
И вот начало моего 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];
}
}
}
Не могли бы вы дать мне пример того, как вы бы фиксировали прикосновения, которые начинались на спрайте и заканчивались на спрайте? Я боролся и не могу заставить работать хеш-код - просто не понимаю, как хеш-код можно использовать для ссылки на прикосновение позже. Я думаю, то, что я пытаюсь сделать, будет называться хеш-трекером?
Я уверен, что есть гораздо менее запутанный способ сделать это, используя хэш-коды и меньше переменных состояния. Я не реализовал метод ccTouchesEnded с другими эффектами переменных состояния, потому что я надеялся найти более простой способ (я знаю, что мне все еще нужно создать методы ccTouchesMoved и Canceled).
2 ответа
Вот как я реализовал это в случае, если кто-то еще пытается это сделать (я ограничил это комбинациями из 2 кнопок, но я мог бы легко расширить логику до комбинаций из 3 и 4 кнопок). Я решил обрабатывать каждое касание индивидуально, используя ccTouchBegan/Ended/Moved вместо использования ccTouchesBegan/Ended/Moved, потому что я просто не мог заставить его работать с хеш-кодом. Любые альтернативные идеи будут приветствоваться.
spuButton.h (подкласс CCSprite)
#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 (Просто мой тик: метод, чтобы мои другие функции не путали пример)
-(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;
}
}
Предположим, у вас есть два прикосновения. Вы получаете событие касания для двух, скажем, в (1,1) и (2,2). Допустим, пользователь обводит оба пальца, тогда вы получите еще одно событие, но на этот раз, возможно, в (3,3) и (4,4).
Вопрос в том, переместился ли (1,1) в (3,3) и (2,2) в (4,4) - или произошло обратное - где (1,1) переместился в (4,4) и (2,2) переехал в (3,3).
Это то, для чего используется "хэш-код" - чтобы дать каждому касанию "имя", чтобы вы могли сказать, что происходит с этим конкретным касанием, когда для него создаются последующие события.
Так что для вашего случая вы получите события касания - посмотрите на координаты и определите, какая клавиша нажата для каждого. Затем вы отслеживаете хэш для каждого касания, чтобы определить, когда они касаются (то есть, конкретной клавиши пианино).
Вы не можете использовать координаты для этого, потому что координаты могут меняться по мере скольжения пальцев.