Cocos2d处理多层触摸

时间:2022-09-10 18:27:10

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:

我已经忙了几天试图找出如何在我的Cocos2d项目中处理触摸。情况跟正常情况有点不同。我有几个不同的游戏图层,上面有我需要通过触摸控制的项目:

  • ControlLayer: Holds the game controls (movement, action button). This layer is on top.
  • ControlLayer:保持游戏控制(移动,动作按钮)。该层位于顶部。
  • GameplayLayer: Holds the game objects (CCSprites). This layer is directly beneath the ControlLayer.
  • GameplayLayer:持有游戏对象(CCSprites)。该图层位于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.

现在,我的触摸在ControlLayer中工作得很好,我可以移动我的可玩角色并让他跳跃并做其他愚蠢的事情。但我无法掌握如何实现我的一些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.

到目前为止我收集的信息让我觉得我需要从控制层获取所有触摸输入。然后我不知何故需要将触摸信息“级联”到GameplayLayer,以便我可以在那里处理输入。另一个选择是让我通过某种方式创建一个指向应该可触摸的对象的指针来从我的精灵中获取CGRect信息。我应该能够在ControlLayer中使用该信息来检查该项目中的每个项目是否触摸了该项目。

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.

这样做的最佳选择是什么,我该如何实现?我对使用cocoa和Objective C进行编程是个新手,所以我不确定这个语言的最佳选择是什么,以及如何在另一个类中访问sprite CGRect信息([mySpriteName boundingBox])然后它是呈现在。

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.

目前,我确定让它工作的唯一方法是为每个CCSprite位置创建重复的CGRects,因此我可以检查它们,但我知道这不是正确的方法。

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

我到目前为止(测试)是这样的: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!

在此先感谢帮助我!

1 个解决方案

#1


27  

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.

解决问题的最简单最简单的方法是使用ccTouchBegan / Moved / Ended代替ccTouchesBegan / Moved / Ended。这意味着,您正在处理特定时刻的单次触摸,因此您可以避免混淆多次触摸,加上ccTouchBegan最重要的功能是CCLayer可以“消耗”触摸并阻止它传播到下一层。下面的代码示例后的更多解释。

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

以下是执行此操作的步骤。在应该处理触摸事件的所有CCLayer子类中实现这些方法集:

First, register with CCTouchDispatcher:

首先,注册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):

接下来,实现ccTouchBegan,下面的示例来自我创建的游戏(当然省略了一些部分):

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

最后像ccTouches *同行一样实现ccTouchMoved和ccTouchEnded,除了它们处理单点触摸而不是触摸。传递给这些方法的触摸仅限于ccTouchBegan中使用的触摸,因此无需在这两种方法中进行验证。

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.

基本上这就是它的工作原理。触摸事件由CCScene基于z排序逐个传递到其每个CCLayer(即从顶层到底层开始),直到任何层消耗触摸。因此,如果顶部的层(例如控制层)消耗触摸,则触摸将不会传播到下一层(例如,对象层)。这样每个层只需要担心自己决定是否消耗触摸。如果它决定不能使用触摸,那么它只需要不消耗触摸(从ccTouchBegan返回NO)并且触摸将自动向下传播到层。

Hope this helps.

希望这可以帮助。

#1


27  

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.

解决问题的最简单最简单的方法是使用ccTouchBegan / Moved / Ended代替ccTouchesBegan / Moved / Ended。这意味着,您正在处理特定时刻的单次触摸,因此您可以避免混淆多次触摸,加上ccTouchBegan最重要的功能是CCLayer可以“消耗”触摸并阻止它传播到下一层。下面的代码示例后的更多解释。

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

以下是执行此操作的步骤。在应该处理触摸事件的所有CCLayer子类中实现这些方法集:

First, register with CCTouchDispatcher:

首先,注册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):

接下来,实现ccTouchBegan,下面的示例来自我创建的游戏(当然省略了一些部分):

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

最后像ccTouches *同行一样实现ccTouchMoved和ccTouchEnded,除了它们处理单点触摸而不是触摸。传递给这些方法的触摸仅限于ccTouchBegan中使用的触摸,因此无需在这两种方法中进行验证。

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.

基本上这就是它的工作原理。触摸事件由CCScene基于z排序逐个传递到其每个CCLayer(即从顶层到底层开始),直到任何层消耗触摸。因此,如果顶部的层(例如控制层)消耗触摸,则触摸将不会传播到下一层(例如,对象层)。这样每个层只需要担心自己决定是否消耗触摸。如果它决定不能使用触摸,那么它只需要不消耗触摸(从ccTouchBegan返回NO)并且触摸将自动向下传播到层。

Hope this helps.

希望这可以帮助。