iOS, как добиться подобного поведения UIButton для узлов в Sprite Kit?

Я хотел бы, чтобы некоторые из моих узлов Sprite Kit вели себя как кнопки UIB. Я попробовал 2 подхода:

1) Использование touchesBegan: - это работает, если пользователь осторожен, но, кажется, срабатывает несколько раз, быстрее, чем я могу отключить взаимодействие, в результате чего кнопка может быть активирована несколько раз:

   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) Я переключился на Kobold-Kit как слой поверх iOS Sprite Kit. Одна из вещей, которую он позволяет мне сделать, - добавить поведение, похожее на кнопку, к любому узлу. Тем не менее, я столкнулся с проблемой, когда если у меня есть две кнопки, расположенные друг над другом, нажатие верхней кнопки активирует обе. Булевы флаги могут предотвратить повторные взаимодействия в этом случае. Я отследил проблему сгруппированных кнопок, запускающих этот вызов в 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];
        }
    }
}

Сцена просто отправляет уведомления всем узлам в пределах сцены. Это приводит к тому, что поведение кнопок, расположенных друг над другом, срабатывает вместе.

Есть ли способ или пример, который показывает, как правильно расположить узлы спрайтов, чтобы позволить поведение, подобное UIButton, где я могу активировать только верхнюю кнопку, и каждая активация отключает кнопку на короткое время после этого?

2 ответа

@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 */
    }
    }

Руководство по программированию SpriteKit предлагает использовать имя SKNode для определения поведения, которого вы хотите достичь при касании. Пример в разделе "Использование действий для анимации сцен" переопределяет метод touchesBegan:withEvent: и выполняется только тогда, когда имя не равно nil. Когда вы закончите, просто сбросьте имя, чтобы вы могли поймать следующее касание.

- (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];
    }
}

Другая идея заключается в том, чтобы повторно включить взаимодействие с пользователем для touchesEnded.

Другие вопросы по тегам