Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocos2d handling touch with multiple layers

Tags:

ios

touch

I've been busy for a few days trying to figure out how to handle touch in my Cocos2d project. The situation is a bit different as normal. I have a few different game layers that have items on it that I need to control with touch:

  • ControlLayer: Holds the game controls (movement, action button). This layer is on top.
  • GameplayLayer: Holds the game objects (CCSprites). This layer is directly beneath the ControlLayer.

Now my touches work fine in the ControlLayer, I can move my playable character around and make him jump and do other silly stuff. Yet I cannot grasp how to implement the touches to some of my CCSprites.

The information I've gathered so far makes me think I need get all my touch input from the control layer. Then I somehow need to 'cascade' the touch information to the GameplayLayer so I can handle the input there. Another option would be for me to get the CGRect information from my sprites by somehow creating an array with pointers to the objects that should be touchable. I should be able to use that information in the ControlLayer to check for each item in that list if the item was touched.

What is the best option to do this, and how do I implement this? I'm kind of new to programming with cocoa and Objective C so I'm not really sure what the best option is for this language and how to access the sprites CGRect information ([mySpriteName boundingBox]) in another class then the layer it is rendered in.

At the moment the only way I'm sure to get it to work is create duplicate CGRects for each CCSprite position and so I can check them, but I know this is not the right way to do it.

What I have so far (to test) is this: ControlLayer.m

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];

CGRect rect = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);

//Tried some stuff here to get see if I could get a sprite by tagname so I could use it's bounding box but that didn't work

// Check for touch with specific location
if (CGRectContainsPoint([tree boundingBox], location)) {
    CCLOG(@"CGRect contains the location, touched!");
}

CCLOG(@"Layer touched at %@", NSStringFromCGPoint(location));

}

Thanks in advance for helping me!

like image 484
Kay Lucas Avatar asked Mar 07 '11 16:03

Kay Lucas


1 Answers

The easiest and simplest way to solve your problem, IMO, is by using ccTouchBegan/Moved/Ended instead of ccTouchesBegan/Moved/Ended. Meaning, you are handling a single touch at a particular moment so you avoid getting confuses over multiple touches, plus the most important feature of ccTouchBegan is a CCLayer can 'consume' the touch and stop it from propagating to the next layers. More explanation after code samples below.

Here are steps to do it. Implement these sets of methods in all CCLayer subclasses that should handle touch events:

First, register with CCTouchDispatcher:

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

Next, implement ccTouchBegan, example below is from a game I've created (some part omitted of course):

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    if (scene.state != lvlPlaying) {
        // don't accept touch if not playing
        return NO;
    }
    CGPoint location = [self convertTouchToNodeSpace:touch];
    if (scene.mode == modePlaying && !firstTouch) {
        if (CGRectContainsPoint(snb_putt.sprite.boundingBox, location)) {
            touchOnPutt = touch.timestamp;

            // do stuff

            // return YES to consume the touch
            return YES;
         }
    }
    // default to not consume touch
    return NO;
}

And finally implement ccTouchMoved and ccTouchEnded like the ccTouches* counterparts, except that they handle single touch instead of touches. The touch that is passed to these methods is restricted to the one that is consumed in ccTouchBegan so no need to do validation in these two methods.

Basically this is how it works. A touch event is passed by CCScene to each of its CCLayers one by one based on the z-ordering (i.e starts from the top layer to the bottom layer), until any of the layers consume the touch. So if a layer at the top (e.g. control layer) consume the touch, the touch won't be propagated to the next layer (e.g. object layer). This way each layer only has to worry about itself to decide whether to consume the touch or not. If it decides that the touch cannot be used, then it just has to not consume the touch (return NO from ccTouchBegan) and the touch will automatically propagate down the layers.

Hope this helps.

like image 156
Lukman Avatar answered Oct 05 '22 11:10

Lukman