2

I would like some of my Sprite Kit nodes to behave like UIButtons. I tried 2 approaches:

1) Use touchesBegan: - this works if a user is careful, but seems to fire multiple times, faster than I can disable interaction, resulting in a button being able to be activated multiple times:

   spriteNode.userInteractionEnabled = YES;

//causes the following to fire when node receives touch
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

        self.userInteractionEnabled = NO;

        DLog(@"Do node action");
        self.userInteractionEnabled = YES;


    }

2)I switched to Kobold-Kit as a layer on top of iOS Sprite Kit. One of the things it allows me to do is add button - like behavior to any node. However, I'm running into an issue where a if I have 2 buttons stacked on top of each other, tapping the top button activates both. Boolean flags can prevent repeated interactions in this case. I tracked the issue of stacked buttons firing together to this call within KKScene:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];

    for (id observer in _inputObservers)
    {
        if ([observer respondsToSelector:@selector(touchesBegan:withEvent:)])
        {
            [observer touchesBegan:touches withEvent:event];
        }
    }
}

The scene simply sends notifications to all nodes within the scene. This causes buttons behaviors that are stacked on top of each other to fire together.

Is there a way or example that shows how to properly arrange sprite nodes to allow behavior similar to UIButton, where I can have only the top button activate, and each activation disables the button for a short time afterwards?

4

2 回答 2

1
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;

self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureStateChanged:)];
self.tapGesture.delegate = self;
[self.view addGestureRecognizer:self.tapGesture];


- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer == self.tapGesture) {
        return CGRectContainsPoint(self.neededSprite.frame, [self convertPoint:[sender locationInView:self.view] toNode:self]);
    }
    return YES;
}



 - (void)tapGestureStateChanged:(UITapGestureRecognizer *)sender
    {
        if (sender.state == UIGestureRecognizerStateRecognized) {
/* do stuff here */
    }
    }
于 2013-12-17T18:22:08.087 回答
0

The SpriteKit Programming Guide suggests using a SKNode name to gate the behavior that you want to achieve when touched. The example in the section called "Using Actions to Animate Scenes" overrides the touchesBegan:withEvent: method and only runs when the name is not nil. When you're done, just reset the name so that you can catch the next touch.

- (void)touchesBegan:(NSSet *) touches withEvent:(UIEvent *)event {
    SKNode *helloNode = [self childNodeWithName:@"helloNode"];
    if (helloNode != nil) {
        helloNode.name = nil;
        SKAction *moveUp = [SKAction moveByX: 0 y: 100.0 duration: 0.5];
        SKAction *zoom = [SKAction scaleTo: 2.0 duration: 0.25];
        SKAction *pause = [SKAction waitForDuration: 0.5];
        SKAction *fadeAway = [SKAction fadeOutWithDuration: 0.25];
        SKAction *remove = [SKAction removeFromParent];
        SKAction *moveSequence = [SKAction sequence:@[moveUp, zoom, pause, fadeAway, remove]];
        [helloNode runAction: moveSequence];
    }
}

Another idea would be to perform your re-enabling of the user interaction to touchesEnded.

于 2015-06-23T15:01:37.820 回答