ios - 创建其 CCSprites 是 CCBatchNode 的子节点的可触摸 CCNode 时出现问题

标签 ios cocos2d-iphone

这可能需要一些解释,但在这里,非常感谢任何见解。

简短版本:如何创建一个 TouchableButton(它自己检测触摸),其 CCSprite 图像是另一个类中 CCSpriteBatchNode 的子节点? (通常 CCSprite 是 TouchableButton 本身的子项)。

长版:

我正在使用 Cocos2d 构建游戏。该游戏着重于一个充满代理(AgentView 类:CCNode)的景观(类 EnvironmentView:CCLayer),这些代理(类 AgentView:CCNode)四处奔跑并相互交互。

EnvironmentView 维护一个 AgentView 对象列表并根据需要创建/销毁它们(取决于它们的交互方式)。

每个 AgentView 都有一个 CCSprite @property,它被添加为 CCBatchNode 的子节点(EnvironmentView 的 @property),后者被添加为 EnvironmentView 的子节点。

我正在尝试实现一项功能,让用户可以触摸代理并将它们从景观中的一个地方移动到另一个地方。

因为有很多代理在 EnvironmentView 中四处移动,所以我不想使用标准方法获取触摸位置并循环遍历所有 AgentView CCSprite 以查看触摸是否击中其中之一(这会降低帧率相当大,请:对促进这种方法的答案不感兴趣)。

相反,我想让每个 AgentView 成为一个可触摸节点(一个知道何时被触摸的节点,而不是一个被告知何时被触摸的节点(上述方法))。

基本上,我想用某种 TouchableButton 对象替换或扩充每个 AgentView 的 CCSprite。

我正在使用一个类(我们称它为 TouchableButton),该类将这种方法用于我游戏中与 UI 相关的按钮,它们知道何时被触摸,而无需在其父层中实现任何 CCTouchesBegan 方法。但是我一直无法为这个用例调整 TouchableButton,原因如下:

TouchableButtons 将 CCSprite 作为初始参数。此 CCSprite 设置为按钮的可触摸部分,并作为按钮本身的子项添加。因为我还在 EnvironmentView 中将 CCSprite 添加为 CCSpriteBatchNode 的子对象,所以我收到一个错误(无法将某些东西作为子对象添加两次到两个不同的父对象)。我如何构建事物以避免这种冲突?

在此先感谢您的帮助!

最佳答案

简短的回答:你不能。

长答案:您可以获得相同的效果,但方式不同。

CCSpriteBatchNode 的工作原理是在具有公共(public)纹理( Sprite 表)的单个 glDrawElements 调用中绘制其所有 CCSprite 子节点,这就是它具有如此出色性能的原因。但结果是,每个 child 都必须是 Sprite ,如果您将 child 添加到 Sprite 中,它将被忽略。

因此,此时您唯一的办法是将 CCSprite 子类化为按钮并复制很多功能,如下所示:

按钮 Sprite .h:

//
//  ButtonSprite.h
//  TestButtonSprite
//
//  Created by Karl Stenerud on 9/1/11.
//

#import "cocos2d.h"

@class ButtonSprite;

typedef void (^ButtonPressCallback)(ButtonSprite* button);

/**
 * A sprite that can respond to touches.
 * Most of this code was taken from CCLayer.
 */
@interface ButtonSprite : CCSprite <CCStandardTouchDelegate, CCTargetedTouchDelegate>
{
    BOOL touchEnabled_;
    int touchPriority_;
    BOOL swallowTouches_;
    BOOL registeredWithDispatcher_;

    BOOL touchInProgress_;
    BOOL buttonWasDown_;

    ButtonPressCallback onButtonPressedCallback_;
}

/** Priority position in which this node will be handled (lower = sooner) */
@property(nonatomic,readwrite,assign) int touchPriority;

/** If true, no other node will respond to touches this one responds to */
@property(nonatomic,readwrite,assign) BOOL swallowTouches;

/** If true, this node responds to touches. */
@property(nonatomic,readwrite,assign) BOOL touchEnabled;

/** Called whenever a full touch completes */
@property(nonatomic,readwrite,copy) ButtonPressCallback onButtonPressedCallback;

/** Called when a button press is detected. */
- (void) onButtonPressed;

/** Called when a button is pushed down. */
- (void) onButtonDown;

/** Called when a button is released. */
- (void) onButtonUp;

- (BOOL) touchHitsSelf:(UITouch*) touch;

- (BOOL) touch:(UITouch*) touch hitsNode:(CCNode*) node;

@end

ButtonSprite.m:

//
//  ButtonSprite.m
//  TestButtonSprite
//
//  Created by Karl Stenerud on 9/1/11.
//

#import "ButtonSprite.h"


@interface ButtonSprite ()

- (void) registerWithTouchDispatcher;
- (void) unregisterWithTouchDispatcher;

@end

@implementation ButtonSprite

@synthesize touchEnabled = touchEnabled_;
@synthesize touchPriority = touchPriority_;
@synthesize swallowTouches = swallowTouches_;
@synthesize onButtonPressedCallback = onButtonPressedCallback_;

- (id) init
{
    if(nil != (self = [super init]))
    {
        touchPriority_ = 0;
        swallowTouches_ = YES;
        touchEnabled_ = YES;

        self.isRelativeAnchorPoint = YES;
        self.anchorPoint = ccp(0.5, 0.5);
    }
    return self;
}

- (void) dealloc
{
    [self unregisterWithTouchDispatcher];
    [onButtonPressedCallback_ release];

    [super dealloc];
}

- (void) registerWithTouchDispatcher
{
    [self unregisterWithTouchDispatcher];

    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:self.touchPriority swallowsTouches:self.swallowTouches];
    registeredWithDispatcher_ = YES;
}

- (void) unregisterWithTouchDispatcher
{
    if(registeredWithDispatcher_)
    {
        [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
        registeredWithDispatcher_ = NO;
    }
}

- (void) setSwallowTouches:(BOOL) value
{
    if(swallowTouches_ != value)
    {
        swallowTouches_ = value;

        if(isRunning_ && touchEnabled_)
        {
            [self registerWithTouchDispatcher];
        }
    }
}

- (void) setTouchPriority:(int) value
{
    if(touchPriority_ != value)
    {
        touchPriority_ = value;
        if(isRunning_ && touchEnabled_)
        {
            [self registerWithTouchDispatcher];
        }
    }
}

-(void) setTouchEnabled:(BOOL)enabled
{
    if( touchEnabled_ != enabled )
    {
        touchEnabled_ = enabled;
        if( isRunning_ )
        {
            if( touchEnabled_ )
            {
                [self registerWithTouchDispatcher];
            }
            else
            {
                [self unregisterWithTouchDispatcher];
            }
        }
    }
}

- (void)cleanup
{
    self.touchEnabled = NO;
}

#pragma mark TouchableNode - Callbacks
-(void) onEnter
{
    // register 'parent' nodes first
    // since events are propagated in reverse order
    if (self.touchEnabled)
    {
        [self registerWithTouchDispatcher];
    }

    // then iterate over all the children
    [super onEnter];
}

-(void) onExit
{
    if(self.touchEnabled)
    {
        [self unregisterWithTouchDispatcher];
    }

    [super onExit];
}

-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    if([self touchHitsSelf:touch])
    {
        touchInProgress_ = YES;
        buttonWasDown_ = YES;
        [self onButtonDown];
        return YES;
    }
    return NO;
}

-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{   
    if(touchInProgress_)
    {
        if([self touchHitsSelf:touch])
        {
            if(!buttonWasDown_)
            {
                [self onButtonDown];
            }
        }
        else
        {
            if(buttonWasDown_)
            {
                [self onButtonUp];
            }
        }
    }
}

-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{   
    if(buttonWasDown_)
    {
        [self onButtonUp];
    }
    if(touchInProgress_ && [self touchHitsSelf:touch])
    {
        touchInProgress_ = NO;
        [self onButtonPressed];
    }
}

-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
    if(buttonWasDown_)
    {
        [self onButtonUp];
    }
    touchInProgress_ = NO;
}

- (void) onButtonDown
{
    buttonWasDown_ = YES;
}

- (void) onButtonUp
{
    buttonWasDown_ = NO;
}

- (void) onButtonPressed
{
    self.onButtonPressedCallback(self);
}


- (BOOL) touchHitsSelf:(UITouch*) touch
{
    return [self touch:touch hitsNode:self];
}

- (BOOL) touch:(UITouch*) touch hitsNode:(CCNode*) node
{
    CGRect r = CGRectMake(0, 0, node.contentSize.width, node.contentSize.height);
    CGPoint local = [node convertTouchToNodeSpace:touch];

    return CGRectContainsPoint(r, local);
}

@end

像这样使用它:

    ButtonSprite* myButton = [ButtonSprite spriteWithFile:@"button_image.png"];
    myButton.onButtonPressedCallback = ^(ButtonSprite* button)
    {
        NSLog(@"Pressed!");
    };
    [self addChild: myButton];

请注意,如果您在批处理节点中使用此类,则它不得有自己的任何子节点!

关于ios - 创建其 CCSprites 是 CCBatchNode 的子节点的可触摸 CCNode 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7271719/

相关文章:

cocos2d-iphone - Cocos2d : How to play a video in the background of a CCLayer

iphone - 使用 CCMenuAdvanced - Cocos2d

iPhone游戏2D阴影

ios - iOS 13如何将UIDocumentPickerViewController与App Group一起使用?

ios - 印象笔记SDK : Cannot upload a note into my Sandbox account

ios - 预期的标识符或'('

cocos2d-iphone - 需要帮助理解雪碧和纹理

ios - 核心数据并发类型未按预期工作

用于分发应用程序的 iOS Enterprise 选项

ios - 在 Collection View 中显示长单元格(如矩形)时出现问题