Como diferenciar um toque de um arrasto

7

Estou criando um aplicativo e tentando fazê-lo para arrastar um sprite (com um segundo sprite no topo) pela tela, mas se eu tocar no sprite, algum outro método deve ser chamado.

O arrasto funciona bem quando toco a borda do meu sprite, mas quando tento arrastar do meio do sprite (onde o segundo sprite está no topo), o arrasto não funciona, mas a torneira é chamada .

Eu sei por que não está fazendo isso, embora haja toques conflitantes de ambos os espíritos, porque o de cima está engolindo o toque antes de atingir o segundo abaixo.

Como eu seria capaz de implementar isso onde eu quero que o sprite arraste se o dedo se mover, mas quero que a torneira seja registrada quando apenas uma torneira é dada (ou seja, o dedo não se moveu na tela)?

Um visual dos sprites com os quais estou trabalhando (se ajudar): insira a descrição da imagem aqui

A runa amarela é um sprite separado da pedra abaixo (porque há animações envolvidas).

--------Touch for the top sprite----------
-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event{
    lastTouchLocation = [RuneScene locationFromTouch:touch];
    BOOL isTouchHandled = CGRectContainsPoint([charSprite boundingBox], lastTouchLocation);

    return isTouchHandled;
}
-(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event{
    NSLog(@"Tap received!");

}

------Touch for the bottom sprite--------

-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event{
    lastTouchLocation = [RuneScene locationFromTouch:touch];
    BOOL isTouchHandled = NO;

    // Check if this touch is on the Spider's sprite.
    if (CGRectContainsPoint([current.runeSprite boundingBox], lastTouchLocation)){
        mover = current;
        isTouchHandled = YES;
    }
    else if(CGRectContainsPoint([rune1.runeSprite boundingBox], lastTouchLocation)){
        mover = rune1;
        isTouchHandled = YES;
    }
    else if(CGRectContainsPoint([rune2.runeSprite boundingBox], lastTouchLocation)){
        mover = rune2;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune3.runeSprite boundingBox], lastTouchLocation)){
        mover = rune3;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune4.runeSprite boundingBox], lastTouchLocation)){
        mover = rune4;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune5.runeSprite boundingBox], lastTouchLocation)){
        mover = rune5;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune6.runeSprite boundingBox], lastTouchLocation)){
        mover = rune6;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune7.runeSprite boundingBox], lastTouchLocation)){
        mover = rune7;
        isTouchHandled = YES;
    }else if(CGRectContainsPoint([rune0.runeSprite boundingBox], lastTouchLocation)){
        mover = rune0;
        isTouchHandled = YES;
    }

    // Stop the move action so it doesn't interfere with the user's scrolling.
    //[self stopActionByTag:ActionTagCastingLayerMovesBack];

    // Always swallow touches, GameLayer is the last layer to receive touches.
    return isTouchHandled;

}

-(void) ccTouchMoved:(UITouch*)touch withEvent:(UIEvent *)event{
    CGPoint currentTouchLocation = [RuneScene locationFromTouch:touch]; 

    // Take the difference of the current to the last touch location.
    CGPoint moveTo = ccpSub(lastTouchLocation, currentTouchLocation);

    // Then reverse it since the goal is not to give the impression of moving the camera over the background, 
    // but to touch and move the background.
    moveTo = ccpMult(moveTo, -1);

    lastTouchLocation = currentTouchLocation;

    [self moveActionWithLocation: moveTo];
}

-(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event{
    if (!current.isPlaced && mover == current && currentR < Rune6) {
        // Move the game layer back to its designated position.
        CCMoveTo* move = [CCMoveTo actionWithDuration:1 position:curPt];
        CCEaseIn* ease = [CCEaseIn actionWithAction:move rate:0.5f];
        //ease.tag = ActionTagCastingLayerMovesBack;
        [current.runeSprite runAction:ease];

        [current setIsPlaced:YES];
        current.charSprite = [characters objectAtIndex:currentR];
        current.charSprite.position = curPt;
        //charSprite.visible = YES;
        [current performSelector:@selector(fade:) withObject:current.charSprite afterDelay:1];
        [current reorderChild:current.charSprite z:10];

        [self updateCurrentRune:currentR];
        [self updateCurrentCastNum:currentP];
        [self reorderChild:current z:10];
    }
}

Tentei pesquisar no UITapGestureRecognizer, mas tudo o que faço para tentar implementar isso nunca funciona. Minhas camadas / sprites não me permitem adicioná-las como gestos. Também li algo sobre o CCGrstureRecognizer ou algo nos fóruns do cocos2d, mas não consigo encontrar nenhuma documentação nessa classe nem descobrir como realmente usá-lo ...

Alguém aqui conhece uma maneira de me ajudar com meu problema?

OghmaOsiris
fonte

Respostas:

5

Não posso falar sobre o cocos2d especificamente, mas, em geral, toque versus arraste é o mesmo problema de reconhecimento de entrada que clique do mouse versus arraste.

A maneira padrão de lidar com isso é observar quando uma ratoeira (ou toque) acontece e armazenar onde aconteceu, para referência posterior. Observe os eventos movidos por mouse (ou movidos por toque) para ver se ele se move. Se ele se mover para uma posição a mais de uma certa distância do clique / toque original (normalmente 3 pixels para operações com o mouse - imagino que esse limite seja um pouco maior para os controles de toque), classifique o clique / toque como um arrasto, e comece a tratá-lo dessa maneira (objetos em movimento, etc.). Se a ratoeira / toque for liberada sem nunca ser convertida em um arrasto, aja na posição de clique / toque armazenada como se tivesse sido um evento simples de clique / toque.

Você poderá implementar esse sistema de reconhecimento simples inteiramente nas mensagens de objetivo C TouchBegan, TouchMoved e TouchEnded que você já possui, apenas armazenando um pequeno estado extra sobre cada toque.

Trevor Powell
fonte
Não é muito simples, você também deve pensar no delta entre a imprensa e o comunicado. isso deve ajudá-lo a entender se é uma pressão longa ou uma torneira. Por exemplo, se você tocar na tela, passar 1 segundo e qualquer evento de lançamento ocorrer, trate-o como um toque longo e ignore o próximo evento de lançamento. Se você conseguir um lançamento antes de 1 segundo, trate-o como um toque.
Gustavo Maciel
0

O iOS já está implementando a detecção de tocar / arrastar.

ccTouchMoved é chamado apenas quando o dedo se move (com uma tolerância). Portanto, basta implementar ccTouchMoved no topSprite. Se for chamado, você pode considerar o arrasto.

-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event{
    lastTouchLocation = [RuneScene locationFromTouch:touch];
    BOOL isTouchHandled = CGRectContainsPoint([charSprite boundingBox], lastTouchLocation);
    dragged=NO;

    return isTouchHandled;
}
-(void) ccTouchMoved:(UITouch*)touch withEvent:(UIEvent *)event{
    NSLog(@"Drag !");

    dragged=YES;
}
-(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event{
    NSLog(@"Tap received!");
    if (!dragged) {
        //Follow tap event to the mainSprite
        //...TODO
    }
    dragged=NO;
}
jptsetung
fonte