如何使touch.locationInNode()识别节点与其子节点之间的区别?

时间:2015-07-12 22:09:58

标签: swift sprite-kit touchesbegan

我开始宣布两个SKSpriteNodes,手柄和刀片,并添加句柄作为自己的孩子,刀片作为手柄的孩子

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 is 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")
        }

    }
}

2 个答案:

答案 0 :(得分:3)

确定用户是否接触过剑柄或刀片是否相当简单,但有一些警告。以下假设1.剑zRotation = 0时,剑图像朝右,2。剑的anchorPoint为(0,0.5),3。剑(刀刃和手柄)是一个精灵节点。将精灵添加到另一个精灵时,父框架的大小会扩展为包含子节点。这就是为什么无论你在哪里点剑,你对Handle.containsPoint的测试都是正确的。

下图显示了一个带有深灰色手柄(左侧)和浅灰色刀片的剑精灵。围绕剑的黑色矩形表示精灵的框架,圆圈表示用户触摸的位置。标记为a的线的长度是从触摸点到剑底的距离。我们可以测试此距离,以查看用户是否触摸了句柄(如果a <= handleLength)或刀片(如果a > handleLength)。在zRotation = 0a = x时,测试结果为x <= handleLength,其中剑的底部为x = 0

enter image description here

在下图中,剑旋转了90度(即zRotation = M_PI_2)。同样,如果a <= handleLength,用户触摸了手柄,否则用户触摸了刀片。由于剑的旋转,唯一的区别是a现在是y值而不是x。在这两种情况下,都可以使用框架的边界框来检测用户是否触过剑。

enter image description here

当精灵旋转45度时,它的框架会自动扩展以包围精灵,如下图中的黑色矩形所示。因此,当用户触摸矩形中的任何位置时,测试if sprite.frame.contains(location)将为真。当触摸的位置相对远离剑时(即,当距离b大时),这可能导致用户拿起剑。如果我们希望所有旋转角度的最大触摸距离相同,则需要进行额外的测试。

enter image description here

好消息是Sprite Kit提供了一种从一个坐标系转换到另一个坐标系的方法。在这种情况下,我们需要从场景坐标转换为剑坐标。这极大地简化了问题,因为它还将点旋转到新的坐标系。从场景转换为剑坐标后,转换后的触摸位置的xy值与所有旋转角度的距离ab相同!现在我们知道了ab,我们可以确定触摸与剑的接近程度以及用户是否触摸了手柄或刀片。

从上面我们可以实现以下代码:

    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")
             }
        }
    }

答案 1 :(得分:1)

这应该可以在不改变现有代码的情况下工作......

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")

        }

    }
}

请注意,您先检查刀片然后检查手柄。另请注意,您必须转换接触点才能从句柄内获得一个点。

据说这可以小规模工作,但你可能想看看为SKSpriteNode创建一个名为Handle或Sword的子类(这就是为什么你不使用它们通常只使用的变量名的第一个上限类的第一个上限),将其设置为userInteractionEnabled,然后覆盖该子类中的touchesBegan并查看它是否正在触及刀片,如果不是,则表示它触及了句柄。

希望这有帮助并且有意义。

相关问题