Problema al crear CCNodes que pueden tocarse, cuyos CCSprites son hijos de un CCBatchNode

Esto puede tomar un poco de explicación, pero aquí va, realmente agradecería cualquier idea.

La versión corta: ¿Cómo puedo crear un TouchableButton (que detecta toques por sí mismo) cuya image CCSprite es un elemento secundario de CCSpriteBatchNode en otra class? (Normalmente, el CCSprite sería un elemento secundario del TouchableButton).

La versión larga:

Estoy construyendo un juego con Cocos2d. El juego se centra en un paisaje (class EnvironmentView: CCLayer) lleno de agentes (class AgentView: CCNode) que se ejecutan e interactúan entre sí.

EnvironmentView mantiene una list de objects AgentView y los crea / los destruye según sea necesario (dependiendo de cómo interactúen).

Cada AgentView tiene un CCSprite @property que se agrega como hijo de un CCBatchNode (una propiedad @ de EnvironmentView) que se agrega como hijo de EnvironmentView.

Estoy intentando implementar una function donde los usuarios puedan tocar a los agentes y moverlos de un lugar a otro en el paisaje.

Debido a que hay muchos agentes desplazándose en EnvironmentView, no quiero utilizar el enfoque estándar para get la location táctil y recorrer todos los CCSprites de AgentView para ver si el toque llega a uno de ellos (esto ralentizaría considerablemente la velocidad de fotogtwigs, por favor : no le interesan las respuestas que promueven este enfoque).

En cambio, me gustaría convertir cada AgentView en un nodo táctil (un nodo que sabe cuándo se toca en lugar de un nodo que se dice cuando se toca (enfoque mencionado anteriormente)).

Básicamente, me gustaría replace o boost cada CCSprite de AgentView con algún tipo de object TouchableButton.

Estoy usando una class (llamémosla TouchableButton) que utiliza este enfoque para los botones relacionados con la interfaz de usuario en mi juego, saben cuándo se tocan sin implementar ningún método CCTouchesBegan en su capa primaria. Pero no he podido adaptar TouchableButton para este caso de uso, he aquí por qué:

TouchableButtons toma un CCSprite como un parámetro init. Este CCSprite está configurado para que sea la parte táctil del button y se agrega como un elemento secundario del button. Porque también estoy agregando CCSprite como hijo del CCSpriteBatchNode en EnvironmentView, obtengo un error (no puedo agregar algo como un niño dos veces a dos objects principales diferentes). ¿Cómo puedo estructurar las cosas para evitar este conflicto?

¡Gracias de antemano por cualquier ayuda!

La respuesta corta: no puedes.

La respuesta larga: puede get el mismo efecto, pero no de la misma manera.

CCSpriteBatchNode funciona dibujando todos sus hijos CCSprite en una sola llamada glDrawElements con una textura común (hoja de sprite), que es lo que le da un buen performance. Pero como resultado, cada niño DEBE ser un sprite, y si agrega un hijo a un sprite, se ignorará.

Por lo tanto, su único recurso en este momento es subclass CCSprite como un button y duplicar una gran cantidad de funciones, así:

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 registenetworkingWithDispatcher_; 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 shanetworkingDispatcher] addTargetedDelegate:self priority:self.touchPriority swallowsTouches:self.swallowTouches]; registenetworkingWithDispatcher_ = YES; } - (void) unregisterWithTouchDispatcher { if(registenetworkingWithDispatcher_) { [[CCTouchDispatcher shanetworkingDispatcher] removeDelegate:self]; registenetworkingWithDispatcher_ = 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 

Úselo de esta manera:

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

Tenga en count que si usa esta class en un nodo por lotes, ¡NO DEBE tener hijos propios!

He luchado mucho con esto, yo mismo, y mi conclusión es que esto no es posible y tienes que usar un file de image por separado para cada button.

Esperaba que esta característica apareciera en 1.0 pero no creo que así sea.

Con la esperanza de que me equivoque, ¡sin embargo! 🙂