9

我放置了一个精灵 A(子类化以接收命中(userInteractionEnabled 为 YES)),然后放置一个普通的精灵 B,它不会在其上接受命中(userInteractionEnabled 默认为 NO),完全覆盖精灵 A。

点击精灵 B,我假设精灵 A 会得到触摸,但没有任何反应。文档中关于此事的部分如下。

我觉得这里有些不清楚,因为精灵 B 似乎仍然接收到触摸但将其丢弃。或者,从可能的触摸接收器中删除 spriteA,因为它不可见。

来自文档: https ://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html#//apple_ref/doc/uid/TP40013043-CH3-SW7

对于在命中测试期间要考虑的节点,其 userInteractionEnabled 属性必须设置为 YES。对于除场景节点之外的任何节点,默认值为 NO。想要接收事件的节点需要从其父类(iOS 上的 UIResponder 和 OS X 上的 NSResponder)实现适当的响应器方法。这是您必须在 Sprite Kit 中实现特定于平台的代码的少数几个地方之一

有任何解决这个问题的方法吗?只要某些东西的 userInteractionEnabled 为 NO,它就不会干扰其他触摸接收器。

更新:即使将精灵 B 的 alpha 设置为 0.2,使精灵 A 非常可见,也不会使精灵 A 可触摸。尽管没有启用交互,Sprite B 只是完全“吞下”了触摸。

4

5 回答 5

10

这是我的解决方案,直到 Apple 以正确的行为更新 SpriteKit,或者有人真正弄清楚如何按照我们的意愿使用它。

https://gist.github.com/bobmoff/7110052

将该文件添加到您的项目中并将其导入您的 Prefix 标头中。现在触摸应该按预期的方式工作。

于 2013-10-22T23:38:49.403 回答
2

我的解决方案不需要 isKindOfClass 之类的东西......

在您的 SKScene 界面中:

@property (nonatomic, strong) SKNode*   touchTargetNode;
// this will be the target node for touchesCancelled, touchesMoved, touchesEnded

在您的 SKScene 实施中

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch*    touch   = [touches anyObject];
    NSArray*    nodes   = [self nodesAtPoint:[touch locationInNode:self]];

    self.touchTargetNode = nil;

    for( SKNode* node in [nodes reverseObjectEnumerator] )
    {
        if( [node conformsToProtocol:@protocol(CSTouchableNode)] )
        {
            SKNode<CSTouchableNode>*    touchable   = (SKNode<CSTouchableNode>*)node;

            if( touchable.wantsTouchEvents )
            {
                self.touchTargetNode = node;
                [node touchesBegan:touches
                         withEvent:event];
                break;
            }
        }
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesCancelled:touches
                                 withEvent:event];
    self.touchTargetNode = nil;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesMoved:touches
                             withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesEnded:touches
                                 withEvent:event];
    self.touchTargetNode = nil;
}

您将需要定义 CSTouchableNode 协议...随意称呼它:)

@protocol CSTouchableNode <NSObject>

- (BOOL) wantsTouchEvents;
- (void) setWantsTouchEvents:(BOOL)wantsTouchEvents;

@end

对于您希望可触摸的 SKNode,它们需要符合 CSTouchableNode 协议。您需要根据需要在您的课程中添加如下内容。

@property (nonatomic, assign) BOOL wantsTouchEvents;

这样做,点击节点就像我期望的那样工作。当 Apple 修复“userInteractionEnabled”错误时,它不应该中断。是的,这是一个错误。一个愚蠢的错误。傻苹果。

更新

SpriteKit 中还有另一个错误……节点的 z 顺序很奇怪。我见过奇怪的节点顺序......因此我倾向于为需要它的节点/场景强制使用 zPosition。

我已经更新了我的 SKScene 实现,以根据 zPosition 对节点进行排序。

nodes = [nodes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:false]]];
for( SKNode* node in nodes )
{
    ...
}
于 2013-11-09T08:10:19.407 回答
1

具有更少代码行的解决方案。请记住在要接收触摸的每个节点中设置 userInteractionEnabled。

- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
    CGPoint touchLocation = self.positionInScene;
    SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];

    NSArray *nodes   = [self nodesAtPoint:touchLocation];
    for (SKSpriteNode *node in [nodes reverseObjectEnumerator]) {
        NSLog(@"Node in touch array: %@. Touch enabled: %hhd", node.name, node.isUserInteractionEnabled);
        if (node.isUserInteractionEnabled == 1)
            touchedNode = node;
    }
    NSLog(@"Newly selected node: %@", touchedNode);
}
于 2014-03-03T20:42:11.527 回答
1

我认为这可能是一种设计选择,但肯定不是很灵活。我更喜欢使用 nodeAtPoint: 结合 isKindOfClass: 方法在场景级别处理 Sprite Kit 中可以重叠的对象(如游戏对象)。对于像按钮这样通常不重叠并放置在不同层中的对象(如覆盖层和按钮),我会在它们的类中处理它们的用户交互。我希望我的触摸事件在 Sparrow 框架中“冒泡”节点树,但我担心它更像是一个功能请求而不是错误报告。

PS:通过在场景级别处理触摸,您可以轻松地触摸和移动节点,即使它们完全被不可移动和不可触摸的节点覆盖!

于 2013-10-23T14:42:16.927 回答
1

这是获取触摸等的示例。

现场

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */

        HeroSprite *newHero = [HeroSprite new];
        newHero.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
        [self addChild:newHero];

        SKSpriteNode *overLapSprite = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(70, 70)];
        overLapSprite.position = CGPointMake(CGRectGetMidX(self.frame) + 30, CGRectGetMidY(self.frame));
        overLapSprite.name = @"overlap";
        [self addChild:overLapSprite];
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];

    if ([node.name isEqualToString:@"heroNode"]) {
        NSLog(@"Scene detect hit on hero");
    }
    if ([node.name isEqualToString:@"overlap"]) {
        NSLog(@"overlap detect hit");
    }

    // go through all the nodes at the point
    NSArray *allNodes = [self nodesAtPoint:location];
    for (SKNode *aNode in allNodes) {
        NSLog(@"Loop; node name = %@", aNode.name);
    }
}

子类化精灵/英雄

- (id) init {
    if (self = [super init]) {
        [self setUpHeroDetails];
        self.userInteractionEnabled = YES;
    }
    return self;
}

-(void) setUpHeroDetails
{
    self.name = @"heroNode";
    SKSpriteNode *heroImage = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
    [self addChild:heroImage];
}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint locationA = [touch locationInNode:self];
    CGPoint locationB = [touch locationInNode:self.parent];
    NSLog(@"Hero got Hit at, %@ %@", NSStringFromCGPoint(locationA), NSStringFromCGPoint(locationB));
}

@end

尝试了很多东西,但触摸没有通过重叠的精灵。我想您可以使用场景类通过循环遍历节点来检测触摸,然后直接调用该节点。

当粒子发射器覆盖按钮时,我遇到了这个问题......

于 2013-10-22T08:15:06.457 回答