【问题标题】:Best practices for handling touches to a CCSprite with cocos2d使用 cocos2d 处理对 CCSprite 的触摸的最佳实践
【发布时间】:2010-05-24 22:10:51
【问题描述】:

大家好。我刚开始研究 cocos2d 库。我听说如果您习惯使用 ActionScript 进行编程,它是一个很容易进入的库,而且我发现很多概念确实相似。

我开始浏览示例项目(链接here 的示例游戏特别有用),我发现通常不会在 CCSprite 中处理触摸。相反,实例化 CCSprites 的 CCLayer 对触摸事件做出反应,并遍历它创建的 sprite 以检测哪个 CCSprite 被触摸(如果有的话)。

我希望 CCSprites 自己处理它们是否已被触摸,并调用 up 以通知它已被触摸(如果需要)。 /tests/TouchesTest 下的 Paddle 类就是这样做的——它自己处理触摸。

所以,我的问题是:什么是最佳实践?在中心位置处理触摸并遍历子项以查看触摸了什么会更好吗?还是每个孩子都应该处理自己的触摸事件?还是没关系?

我希望每个孩子都处理自己的触摸事件,但我想遵循这方面的最佳做法(如果存在)。谢谢!

【问题讨论】:

标签: iphone ipad cocos2d-iphone


【解决方案1】:

我认为这是一个偏好问题,但我喜欢让精灵检测它是否被子类化 CCSprite 触及。我在我的 CCSprite 子类中创建了一个 getter 方法,该方法从子类中检索状态变量,然后主程序可以相应地采取行动。

这是我的 CCSprite 子类“spuButton”的示例头文件:

    #import "cocos2d.h"

    typedef enum tagButtonState {
        kButtonStatePressed,
        kButtonStateNotPressed
    } ButtonState;

    typedef enum tagButtonStatus {
        kButtonStatusEnabled,
        kButtonStatusDisabled
    } ButtonStatus;

    @interface spuButton : CCSprite <CCTargetedTouchDelegate> {
    @private
        ButtonState state;
        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;

    @end

以下是 .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 (state == kButtonStateNotPressed) return NO;
        if (state == kButtonStatePressed) return YES;
        return NO;
    }

    - (BOOL)isNotPressed {
        if (state == kButtonStateNotPressed) return YES;
        if (state == kButtonStatePressed) return NO;
        return YES;
    }

    - (id)initWithTexture:(CCTexture2D *)aTexture
    {
        if ((self = [super initWithTexture:aTexture]) ) {

            state = kButtonStateNotPressed;
        }

        return self;
    }

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

    - (void)onExit
    {
        [[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 (state == kButtonStatePressed) return NO;
        if ( ![self containsTouchLocation:touch] ) return NO;
        if (buttonStatus == kButtonStatusDisabled) return NO;

        state = kButtonStatePressed;
        [self setTexture:buttonLit];

        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 ([self containsTouchLocation:touch]) return;
        //if (buttonStatus == kButtonStatusDisabled) return NO;

        state = kButtonStateNotPressed;
        [self setTexture:buttonNormal];

    }

    - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
    {

        state = kButtonStateNotPressed;
        [self setTexture:buttonNormal];


    }

@end

希望这对您有所帮助,祝您编码愉快!

增加打勾方法和解释(针对Stephan的问题如下):

要检查按钮的状态,我有一个勾号:基本上触发每一帧并检查所有按钮状态的方法。

    -(void)tick:(ccTime)dt {

do my button checks here....

}

我通过调用我的 spuButton 类中的 isPressed 或 isNotPressed 函数来检查按钮的状态。

for (spuButton *aButton in _fourButtonsArray) {
     if ([aButton isNotPressed]) continue; //this button is not pressed
     .....otherwise record that it is pressed.....
}

然后我做同样的检查,看看它是否已经被释放并做出相应的回应。我这样做是因为我希望能够对多个按钮按下组合做出反应,而且我想在它被按下时做一些事情,然后在它被释放时做其他事情。我使用 ccTouchBegan 和 ccTouchEnded 来更改纹理(精灵图像)并相应地更改状态变量。

【讨论】:

  • 太棒了,看起来很棒!感谢代码 sn-p,那里有一些好的实践/模式可供我学习。
  • 没问题 - 欢迎您。谢谢它我喜欢如何布置它 - 每个人都有自己的风格。 8)这里也有一些好的资源链接:stackoverflow.com/questions/2293457/cocos2d-resources/…stackoverflow.com/questions/4104124/…
  • PS:不要忘记投票给那些有用的答案(以及 cmets 也是如此),这样发帖人就会因此获得荣誉。感谢您选择此作为一个好的答案。 8)
【解决方案2】:

只是添加到这个线程。 Mark 还提供了一个如何实例化 spuButton 的示例,这很有帮助:

Problem with cocos2d and orientation changes, textures are deformed

您还可以修改此示例以传递正常和亮起的按钮图像,如下所示:

+ (id)spuButtonWithTexture:(CCTexture2D *)normalTexture lit:(CCTexture2D *)litTexture

然后做同样的事情:

- (id)initWithTexture:(CCTexture2D *)normalTexture lit:(CCTexture2D *)litTexture

在此方法中,您可以设置两种纹理:

[self setNormalTexture:normalTexture];
[self setLitTexture:litTexture];

【讨论】:

    【解决方案3】:

    这是我的解决方案,基于 CCSprite 希望它对某人有用

    这是受控对象(如播放器或其他东西)的协议:

    @class AGSensitiveButton;
    
    @protocol AGSensitiveButtonControlledObjectProtocol <NSObject>
    @required
    - (void)sensitiveButtonTouchDown:(AGSensitiveButton *)sButton;
    - (void)sensitiveButtonTouchUp:(AGSensitiveButton *)sButton;
    @optional
    - (void)sensitiveTouchButtonKeepPressed:(AGSensitiveButton *)sButton forTime:(ccTime)pressTime;
    @end
    

    .h 文件:

    #import "CCSprite.h"
    #import "cocos2d.h"
    #import "AGSensitiveButtonControlledObjectProtocol.h"
    
    typedef enum {
        AGSensitiveButtonStateNormal = 0,
        AGSensitiveButtonStateHighlighted,
        AGSensitiveButtonStateDisabled
    } AGSensitiveButtonState;
    
    @interface AGSensitiveButton : CCSprite <CCTargetedTouchDelegate>
    
    @property (nonatomic, assign, getter = isEnabled) BOOL enabled;
    @property (nonatomic, assign) ccTime maximumTouchDuration;
    @property (nonatomic, weak) id <AGSensitiveButtonControlledObjectProtocol> controlledObject;
    @property (nonatomic, copy) void (^touchDownHandler)();
    @property (nonatomic, copy) void (^touchUpHandler)();
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture;
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture;
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                 controllerObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject;
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture
                 controlledObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject;
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture
                 controlledObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject
                 touchDownHandler:(void(^)(void))touchDownHandler
                   touchUpHandler:(void(^)(void))touchUpHandler;
    
    - (void)setTexture:(CCTexture2D *)texture forState:(AGSensitiveButtonState)state;
    
    - (BOOL)isHighlighted;
    
    @end
    

    实现.m文件:

    #import "AGSensitiveButton.h"
    
    @interface AGSensitiveButton ()
    @property (nonatomic, assign) AGSensitiveButtonState state;
    @property (nonatomic, strong) NSDictionary *stateTextures;
    @property (nonatomic, assign) ccTime currentTouchTime;
    @end
    
    @implementation AGSensitiveButton
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture {
        return [self buttonWithNormalTexture:normalTexture
                          highlightedTexture:highTexture
                            controllerObject:nil];
    }
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture {
        return [self buttonWithNormalTexture:normalTexture
                          highlightedTexture:highTexture
                             disabledtexture:disabledTexture
                            controlledObject:nil];
    }
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                 controllerObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject {
        return [self buttonWithNormalTexture:normalTexture
                          highlightedTexture:highTexture
                             disabledtexture:nil
                            controlledObject:controlledObject];
    }
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture
                 controlledObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject {
        return [self buttonWithNormalTexture:normalTexture
                          highlightedTexture:highTexture
                             disabledtexture:disabledTexture
                            controlledObject:controlledObject
                            touchDownHandler:NULL
                              touchUpHandler:NULL];
    }
    
    + (id)buttonWithNormalTexture:(CCTexture2D *)normalTexture
               highlightedTexture:(CCTexture2D *)highTexture
                  disabledtexture:(CCTexture2D *)disabledTexture
                 controlledObject:(id <AGSensitiveButtonControlledObjectProtocol>)controlledObject
                 touchDownHandler:(void(^)(void))touchDownHandler
                   touchUpHandler:(void(^)(void))touchUpHandler {
        AGSensitiveButton *button = [[self alloc] initWithTexture:normalTexture
                                                             rect:CGRectMake(0.0, 0.0, normalTexture.contentSize.width, normalTexture.contentSize.height)];
        [button setTexture:normalTexture forState:AGSensitiveButtonStateNormal];
        [button setTexture:highTexture forState:AGSensitiveButtonStateHighlighted];
        [button setTexture:disabledTexture forState:AGSensitiveButtonStateDisabled];
        button.controlledObject = controlledObject;
        button.touchDownHandler = touchDownHandler;
        button.touchUpHandler = touchUpHandler;
        return button;
    }
    
    - (void)setEnabled:(BOOL)enabled {
        [self setupNewState:enabled ? AGSensitiveButtonStateNormal : AGSensitiveButtonStateDisabled];
    }
    
    - (BOOL)isEnabled {
        return (self.state != AGSensitiveButtonStateDisabled);
    }
    
    - (BOOL)isHighlighted {
        return (self.state == AGSensitiveButtonStateHighlighted);
    }
    
    - (void)toggleTextureForCurrentState {
        CCTexture2D *textureToSet = [self.stateTextures objectForKey:[NSNumber numberWithInteger:self.state]];
        if (textureToSet) {
            self.texture = textureToSet;
            self.textureRect = CGRectMake(0.0, 0.0, textureToSet.contentSize.width, textureToSet.contentSize.height);
        }
    }
    
    - (void)setTexture:(CCTexture2D *)texture forState:(AGSensitiveButtonState)state {
        NSMutableDictionary *newStates = self.stateTextures.mutableCopy;
        if (texture) {
            [newStates setObject:texture forKey:[NSNumber numberWithInteger:state]];
        } else {
            [newStates removeObjectForKey:[NSNumber numberWithInteger:state]];
        }
        self.stateTextures = newStates.copy;
    }
    
    - (NSDictionary *)stateTextures {
        if (!_stateTextures) {
            _stateTextures = [[NSDictionary alloc] init];
        }
        return _stateTextures;
    }
    
    - (void)onEnter {
        [super onEnter];
        [self toggleTextureForCurrentState];
        [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
        [self scheduleUpdate];
    }
    
    - (void)onExit {
        [super onExit];
        [self unscheduleUpdate];
        [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
    }
    
    - (void)update:(ccTime)dt {
        if ((self.state == AGSensitiveButtonStateHighlighted) && (self.maximumTouchDuration)) {
            self.currentTouchTime+=dt;
            if (self.currentTouchTime >= self.maximumTouchDuration) {
                [self ccTouchEnded:nil withEvent:nil];
        } else {
            if ([self.controlledObject respondsToSelector:@selector(sensitiveTouchButtonKeepPressed:forTime:)]) {
                [self.controlledObject sensitiveTouchButtonKeepPressed:self forTime:self.currentTouchTime];
            }
        }
        }
    }
    
    - (CGRect)rectForTouches {
        return CGRectMake(-self.contentSize.width/2, -self.contentSize.height/2,
                          self.contentSize.width, self.contentSize.height);
    }
    
    - (void)forwardTouchDownEventIntoHandlers {
        if ([self.controlledObject respondsToSelector:@selector(sensitiveButtonTouchDown:)]) {
            [self.controlledObject sensitiveButtonTouchDown:self];
        }
        if (self.touchDownHandler) {
            self.touchDownHandler();
        }
    }
    
    - (void)forwardTouchUpEventIntoHandlers {
        if ([self.controlledObject respondsToSelector:@selector(sensitiveButtonTouchUp:)]) {
            [self.controlledObject sensitiveButtonTouchUp:self];
        }
        if (self.touchUpHandler) {
            self.touchUpHandler();
        }
    }
    
    - (void)setupNewState:(AGSensitiveButtonState)state {
        if (self.state != state) {
            switch (state) {
                case AGSensitiveButtonStateHighlighted: {
                    [self forwardTouchDownEventIntoHandlers];
                    break;
                }
                default: {
                    if (self.state == AGSensitiveButtonStateHighlighted) {
                        [self forwardTouchUpEventIntoHandlers];
                    }
                    break;
                }
            }
            self.state = state;
            [self toggleTextureForCurrentState];
        }
    }
    
    #pragma mark - CCTargetedTouchDelegate
    
    - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
        if ((self.state != AGSensitiveButtonStateNormal) || (!CGRectContainsPoint([self rectForTouches], [self convertTouchToNodeSpaceAR:touch]))) {
            return NO;
        }
        self.currentTouchTime = 0.0;
        [self setupNewState:AGSensitiveButtonStateHighlighted];
        return YES;
    }
    
    - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
        if (self.state == AGSensitiveButtonStateHighlighted) {
            if (!CGRectContainsPoint([self rectForTouches], [self convertTouchToNodeSpaceAR:touch])) {
                [self ccTouchEnded:touch withEvent:event];
            }
        }
    }
    
    - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
        if (self.state == AGSensitiveButtonStateHighlighted) {
            [self setupNewState:AGSensitiveButtonStateNormal];
        }
    }
    
    @end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多