touchesEnded:withEvent: 未检测到 SpriteKit 中的顶部节点
touchesEnded:withEvent: not detecting the top node in SpriteKit
我有一个自定义 class Object
派生自 SKSpriteNode
和一个 SKLabelNode
成员变量。
#import <SpriteKit/SpriteKit.h>
@interface Object : SKSpriteNode {
SKLabelNode* n;
}
@end
在实现中,我设置了SKLabelNode
.
#import "Object.h"
@implementation Object
- (instancetype) initWithImageNamed:(NSString *)name {
self = [super initWithImageNamed:name];
n = [[SKLabelNode alloc] initWithFontNamed:@"Courier"];
n.text = @"Hello";
n.zPosition = -1;
//[self addChild:n];
return self;
}
注意: 我还没有将 SKLabelNode
作为 child 添加到 Object
。我把这行注释掉了。
我有一个单独的 class 来自 SKScene
。在此 class 中,我将 Object
的实例添加为 child。这是我在 class:
中的 touchesBegan:withEvent:
方法
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray* nodes = [self nodesAtPoint:loc]; //to find all nodes at the touch location
SKNode* n = [self nodeAtPoint:loc]; //to find the top most node at the touch location
NSLog(@"TOP: %@",[n class]);
for(SKNode* node in nodes) {
NSLog(@"%@",[node class]);
}
}
当我在我的 SKScene
class 中点击 Object
的实例时,它按我预期的那样工作。它检测到节点是 Object
的实例。它记录:
TOP: Object
Object
现在,如果我回到 Object
class 并取消注释 [self addChild:n]
,这样 SKLabelNode
就会作为 child 添加到 Object
,这是记录:
TOP: SKLabelNode
Object
SKLabelNode
SKSpriteNode
为什么将 SKLabelNode
作为 child 添加到派生自 SKSpriteNode
的 class 会导致触摸检测到 object 本身,以及一个 SKLabelNode
和一个 SKSpriteNode
?
另外,为什么SKLabelNode
在上面?我将它的 zPosition 设置为 -1,所以我假设在另一个 class 的 touchesBegan:withEvent:
中,Object
会被检测为在顶部?
当然,我没有理解一个重要的概念。
如果你用nodeAtPoint
检查-touchesEnded:
中的节点,它肯定会return触摸点的最上面的节点,在这种情况下,是SKLabelNode
。有几种方法可以解决这个问题:
让子class检测它自己的触摸
最干净的方法是让 Object subclass 检测它自己的触摸。这可以通过将 userInteractionEnabled
属性 设置为 YES
来完成。这可以在 init 方法本身中完成。
-(instanceType) init {
if (self = [super init]) {
self.userInteractionEnabled = YES;
}
return self;
}
现在,在 class 本身中实现触摸委托方法。由于 LabelNode 的 userInteractionEnabled
属性 默认设置为 NO
,触摸委托仍会在点击时触发。
您可以在触摸委托中使用委托、NSNotificationCenter 或直接向父级发送消息。
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
//Send a message to the delegate, which is the SKScene, should be included in protocol
[self.delegate handleTouchOnObjectInstance:self];
//or, send a message directly to the SKScene, the method should be included in the interface.
MyScene *myScene = (MyScene*)self.scene;
[myScene handleTouchOnObjectInstance:self];
}
正在检查节点的父节点
在SKScene的touch delegate中,可以查看nodeAtPoint
return的SKLabelNode的父节点是否是Cclass.[=28的实例=]
//Inside the touch delegate, after extracting touchPoint
SKNode *node = [self nodeAtPoint: touchPoint];
if ([node isKindOfClass: [SKLabelNode class]]) {
if ([node.parent isKindOfClass: [Object class]])
[self performOperationOnCInstance: (Object*)node.parent];
} else if ([node isKindOfClass: [Object class]])
[self performOperationOnCInstance: (Object*)node];
使用 nodesAtPoint 而不是 nodeAtPoint
此方法returns 是在某个点检测到的所有节点的数组。因此,如果点击 SKLabelNode,nodesAtPoint
方法也会 return 对象节点。
//Inside the touch delegate, after extracting touchPoint
NSArray *nodes = [self nodesAtPoint: touchPoint];
for (SKNode *node in nodes) {
if ( [node isKindOfClass: [Object class]]) {
[self performOperationOnObjectInstance: (Object*)node];
break;
}
}
编辑:
zPosition
和节点树是有区别的。
zPosition
只告诉场景以特定顺序渲染节点,无论它们的子顺序是什么。 属性 的引入是为了更容易指定绘制节点子节点的顺序。如果通过以特定顺序将节点添加到父节点来实现相同的目的,则管理节点变得困难。
调用nodesAtPoint
方法时,场景通过节点树进行深度优先搜索,查看哪些节点与给定的触摸点相交。这解释了填充数组的顺序。
nodeAtPoint
方法return场景根据zPosition找到最深的节点
Apple 文档中的以下部分对此进行了广泛的解释:Creating The Node Tree
我有一个自定义 class Object
派生自 SKSpriteNode
和一个 SKLabelNode
成员变量。
#import <SpriteKit/SpriteKit.h>
@interface Object : SKSpriteNode {
SKLabelNode* n;
}
@end
在实现中,我设置了SKLabelNode
.
#import "Object.h"
@implementation Object
- (instancetype) initWithImageNamed:(NSString *)name {
self = [super initWithImageNamed:name];
n = [[SKLabelNode alloc] initWithFontNamed:@"Courier"];
n.text = @"Hello";
n.zPosition = -1;
//[self addChild:n];
return self;
}
注意: 我还没有将 SKLabelNode
作为 child 添加到 Object
。我把这行注释掉了。
我有一个单独的 class 来自 SKScene
。在此 class 中,我将 Object
的实例添加为 child。这是我在 class:
touchesBegan:withEvent:
方法
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray* nodes = [self nodesAtPoint:loc]; //to find all nodes at the touch location
SKNode* n = [self nodeAtPoint:loc]; //to find the top most node at the touch location
NSLog(@"TOP: %@",[n class]);
for(SKNode* node in nodes) {
NSLog(@"%@",[node class]);
}
}
当我在我的 SKScene
class 中点击 Object
的实例时,它按我预期的那样工作。它检测到节点是 Object
的实例。它记录:
TOP: Object
Object
现在,如果我回到 Object
class 并取消注释 [self addChild:n]
,这样 SKLabelNode
就会作为 child 添加到 Object
,这是记录:
TOP: SKLabelNode
Object
SKLabelNode
SKSpriteNode
为什么将 SKLabelNode
作为 child 添加到派生自 SKSpriteNode
的 class 会导致触摸检测到 object 本身,以及一个 SKLabelNode
和一个 SKSpriteNode
?
另外,为什么SKLabelNode
在上面?我将它的 zPosition 设置为 -1,所以我假设在另一个 class 的 touchesBegan:withEvent:
中,Object
会被检测为在顶部?
当然,我没有理解一个重要的概念。
如果你用nodeAtPoint
检查-touchesEnded:
中的节点,它肯定会return触摸点的最上面的节点,在这种情况下,是SKLabelNode
。有几种方法可以解决这个问题:
让子class检测它自己的触摸
最干净的方法是让 Object subclass 检测它自己的触摸。这可以通过将 userInteractionEnabled
属性 设置为 YES
来完成。这可以在 init 方法本身中完成。
-(instanceType) init {
if (self = [super init]) {
self.userInteractionEnabled = YES;
}
return self;
}
现在,在 class 本身中实现触摸委托方法。由于 LabelNode 的 userInteractionEnabled
属性 默认设置为 NO
,触摸委托仍会在点击时触发。
您可以在触摸委托中使用委托、NSNotificationCenter 或直接向父级发送消息。
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
//Send a message to the delegate, which is the SKScene, should be included in protocol
[self.delegate handleTouchOnObjectInstance:self];
//or, send a message directly to the SKScene, the method should be included in the interface.
MyScene *myScene = (MyScene*)self.scene;
[myScene handleTouchOnObjectInstance:self];
}
正在检查节点的父节点
在SKScene的touch delegate中,可以查看nodeAtPoint
return的SKLabelNode的父节点是否是Cclass.[=28的实例=]
//Inside the touch delegate, after extracting touchPoint
SKNode *node = [self nodeAtPoint: touchPoint];
if ([node isKindOfClass: [SKLabelNode class]]) {
if ([node.parent isKindOfClass: [Object class]])
[self performOperationOnCInstance: (Object*)node.parent];
} else if ([node isKindOfClass: [Object class]])
[self performOperationOnCInstance: (Object*)node];
使用 nodesAtPoint 而不是 nodeAtPoint
此方法returns 是在某个点检测到的所有节点的数组。因此,如果点击 SKLabelNode,nodesAtPoint
方法也会 return 对象节点。
//Inside the touch delegate, after extracting touchPoint
NSArray *nodes = [self nodesAtPoint: touchPoint];
for (SKNode *node in nodes) {
if ( [node isKindOfClass: [Object class]]) {
[self performOperationOnObjectInstance: (Object*)node];
break;
}
}
编辑:
zPosition
和节点树是有区别的。
zPosition
只告诉场景以特定顺序渲染节点,无论它们的子顺序是什么。 属性 的引入是为了更容易指定绘制节点子节点的顺序。如果通过以特定顺序将节点添加到父节点来实现相同的目的,则管理节点变得困难。
调用nodesAtPoint
方法时,场景通过节点树进行深度优先搜索,查看哪些节点与给定的触摸点相交。这解释了填充数组的顺序。
nodeAtPoint
方法return场景根据zPosition找到最深的节点
Apple 文档中的以下部分对此进行了广泛的解释:Creating The Node Tree