0
votes

I have an interesting problem handling touch events in a cocos2D program I’m writing. I have 3 CCLayer sublassed layers in a CCScene:

backgroundLayer – z:0 – simple static layer used to display a background image.

planetLayer - z:3 – display layer – visualization of data changes are displayed here.

gameControlsLayer – z:5 – layer used to display data controllers such as sliders and buttons.

I separated the planet and control layers because I want to pan and zoom the planet layer without worrying about the controls being affected.

Here is the scene init code that sets up these layers:

- (id) init
{
    self = [super init];
    if (self != nil)
    {
        // background layer
        BackgroundLayer *backgroundLayer = [BackgroundLayer node];
        [self addChild:backgroundLayer z:0];

        // planet layer
        PlanetLayer *planetLayer = [PlanetLayer node];
        [self addChild:planetLayer z:3];

        // gameplay layer
        GameControlsLayer *gameControlsLayer = [GameControlsLayer node];
        [self addChild:gameControlsLayer z:5];
    }
    return self;
}

I need touch handling on both the planet and control layers. On the control layer, the touch handling is inside control objects that are layer children. I wrote all this code first and it works fine.

After that, I wrote layer-wide touch handling for the planet layer so I could do touch scrolling and zooming. I’ve got the scrolling in (though not the zoom yet), and it works fine. My problem is this: When I touch and manipulate the controls on the control layer, they work fine, but as I move a slider thumb up and down, for example, the planet layer behind the control layer pans as if the touches were meant for it.

It may be redundant to include the code, but here are the relevant snippets:

For the slider control, a child of the control layer:

- (void) onEnterTransitionDidFinish
{
    [[CCTouchDispatcher sharedDispatcher] 
      addTargetedDelegate:self priority:1 swallowsTouches:YES];
}

- (void) onExit
{
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
}

#pragma mark Touch Delegate

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    CGPoint location = [[CCDirector sharedDirector]
               convertToGL:[touch locationInView:[touch view]]];



    float distance = [self distanceBetweenPointOne:thumbPosition
                                       andPointTwo:location];
    if (distance > thumbRadius*2)
    {
        return NO;
    }

    // touch is on the thumb. store the location so we 
    // can calculate changes on ccTouchMoved events
    touched = YES;
    return YES;
}

- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (!touched)
    {
        return;
    }

    CGPoint location = [[CCDirector sharedDirector]
               convertToGL:[touch locationInView:[touch view]]];
    location = [self convertToNodeSpace:location];

    // if we're too far off the thumb, abandon it
    float distance = [self distanceBetweenPointOne:thumbPosition
                                       andPointTwo:location];
    if (distance > thumbRadius*3)
    {
        //touched = NO;
        return;
    }

    // this call adjusts some ivars – not relevant
    [self updateValueAndPosition:location];
}

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (touched)
    {
        touched = NO;
    }
}

- (void)ccTouchCancelled:(UITouch *)touch 
               withEvent:(UIEvent *)event
{
    [self ccTouchEnded:touch withEvent:event];
}

Here is the relevant code from the planet layer:

// Yes, we want touch notifications please
//
- (void) onEnterTransitionDidFinish
{
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
       priority:0 swallowsTouches:NO];
}

#pragma mark Touch Delegate

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    CGPoint touchLocation = [touch locationInView:[touch view]];
    touchLocation = [[CCDirector sharedDirector]
                      convertToGL:touchLocation];

    // ****NOTE – the following is a kludge I used it to break
    // ********** the behavior; the controls are all grouped
    // ********** at the bottom of the screen. But this is not
    // ********** an acceptable fix. I shouldn’t need to mask
    // ********** off certain areas of the screen. This strategy
    // ********** will not hold once I add other controls to the
    // ********** control layer.
    //
    if (touchLocation.y > 250.0f)
    {
        self.touched = YES;
        touchHash = [touch hash];
        return YES;
    }
    return YES;
}

- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (!touched)
    {
        return;
    }

    if ([touch hash] == touchHash)
    {
        CGSize screenSize = [[CCDirector sharedDirector] winSize];

        CGPoint touchLocation = 
                [touch locationInView:[touch view]];
        CGPoint prevLocation = 
                [touch previousLocationInView:[touch view]];

        touchLocation = [[CCDirector sharedDirector]
                          convertToGL:touchLocation];
        prevLocation = [[CCDirector sharedDirector]
                          convertToGL:prevLocation];

        CGPoint diff = ccpSub(touchLocation, prevLocation);

        CGPoint currentPosition = [self position];
        CGPoint newPosition = ccpAdd(currentPosition, diff);

        if (newPosition.x < lowestX)
            newPosition.x = lowestX;
        else if (newPosition.x > highestX-screenSize.width)
            newPosition.x = highestX-screenSize.width;

        if (newPosition.y < lowestY)
            newPosition.y = lowestY;
        else if (newPosition.y > highestY-screenSize.height)
            newPosition.y = highestY-screenSize.height;

        [self setPosition:newPosition];
    }
}

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    if ([touch hash] == touchHash)
    {
        if (touched)
        {
            touched = NO;
        }
    }
}

- (void)ccTouchCancelled:(UITouch *)touch 
               withEvent:(UIEvent *)event
{
    [self ccTouchEnded:touch withEvent:event];
}

I want to add the pinch zoom capabilities, but I need to clear up this issue before that will be advisable. Does anybody have the answer here? I feel like I’m missing something obvious…

Thanks!

1

1 Answers

0
votes

In your planetLayer's ccTouchBegan:, you should find a way to tell whether the touch was inside of one of the controls and in such case return NO instead of YES.

One possibility you have is checking [touch view] (I have a control layer made of UIKit object and it works like that), otherwise you should get the coordinate of the touch and check whether they CGRectContainsPoint([control rect], location)).