【问题标题】:SpriteKit Controls / GestureSpriteKit 控件/手势
【发布时间】:2020-05-25 16:37:06
【问题描述】:

基本上我正在尝试合并执行以下功能的 X2 游戏场景按钮:

1) 点击飞行(这个我有工作) 2) 点击以从玩家位置发射弹丸(我没有工作)。

我的问题是我目前在屏幕上的任何位置触摸时都设置了飞行功能。我尝试了以下方法:

这是对我的 GameScene 的引用:我认为为了将其拆分出来,我需要屏幕上的一个节点来引用此函数。这在控制台中不会出错,但不会出现在 GameScene 中。

//触发射击的按钮:

    let btnTest = SKSpriteNode(imageNamed: "Crater")
    btnTest.setScale(0.2)
    btnTest.name = "Button"
    btnTest.zPosition = 10
    btnTest.position = CGPoint(x: 100, y: 200)
    self.addChild(btnTest)

在 Player 类中,我分解了以下内容:

var shooting = false

var shootAnimation = SKAction()
var noshootAnimation = SKAction()


   Init func: 

    self.run(noshootAnimation, withKey: "noshootAnimation")

    let projectile = SKSpriteNode(imageNamed: "Crater")
    projectile.position = CGPoint (x: 100, y: 50)
    projectile.zPosition = 20
    projectile.name = "projectile"


    // Assigning categories to Game objects:
    self.physicsBody?.categoryBitMask =
        PhysicsCategory.plane.rawValue
    self.physicsBody?.contactTestBitMask =
        PhysicsCategory.ground.rawValue 
    self.physicsBody?.collisionBitMask =
        PhysicsCategory.ground.rawValue

    self.physicsBody?.applyImpulse(CGVector(dx: 300, dy: 0))
    self.addChild(projectile)



      // Start the shoot animation, set shooting to true:
       func startShooting() {


       self.removeAction(forKey: "noshootAnimation")
       self.shooting = true

   }

   // Stop the shoot animation, set shooting to false:
   func stopShooting() {


       self.removeAction(forKey: "shootAnimation")
       self.shooting = false
   }

节点出现在看起来很有希望的 GameScene 中,最后我移动到 GameScene 中的最后一段代码如下:

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

                    let location = touch.location(in: self)

                    let nodeTouched = atPoint(location)

                    if let gameSprite = nodeTouched as? GameSprite {

                    gameSprite.onTap()
                    }

                    // Check the HUD buttons which I have appearing when game is over…
                    if nodeTouched.name == "restartGame" {
                    // Transition to a new version of the GameScene
                    // To restart the Game
                        self.view?.presentScene(GameScene(size: self.size), transition: .crossFade(withDuration: 0.6))
                    }
                    else if nodeTouched.name == "returnToMenu"{
                    // Transition to the main menu scene
                        self.view?.presentScene(MenuScene(size: self.size), transition: . crossFade(withDuration: 0.6))

                    }
            }

                Player.startFly()
                player.startShooting()

                    }

            override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
                Player.stopFly()
                player.stopShooting()

            }

            override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
                Player.stopFly()
                player.stopShooting()

            }

            override func update(_ currentTime: TimeInterval) {

                player.update()         
}
}

不幸的是,GameScene 中没有发生任何事情,并且在按下屏幕时节点不会触发,无论如何我可以修改上面的代码以允许“点击飞行”+“点击射击”功能。我仍然无法弄清楚如何让我早先在 GameScene 中声明的按钮出现在其中,我的手势/触摸位置可以定位到屏幕上的这个节点,而不是我目前拥有的整个屏幕。 .

我可以说这听起来比实际一起编码更简单。

【问题讨论】:

    标签: xcode sprite-kit controls gesture hud


    【解决方案1】:

    首先,上面的代码没有调用键"shootAnimation"运行动画,缺少stopShooting()时重新运行非拍摄动画的调用和重新运行拍摄的调用startShooting() 中的动画。方法应该包括这些,如下所示

    func startShooting() {
        self.removeAction(forKey: "noshootAnimation")
    
        // Add this call
        self.run(shootAnimation)
        self.shooting = true
    }
    
    func stopShooting() {
        self.removeAction(forKey: "shootAnimation")
    
        // Add this call
        self.run(noshootAnimation)
        self.shooting = true
    }
    

    其次,noshootAnimationshootAnimation 动画是空动作:如果初始化为 SKAction(),它们不会做任何事情。根据您要执行的操作,SKAction 的许多类方法将为您创建操作 here

    第三,如果节点的isUserInteractionEnabled 属性设置为false(默认为false),任何SKNode(场景是SKNode 子类)都不会接收触摸调用;相反,它将被视为其父级收到呼叫。然而,如果一个节点的isUserInteractionEnabledtrue,它将是接收UIResponder 调用的那个,不是它的父节点。确保为场景和需要接收触摸的任何节点将此属性设置为 true(您可以在场景中或其他地方的 didMove(to:) 中执行此操作)。

    我现在将提出改进建议。我经常在 Spritekit 中使用按钮,但它们都是自定义按钮类(本身是 SKSpriteNode 的子类)的子类,它使用协议委托来提醒成员触摸事件。该方案如下所示:

    class Button: SKSpriteNode {
        weak var responder: ButtonResponder?
        // Custom code
    
        // MARK: UIResponder
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesBegan(touches, with: event)
    
            // Respond here
            responder?.respond(to: self)
        }
    }
    
    protocol ButtonResponder: AnyObject {
        func respond(to button: Button)
    }
    
    class MyScene: SKScene, ButtonResponder {
        func respond(to button: Button) {
            // Do something on touch, but typically check the name
    
            switch button.name {
            case "myButton":
                // Do something specific
            default:
                break
            }
        }
    
        override func didMove(to view: SKView) {
            // Set the scene as a responder
            let buttonInScene = childNode(withName: "myButton") as! Button
            buttonInScene.responder = self
        }
    }
    

    有关委托的更多信息,请查看here。希望这会有所帮助。

    编辑:转换触摸位置

    您可以通过传递对UITouchlocation(in:) 方法的引用而不是像您在代码中所做的那样将场景转换为屏幕上的节点

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
    
        let yourNode = childNode(withName: "yourNode")!
        for touch in touches {
            let touchLocationToYourNode = touch.location(in: yourNode)
            // Do something
        }
    }
    

    或者,使用SKNodeconvert(_:from:)convert(_:to:) 方法

    let nodeA = SKNode()
    let nodeB = SKNode()
    
    nodeA.xScale = 1.5
    nodeA.yScale = 1.2
    nodeA.position = .init(x: 100, y: 100)
    nodeA.zRotation = .pi
    
    let pointInNodeACoordinateSystem = CGPoint(x: 100, y: 100)
    let thatSamePointInNodeBCoordinateSystem = nodeB.convert(pointInNodeACoordinateSystem, from: nodeA)
    

    【讨论】:

    • 您好!感谢您抽出宝贵时间审阅这篇文章!我已经按照您上面的建议实施了,这一切都很好!几个小时的挫败感解决了!我终于可以在 GameScene 期间使用按钮功能了,感谢您的逻辑演练,这肯定有助于我的理解!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 2020-08-17
    • 1970-01-01
    • 2011-06-18
    相关资源
    最近更新 更多