【问题标题】:Attack button in SpriteKitSpriteKit 中的攻击按钮
【发布时间】:2017-09-21 19:54:54
【问题描述】:

我对 Xcode 有点陌生,并且一直在为我的班级制作 2d 游戏。一段时间以来,我一直遇到按钮问题。我刚刚找到了为什么我的跳跃按钮不起作用的解决方案,但我也有一个攻击按钮。

我为按钮设置了代码,使其显示在屏幕上并在按下时更改其图像。但是,我不知道要输入什么代码来将播放器图像更改为跳跃动画。我也不知道如何为它和敌人设置碰撞,以便攻击动画会命中。

所以我的问题是,如何让按钮开始跳跃动画,以及如何设置碰撞以在被击中时摧毁敌人?

这是我目前的代码:

override func sceneDidLoad() {
    //Setup start button
    jumpButton = SKSpriteNode(texture: jumpButtonTexture)
    jumpButton.position = CGPoint(x: 1850, y: 130)
    jumpButton.size = CGSize(width: 200, height: 200)
    jumpButton.zPosition = 20

    addChild(jumpButton)

    //Setup start button
    attackButton = SKSpriteNode(texture: attackButtonTexture)
    attackButton.position = CGPoint(x: 1550, y: 130)
    attackButton.size = CGSize(width: 200, height: 200)
    attackButton.zPosition = 20

    addChild(attackButton)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        if selectedButton != nil {
            handleJumpButtonHover(isHovering: false)
            handleAttackButtonHover(isHovering: false)
        }

        if jumpButton.contains(touch.location(in: self)) {
            selectedButton = jumpButton
            handleJumpButtonHover(isHovering: true)
        } else if attackButton.contains(touch.location(in: self)) {
            selectedButton = attackButton
            handleAttackButtonHover(isHovering: true)
        }
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        if selectedButton == jumpButton {
            handleJumpButtonHover(isHovering: false)

            if (jumpButton.contains(touch.location(in: self))) {
                handleJumpButtonClick()
            }
        } else if selectedButton == attackButton {
            handleAttackButtonHover(isHovering: false)

            if (attackButton.contains(touch.location(in: self))) {
                handleAttackButtonClick()
            }
        }
    }
    selectedButton = nil
    }

    func handleJumpButtonHover(isHovering : Bool) {
    if isHovering {
        jumpButton.texture = jumpButtonPressedTexture
    } else {
        jumpButton.texture = jumpButtonTexture
    }
}

func handleAttackButtonHover(isHovering : Bool) {
    if isHovering {
        attackButton.texture = attackButtonPressedTexture
    } else {
        attackButton.texture = attackButtonTexture
    }
}

func handleJumpButtonClick() {
    self.playerNode.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
    self.playerNode.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 20))
}

func handleAttackButtonClick() {
}

【问题讨论】:

    标签: ios swift button sprite-kit


    【解决方案1】:

    我编写了一个小型“入门”项目,以简单的方式(我希望)演示了很多概念 - 只需将代码放入新项目并运行:


    这是一个简单的 Sprite-Kit GameScene.swift。创建一个新的空 SpriteKit 项目并用它替换 GameScene.swift。然后构建并运行。

    单击屏幕上的任何对象以使它们移动。检查日志和 cmets,看看哪些碰撞,哪些接触。

    //
    //  GameScene.swift
    //  bounceTest
    //
    //  Created by Stephen Ives on 05/04/2016.
    //  Copyright (c) 2016 Stephen Ives. All rights reserved.
    //
    
    import SpriteKit
    
    
    
    class GameScene: SKScene, SKPhysicsContactDelegate {
    
        let objectSize = 150
        let initialImpulse: UInt32 = 300  // Needs to be proportional to objectSize
    
        //Physics categories
        let purpleSquareCategory:   UInt32 = 1 << 0
        let redCircleCategory:      UInt32 = 1 << 1
        let blueSquareCategory:     UInt32 = 1 << 2
        let edgeCategory:           UInt32 = 1 << 31
    
        let purpleSquare = SKSpriteNode()
        let blueSquare = SKSpriteNode()
        let redCircle = SKSpriteNode()
    
        override func didMove(to view: SKView) {
    
            physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    
            //Create an boundary else everything will fly off-screen
            let edge = frame.insetBy(dx: 0, dy: 0)
            physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
            physicsBody?.isDynamic = false  //This won't move
            name = "Screen_edge"
    
            scene?.backgroundColor = SKColor.black
    
            //        Give our 3 objects their attributes
    
            blueSquare.color = SKColor.blue
            blueSquare.size = CGSize(width: objectSize, height: objectSize)
            blueSquare.name = "shape_blueSquare"
            blueSquare.position = CGPoint(x: size.width * -0.25, y: size.height * 0.2)
    
            let circleShape = SKShapeNode(circleOfRadius: CGFloat(objectSize))
            circleShape.fillColor = SKColor.red
            redCircle.texture = view.texture(from: circleShape)
            redCircle.size = CGSize(width: objectSize, height: objectSize)
            redCircle.name = "shape_redCircle"
            redCircle.position = CGPoint(x: size.width * 0.4, y: size.height * -0.4)
    
            purpleSquare.color = SKColor.purple
            purpleSquare.size = CGSize(width: objectSize, height: objectSize)
            purpleSquare.name = "shape_purpleSquare"
            purpleSquare.position = CGPoint(x: size.width * -0.35, y: size.height * 0.4)
    
            addChild(blueSquare)
            addChild(redCircle)
            addChild(purpleSquare)
    
            redCircle.physicsBody = SKPhysicsBody(circleOfRadius: redCircle.size.width/2)
            blueSquare.physicsBody = SKPhysicsBody(rectangleOf: blueSquare.frame.size)
            purpleSquare.physicsBody = SKPhysicsBody(rectangleOf: purpleSquare.frame.size)
    
            setUpCollisions()
    
            checkPhysics()
    
        }
    
    
        func setUpCollisions() {
    
            //Assign our category bit masks to our physics bodies
            purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
            redCircle.physicsBody?.categoryBitMask = redCircleCategory
            blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
            physicsBody?.categoryBitMask = edgeCategory  // This is the edge for the scene itself
    
            // Set up the collisions. By default, everything collides with everything.
    
            redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory  // Circle doesn't collide with purple square
            purpleSquare.physicsBody?.collisionBitMask = 0   // purpleSquare collides with nothing
            //        purpleSquare.physicsBody?.collisionBitMask |= (redCircleCategory | blueSquareCategory)  // Add collisions with red circle and blue square
            purpleSquare.physicsBody?.collisionBitMask = (redCircleCategory)  // Add collisions with red circle
            blueSquare.physicsBody?.collisionBitMask = (redCircleCategory)  // Add collisions with red circle
    
    
            // Set up the contact notifications. By default, nothing contacts anything.
            redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory   // Notify when red circle and purple square contact
            blueSquare.physicsBody?.contactTestBitMask |= redCircleCategory     // Notify when blue square and red circle contact
    
            // Make sure everything collides with the screen edge and make everything really 'bouncy'
            enumerateChildNodes(withName: "//shape*") { node, _ in
                node.physicsBody?.collisionBitMask |= self.edgeCategory  //Add edgeCategory to the collision bit mask
                node.physicsBody?.restitution = 0.9 // Nice and bouncy...
                node.physicsBody?.linearDamping = 0.1 // Nice and bouncy...
            }
    
            //Lastly, set ourselves as the contact delegate
            physicsWorld.contactDelegate = self
        }
    
        func didBegin(_ contact: SKPhysicsContact) {
            let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
            switch contactMask {
            case purpleSquareCategory | blueSquareCategory:
                print("Purple square and Blue square have touched")
            case redCircleCategory | blueSquareCategory:
                print("Red circle and Blue square have touched")
            case redCircleCategory | purpleSquareCategory:
                print("Red circle and purple Square have touched")
            default: print("Unknown contact detected")
            }
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
            for touch in touches {
                let touchedNode = selectNodeForTouch(touch.location(in: self))
    
                if let node = touchedNode {
                    node.physicsBody?.applyImpulse(CGVector(dx: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2), dy: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2)))
                    node.physicsBody?.applyTorque(CGFloat(arc4random_uniform(20)) - CGFloat(10))
                }
    
            }
        }
    
        // Return the sprite where the user touched the screen
        func selectNodeForTouch(_ touchLocation: CGPoint) -> SKSpriteNode? {
    
            let touchedNode = self.atPoint(touchLocation)
            print("Touched node is \(touchedNode.name)")
            //        let touchedColor = getPixelColorAtPoint(touchLocation)
            //        print("Touched colour is \(touchedColor)")
    
            if touchedNode is SKSpriteNode {
                return (touchedNode as! SKSpriteNode)
            } else {
                return nil
            }
        }
    
        //MARK: - Analyse the collision/contact set up.
        func checkPhysics() {
    
            // Create an array of all the nodes with physicsBodies
            var physicsNodes = [SKNode]()
    
            //Get all physics bodies
            enumerateChildNodes(withName: "//.") { node, _ in
                if let _ = node.physicsBody {
                    physicsNodes.append(node)
                } else {
                    print("\(node.name) does not have a physics body so cannot collide or be involved in contacts.")
                }
            }
    
            //For each node, check it's category against every other node's collion and contctTest bit mask
            for node in physicsNodes {
                let category = node.physicsBody!.categoryBitMask
                // Identify the node by its category if the name is blank
                let name = node.name != nil ? node.name! : "Category \(category)"
    
                let collisionMask = node.physicsBody!.collisionBitMask
                let contactMask = node.physicsBody!.contactTestBitMask
    
                // If all bits of the collisonmask set, just say it collides with everything.
                if collisionMask == UInt32.max {
                    print("\(name) collides with everything")
                }
    
                for otherNode in physicsNodes {
                if (node.physicsBody?.dynamic == false) {
                    print("This node \(name) is not dynamic")
                }
                    if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
                        let otherCategory = otherNode.physicsBody!.categoryBitMask
                        // Identify the node by its category if the name is blank
                        let otherName = otherNode.name != nil ? otherNode.name! : "Category \(otherCategory)"
    
                        // If the collisonmask and category match, they will collide
                        if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
                            print("\(name) collides with \(otherName)")
                        }
                        // If the contactMAsk and category match, they will contact
                        if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(otherName)")}
                    }
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      你问题的前半部分很简单。您只需要在该函数中设置播放器的新纹理,然后在该动画完成后,恢复为原始播放器动画。所以我建议在类级别创建这两个动画,并为默认动画分配一个键,并使用攻击动作的完成处理程序将其设置回来。

      class GameScene: SKScene {
          let frame2 = SKTexture(imageNamed: "Ttam2")
          let frame3 = SKTexture(imageNamed: "Ttam3")
          let frame4 = SKTexture(imageNamed: "Ttam4")
      
          let attackFrame1 = SKTexture(imageNamed: "Ttam1_ATTACK")
          let attackFrame2 = SKTexture(imageNamed: "Ttam2_ATTACK")
      
          var animation: SKAction!
          var attackAnination: SKAction!
      
          override func sceneDidLoad(){
             animation = SKAction.repeatForever(SKAction.animate(with: [playerTexture, frame2, frame3, frame4], timePerFrame: 0.2))
      
             attackAnimation = SKAction.animate(with: [attackFrame1,attackFrame2],timePerFrame: 0.2)
      
             playerNode.run(animation,withKey:"animate")
          }
      
          func handleAttackButtonClick(){
              playerNode.removeAction(forKey:"animate")
              playerNode.run(attackAnimation,completion:{
                   self.playerNode.run(animation,withKey: "animate")
              })
          }
      }
      

      关于碰撞的后半部分,您要使用的是contactTestBitmask。您将 SKPhysicsContactDelegate 添加到您的场景类并设置您要在哪些节点上注册联系人的位掩码。 SO上有很多关于这方面的问题和答案。没必要再回答我不认为。如果您挂断电话,请随时回来提出新问题。

      【讨论】:

      • 哦,好吧,所以我会说attackFrame1和2是创建播放器内部的图像?然后让有趣的句柄 AttackButtonClick() 中的动画代码正确吗?
      • attackFrame 1 和 2 是攻击图像的 SKTextures。就像你对普通动画所做的那样。您只是在该函数中的节点上运行动画操作。
      • 我的动画似乎仍然存在问题。我有角色在 func createPlayer 内行走的动画。我将动画设置为 let animation = SKAction.animate(with:)。我可能只是将attackFrame1 和attackFrame2 放在了错误的位置。此外,当将动画 = SKAction.repeatForever 放入 sceneDidLoad() 时,它会作为未解析的标识符出现
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-06
      • 1970-01-01
      • 2021-02-04
      相关资源
      最近更新 更多