Swift 3.0:当给定非 SKView 父级时,SKSpriteNode 作为 Button 不起作用?

Posted

技术标签:

【中文标题】Swift 3.0:当给定非 SKView 父级时,SKSpriteNode 作为 Button 不起作用?【英文标题】:Swift 3.0: SKSpriteNode as Button does not work when given a non-SKView parent? 【发布时间】:2017-01-15 16:36:40 【问题描述】:

已经有一些关于如何在 SpriteKit 中制作按钮的问题,例如 Swift Spritekit Adding Button Programaticly 和 Setting up buttons in SKScene。

无论哪种方式,解决方案是为按钮制作一个带有纹理的SKSpriteNode,然后在函数touchesEnded 中测试触摸是否落在按钮SKSpriteNode 内。

然而,有趣的是,如果要将按钮设为子按钮,例如 SKCameraNode 或另一个 SKSpriteNode,则此方法不再有效。

我的问题是为什么?以及如何克服这个困境。

更新:

关于下面的帖子,这里有两个简单的 GameScene.swift 文件的替代版本。主要区别在于,在第一种情况下,sprite2 不是相机的孩子,而在版本 2 中它是。

注意:在 gifs 中,请注意在版本 1 中,单击紫色精灵 (sprite2) 会导致它打印“你点击了紫色精灵。”,而在版本 2 中它显示的是“蓝色精灵”。因此问题明确:

点击另一个 SKNode 的子节点注册为点击最上面的父节点,而不是实际点击的节点!

新问题:如何纠正。

附录:在第 2 版中,sprite2 由于成为相机的子项而稍微改变了位置 - 因为它是 M.W.E.而且它不会影响这个演示,我选择不更正它。

版本 1

class GameScene: SKScene 
    var cam: SKCameraNode!

    var sprite = SKSpriteNode(imageNamed: "sprite")
    var sprite2 = SKSpriteNode(imageNamed: "sprite2")


    override func didMove(to view: SKView) 
        backgroundColor = SKColor.white

        sprite.position = CGPoint(x: size.width/4,y: size.height/2)
        sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)

        sprite.anchorPoint = CGPoint(x:0.5, y:0.5)

        // Set up the camera
        cam = SKCameraNode()
        self.camera = cam
        cam.setScale(3.0)

        cam.position = CGPoint(x: size.width/4, y: 0)
        sprite.addChild(cam)



        addChild(sprite); addChild(sprite2)


    


    func touchDown(atPoint pos : CGPoint) 

    

    func touchMoved(toPoint pos : CGPoint) 

    

    func touchUp(atPoint pos : CGPoint) 

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 

        for t in touches  self.touchDown(atPoint: t.location(in: self)) 
    

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) 
        for t in touches  self.touchMoved(toPoint: t.location(in: self)) 
    

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
        let touch = touches.first
        let touchLocation = touch!.location(in: self)

        if sprite.contains(touchLocation) 
            print("You tapped the blue sprite")
        

        if sprite2.contains(touchLocation) 
            print("You tapped the purple sprite")
        

    

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) 
        for t in touches  self.touchUp(atPoint: t.location(in: self)) 
    


    override func update(_ currentTime: TimeInterval) 
        // Called before each frame is rendered
    

版本 2

class GameScene: SKScene 
    var cam: SKCameraNode!

    var sprite = SKSpriteNode(imageNamed: "sprite")
    var sprite2 = SKSpriteNode(imageNamed: "sprite2")


    override func didMove(to view: SKView) 
        backgroundColor = SKColor.white

        // Scale Sprites
//        sprite.setScale(0.3)
        sprite2.setScale(0.3)

        sprite.position = CGPoint(x: size.width/4,y: size.height/2)
//        sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)

        sprite.anchorPoint = CGPoint(x:0.5, y:0.5)

        // Set up the camera
        cam = SKCameraNode()
        self.camera = cam
        cam.setScale(3.0)

        cam.position = CGPoint(x: size.width/4, y: 0)
        sprite.addChild(cam)



        addChild(sprite); //addChild(sprite2)
        cam.addChild(sprite2)


    


    func touchDown(atPoint pos : CGPoint) 

    

    func touchMoved(toPoint pos : CGPoint) 

    

    func touchUp(atPoint pos : CGPoint) 

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 

        for t in touches  self.touchDown(atPoint: t.location(in: self)) 
    

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) 
        for t in touches  self.touchMoved(toPoint: t.location(in: self)) 
    

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
        let touch = touches.first
        let touchLocation = touch!.location(in: self)

        if sprite.contains(touchLocation) 
            print("You tapped the blue sprite")
        

        if sprite2.contains(touchLocation) 
            print("You tapped the purple sprite")
        

    

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) 
        for t in touches  self.touchUp(atPoint: t.location(in: self)) 
    


    override func update(_ currentTime: TimeInterval) 
        // Called before each frame is rendered
    

【问题讨论】:

您是在SKScene 中使用touchesEnded 拿着您的按钮和相机,还是在SKSpriteNode 的子类上,即您的按钮实例? touchesEndedSKScene 中按住按钮和相机。 【参考方案1】:

我不认为你说的是​​真的。您可以检测来自子节点的触摸。为了演示,我刚刚在我的一个 SpriteKit 项目中运行了一些测试代码,在其中我检测到相机节点上的触摸

var cameraNode = SKCameraNode()

然后在didMove(to:):

addChild(cameraNode)
camera = cameraNode
camera?.position = CGPoint(x: size.width/2, y: size.height/2)

使用touchesEnded检测cameraNode上的触摸:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    guard let touch = touches.first else  return 

    let location = touch.location(in: cameraNode)

    print("LocationX: \(location.x), LocationY: \(location.y)")

这是我的打印输出:

LocationX: -13.9129028320312, LocationY: 134.493041992188

为了进一步解释,如果您将按钮添加为 cameraNode 的子项,您将执行以下操作:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    guard let touch = touches.first else  return 

    let location = touch.location(in: cameraNode)

    // Check if tap was within button frame (assuming your buttonNode is named button and a child of cameraNode)
    if button.frame.contains(location) 
        // Tap was within the frame of the button
        // Do whatever is necessary
    

进一步编辑 -

您的问题源于您请求触摸位置的节点。正如我在回答中提到的,您需要根据您检查精灵帧的节点来推断触摸位置。在您的编辑中,您仅检测到self 上的触摸,这将为您提供相对于您的scene 的坐标。如果要检测子视图(如摄像头节点)上的触摸,则需要请求摄像头节点内的触摸位置。根据您添加的代码,这是我正在谈论的内容:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    guard let touch = touches.first else  return 

    // This will give you touch location from the camera node 'cam' NOT 'self'
    let cameraLocation = touch.location(in: cam)

    // This will give you touch location from the scene itself
    let sceneLocation = touch.location(in: self)

    if sprite.contains(sceneLocation) 
        print("You tapped the blue sprite")
    

    if sprite2.contains(cameraLocation) 
        print("You tapped the purple sprite")
    


我刚试过这个,对我来说效果很好。

【讨论】:

是的,这行得通,我是说如果你有一个精灵,那么它就是你相机的孩子。 @SamNeuron - 这正是我要说的。当您将精灵添加为cameraNode 的子对象时,您会检测到按钮的框架与cameraNode 的框架相对应。它应该仍然可以正常工作。 即使相机也是另一个 Sprite 的子对象? @SumNeuron - 是的,这不是问题。 touchesEnded 检测到屏幕上的触摸,然后将这些坐标转换为请求节点的坐标 请查看更新。不幸的是,由于某种原因看不到鼠标光标,但这一点是成立的。

以上是关于Swift 3.0:当给定非 SKView 父级时,SKSpriteNode 作为 Button 不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥当我在 grails 上删除一对多关系上的父级时,会在子级上调用 beforeInsert 事件?

Swift 3.0 如何在 Firebase 中的父级上添加子级

仅在父级时删除之前发布

phpunit,laravel:当当前类范围没有父级时不能使用“父级”

单击父级时仅打开一个子菜单

Hibernate @OneToMany 在更新父级时从列表中删除子级