Проблема создания осязаемых CCNode, чьи CCSprites являются дочерними для CCBatchNode - PullRequest
4 голосов
/ 01 сентября 2011

Это может потребовать небольшого количества объяснения, но здесь, действительно, буду признателен за любую проницательность.

Краткая версия: Как я могу создать TouchableButton (который самостоятельно обнаруживает касания), чье изображение CCSprite является дочерним по отношению к CCSpriteBatchNode в другом классе? (Обычно CCSprite является дочерним элементом самого TouchableButton).

Длинная версия:

Я создаю игру, используя Cocos2d. Игра фокусируется на ландшафте (класс EnvironmentView: CCLayer), заполненном агентами (класс AgentView: CCNode), которые бегают и взаимодействуют друг с другом.

EnvironmentView поддерживает список объектов AgentView и создает / уничтожает их по мере необходимости (в зависимости от того, как они взаимодействуют).

Каждый AgentView имеет CCSprite @property, который добавляется как дочерний элемент для CCBatchNode (@property of EnvironmentView), который добавляется как дочерний элемент для EnvironmentView.

Я пытаюсь реализовать функцию, с помощью которой пользователи могут касаться агентов и перемещать их из одного места в другое в ландшафте.

Поскольку в EnvironmentView перемещается много агентов, я не хочу использовать стандартный подход для определения местоположения касания и прохождения всех CCSprites AgentView, чтобы увидеть, касается ли касание одного из них (это замедлит частоту кадров). значительно, пожалуйста: не заинтересованы в ответах, продвигающих этот подход).

Вместо этого я хотел бы превратить каждый AgentView в сенсорный узел (узел, который знает, когда к нему прикоснулись, а не узел, который сообщает, когда к нему прикоснулись (подход, упомянутый выше)).

По сути, я хотел бы заменить или дополнить CCSprite каждого AgentView каким-либо объектом TouchableButton.

Я использую класс (назовем его TouchableButton), который использует этот подход для кнопок, связанных с пользовательским интерфейсом, в моей игре, они знают, когда к ним прикасаются, без реализации каких-либо методов CCTouchesBegan на их родительском уровне. Но я не смог адаптировать TouchableButton для этого варианта использования, вот почему:

TouchableButtons принимают CCSprite в качестве параметра init. Этот CCSprite устанавливается как сенсорная часть кнопки и добавляется как дочерний элемент самой кнопки. Поскольку я также добавляю CCSprite в качестве дочернего элемента для CCSpriteBatchNode в EnvironmentView, я получаю сообщение об ошибке (не могу дважды добавить что-либо в качестве дочернего для двух разных родительских объектов). Как я могу структурировать вещи, чтобы избежать этого конфликта?

Заранее спасибо за любую помощь!

Ответы [ 2 ]

2 голосов
/ 01 сентября 2011

Краткий ответ: Вы не можете.

Длинный ответ: Вы можете получить тот же эффект, но не таким же образом.

CCSpriteBatchNode работает, рисуя все свои дочерние элементы CCSprite в одном вызове glDrawElements с общей текстурой (спрайт-листом), что и дает ему такую ​​хорошую производительность. Но в результате каждый дочерний элемент ДОЛЖЕН быть спрайтом, и если вы добавите дочерний элемент в спрайт, он будет проигнорирован.

Итак, на данный момент вы можете использовать только подкласс CCSprite в качестве кнопки и дублировать множество функций, например:

ButtonSprite.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];

Обратите внимание, что если вы используете этот класс в пакетном узле, он НЕ ДОЛЖЕН иметь своих дочерних элементов!

0 голосов
/ 01 сентября 2011

Я сам много с этим боролся, и я пришел к выводу, что это невозможно, и вы должны использовать отдельный файл изображения для каждой кнопки.

Я надеялся, что эта функция появится в1.0, но я не думаю, что это так.

Надеюсь, что я ошибаюсь!:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...