【问题标题】:How to make touch.locationInNode() recognize the difference between a node and its child?如何让 touch.locationInNode() 识别节点与其子节点之间的区别?
【发布时间】:2015-07-12 22:09:57
【问题描述】:

我首先声明了两个 SKSpriteNode,handle 和 Blade,并将 handle 添加为 self 的子节点,并将 Blade 添加为 handle 的子节点

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

当我点击手柄时,它会打印到控制台“手柄被点击”,但是当我点击刀片时,它也会打印“手柄被点击”。它清楚地认识到刀片是手柄的孩子,但是我怎样才能让它在我点击刀片时打印“刀片被点击”?

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

    }
}

【问题讨论】:

  • 您可以通过为每个节点分配唯一的名称属性并在 touchesBegan 方法中检查节点的名称来区分。
  • 但这正是我所做的,但没有奏效。我将其中一个命名为手柄,另一个命名为刀片,当我点击刀片时,它以为我在点击手柄
  • 你好@Lahav。你是对的,因为它是一个孩子,你不能使用containsPoint,因为它基本上是使用精灵的框架,并查看场景坐标系中的点是否与它的一个孩子中的点匹配,但是你想要孩子们的孩子。永远不要害怕帮助即将到来!我本人或 Sprite Kit 联盟成员之一将很快提供帮助。请待命。

标签: swift sprite-kit touchesbegan


【解决方案1】:

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

下图显示了带有深灰色手柄(左侧)和浅灰色刀片的剑精灵。剑周围的黑色矩形代表精灵的框架,圆圈代表用户触摸的位置。标有a 的线的长度是从接触点到剑底的距离。我们可以测试这个距离,看看用户是否触摸了手柄(如果a &lt;= handleLength)或刀片(如果a &gt; handleLength)。当zRotation = 0a = x 所以测试是x &lt;= handleLength,剑底是x = 0

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

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

好消息是 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")
             }
        }
    }

【讨论】:

    【解决方案2】:

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

    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 并查看它是否正在触摸刀片,如果没有,您知道它触摸了手柄。

    希望这会有所帮助且有意义。

    【讨论】:

      猜你喜欢
      • 2018-04-04
      • 2014-08-12
      • 1970-01-01
      • 2013-07-11
      • 1970-01-01
      • 1970-01-01
      • 2018-03-18
      • 1970-01-01
      • 2023-03-04
      相关资源
      最近更新 更多