如何让 touch.locationInNode() 识别节点与其 child 之间的区别?
How to make touch.locationInNode() recognize the difference between a node and its child?
我首先声明了两个 SKSpriteNode,handle 和 blade,并将 handle 添加为 self 的 child,将 blade 添加为 handle[的 child =14=]
var handle = SKSpriteNode(imageNamed: "Handle.png")
var blade = SKSpriteNode(imageNamed: "Blade.png")
override func didMoveToView(view: SKView) {
handle.position = CGPointMake(self.size.width / 2, self.size.height / 14)
blade.position = CGPointMake(0, 124)
self.addChild(Handle)
Handle.addChild(Blade)
}
当我点击句柄时,它会打印到控制台 "Handle was clicked",但是当我点击 Blade 时,它也会打印 "Handle was clicked"。明明是blade是handle的child,但是点击blade的时候怎么弄,打印出来的是"Blade was clicked"?
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (Handle.containsPoint(location)){
NSLog("Handle was clicked")
}
else if (Blade.containsPoint(location)){
NSLog("Blade was clicked")
}
}
}
这应该可以在不更改太多现有代码的情况下工作...
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let locationInScene = touch.locationInNode(self)
let locationInHandel = touch.locationInNode(Handle)
if (Blade.containsPoint(locationInHandel)){
NSLog("Blade was clicked")
}
else if (Handle.containsPoint(locationInScene)){
NSLog("Handle was clicked")
}
}
}
请注意,您首先要检查 blade,然后再检查句柄。另请注意,您必须转换接触点以从句柄内为您提供一个点。
话虽如此,这将在小范围内起作用,但您可能想看看为 SKSpriteNode 创建一个名为 Handle 或 Sword 的子类(这就是为什么您不对变量名使用首字母大写的原因,它们通常是仅对 类) 使用第一个大写字母,将其设置为 userInteractionEnabled,然后覆盖该子类中的 touchesBegan 并查看它是否触及 blade,如果没有触及手柄。
希望这对您有所帮助并且有意义。
确定用户是触摸了剑柄还是 blade 相当简单,但有一些注意事项。下面假设 1. zRotation = 0
时剑的图像向右, 2. 剑的 anchorPoint
为 (0, 0.5), 3. 剑 (blade和句柄)是单个精灵节点。当您将一个 sprite 添加到另一个 sprite 时,parent 的框架的大小会扩展以包含 child 节点。这就是为什么你的测试 Handle.containsPoint
无论你点击剑的哪个位置都是正确的。
下图是一把深灰色手柄(左边)和浅灰色的剑精灵blade。剑周围的黑色矩形代表精灵的框架,圆圈代表用户触摸的位置。标有a
的线的长度是触摸点到剑底的距离。我们可以测试这个距离,看看用户是否触摸了手柄(如果 a <= handleLength
)或 blade(如果 a > handleLength
)。当zRotation = 0
,a = x
所以考的是x <= handleLength
,其中剑底是x = 0
.
下图中,剑旋转了90度(即zRotation = M_PI_2
)。同样,如果 a <= handleLength
,用户触摸了句柄,否则用户触摸了 blade。唯一的区别是 a
现在是 y
值而不是 x
由于剑的旋转。在这两种情况下,都可以使用框架的边界框来检测用户是否触摸了剑。
然而,当精灵旋转45度时,它的边框会自动扩展以包围精灵,如下图黑色矩形所示。因此,当用户触摸矩形中的任何地方时,测试 if sprite.frame.contains(location)
将为真。这可能导致用户在触摸的位置离剑相对较远时(即,当距离 b
较大时)拿起剑。如果我们希望最大触摸距离在所有旋转角度上都相同,则需要进行额外的测试。
好消息是 Sprite Kit 提供了一种从一个坐标系转换到另一个坐标系的方法。在这种情况下,我们需要从场景坐标转换为剑坐标。这极大地简化了问题,因为它还将点旋转到新的坐标系。从场景坐标转换为剑坐标后,转换后的触摸位置x
和y
值与所有旋转角度的距离a
和b
相同!现在我们知道 a
和 b
,我们可以确定触摸距离剑有多近以及用户是否触摸了手柄或 blade。
根据以上,我们可以实现如下代码:
let location = touch.locationInNode(self)
// Check if the user touched inside of the sword's frame (see Figure 1-3)
if (sword.frame.contains(location)) {
// Convert the touch location from scene to sword coordinates
let point = sword.convertPoint(location, fromNode: self)
// Check if the user touched any part of the sword. Note that a = point.x and b = point.y
if (fabs(point.y) < sword.size.height/2 + touchTolerance) {
// Check if the user touched the handle
if (point.x <= handleLength) {
println("touched handle")
}
else {
println("touched blade")
}
}
}
我首先声明了两个 SKSpriteNode,handle 和 blade,并将 handle 添加为 self 的 child,将 blade 添加为 handle[的 child =14=]
var handle = SKSpriteNode(imageNamed: "Handle.png")
var blade = SKSpriteNode(imageNamed: "Blade.png")
override func didMoveToView(view: SKView) {
handle.position = CGPointMake(self.size.width / 2, self.size.height / 14)
blade.position = CGPointMake(0, 124)
self.addChild(Handle)
Handle.addChild(Blade)
}
当我点击句柄时,它会打印到控制台 "Handle was clicked",但是当我点击 Blade 时,它也会打印 "Handle was clicked"。明明是blade是handle的child,但是点击blade的时候怎么弄,打印出来的是"Blade was clicked"?
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (Handle.containsPoint(location)){
NSLog("Handle was clicked")
}
else if (Blade.containsPoint(location)){
NSLog("Blade was clicked")
}
}
}
这应该可以在不更改太多现有代码的情况下工作...
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let locationInScene = touch.locationInNode(self)
let locationInHandel = touch.locationInNode(Handle)
if (Blade.containsPoint(locationInHandel)){
NSLog("Blade was clicked")
}
else if (Handle.containsPoint(locationInScene)){
NSLog("Handle was clicked")
}
}
}
请注意,您首先要检查 blade,然后再检查句柄。另请注意,您必须转换接触点以从句柄内为您提供一个点。
话虽如此,这将在小范围内起作用,但您可能想看看为 SKSpriteNode 创建一个名为 Handle 或 Sword 的子类(这就是为什么您不对变量名使用首字母大写的原因,它们通常是仅对 类) 使用第一个大写字母,将其设置为 userInteractionEnabled,然后覆盖该子类中的 touchesBegan 并查看它是否触及 blade,如果没有触及手柄。
希望这对您有所帮助并且有意义。
确定用户是触摸了剑柄还是 blade 相当简单,但有一些注意事项。下面假设 1. zRotation = 0
时剑的图像向右, 2. 剑的 anchorPoint
为 (0, 0.5), 3. 剑 (blade和句柄)是单个精灵节点。当您将一个 sprite 添加到另一个 sprite 时,parent 的框架的大小会扩展以包含 child 节点。这就是为什么你的测试 Handle.containsPoint
无论你点击剑的哪个位置都是正确的。
下图是一把深灰色手柄(左边)和浅灰色的剑精灵blade。剑周围的黑色矩形代表精灵的框架,圆圈代表用户触摸的位置。标有a
的线的长度是触摸点到剑底的距离。我们可以测试这个距离,看看用户是否触摸了手柄(如果 a <= handleLength
)或 blade(如果 a > handleLength
)。当zRotation = 0
,a = x
所以考的是x <= handleLength
,其中剑底是x = 0
.
下图中,剑旋转了90度(即zRotation = M_PI_2
)。同样,如果 a <= handleLength
,用户触摸了句柄,否则用户触摸了 blade。唯一的区别是 a
现在是 y
值而不是 x
由于剑的旋转。在这两种情况下,都可以使用框架的边界框来检测用户是否触摸了剑。
然而,当精灵旋转45度时,它的边框会自动扩展以包围精灵,如下图黑色矩形所示。因此,当用户触摸矩形中的任何地方时,测试 if sprite.frame.contains(location)
将为真。这可能导致用户在触摸的位置离剑相对较远时(即,当距离 b
较大时)拿起剑。如果我们希望最大触摸距离在所有旋转角度上都相同,则需要进行额外的测试。
好消息是 Sprite Kit 提供了一种从一个坐标系转换到另一个坐标系的方法。在这种情况下,我们需要从场景坐标转换为剑坐标。这极大地简化了问题,因为它还将点旋转到新的坐标系。从场景坐标转换为剑坐标后,转换后的触摸位置x
和y
值与所有旋转角度的距离a
和b
相同!现在我们知道 a
和 b
,我们可以确定触摸距离剑有多近以及用户是否触摸了手柄或 blade。
根据以上,我们可以实现如下代码:
let location = touch.locationInNode(self)
// Check if the user touched inside of the sword's frame (see Figure 1-3)
if (sword.frame.contains(location)) {
// Convert the touch location from scene to sword coordinates
let point = sword.convertPoint(location, fromNode: self)
// Check if the user touched any part of the sword. Note that a = point.x and b = point.y
if (fabs(point.y) < sword.size.height/2 + touchTolerance) {
// Check if the user touched the handle
if (point.x <= handleLength) {
println("touched handle")
}
else {
println("touched blade")
}
}
}