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.