Create Button in SpriteKit: Swift

后端 未结 3 1936
礼貌的吻别
礼貌的吻别 2021-02-05 17:42

I want to create a button in SpriteKit or in an SKScene that sends the view to another view controller.

I tried using the \"performSegue with i

相关标签:
3条回答
  • 2021-02-05 18:30

    I have translated Alessandro Ornano’s answer to Swift 3.1:

    import SpriteKit
    
    class FTButtonNode: SKSpriteNode {
    
    enum FTButtonActionType: Int {
        case TouchUpInside = 1,
        TouchDown, TouchUp
    }
    
    var isEnabled: Bool = true {
        didSet {
            if (disabledTexture != nil) {
                texture = isEnabled ? defaultTexture : disabledTexture
            }
        }
    }
    var isSelected: Bool = false {
        didSet {
            texture = isSelected ? selectedTexture : defaultTexture
        }
    }
    
    var defaultTexture: SKTexture
    var selectedTexture: SKTexture
    var label: SKLabelNode
    
    required init(coder: NSCoder) {
        fatalError("NSCoding not supported")
    }
    
    init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {
        
        self.defaultTexture = defaultTexture
        self.selectedTexture = selectedTexture
        self.disabledTexture = disabledTexture
        self.label = SKLabelNode(fontNamed: "Helvetica");
        
        super.init(texture: defaultTexture, color: UIColor.white, size: defaultTexture.size())
        isUserInteractionEnabled = true
        
        //Creating and adding a blank label, centered on the button
        self.label.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center;
        self.label.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center;
        addChild(self.label)
        
        // Adding this node as an empty layer. Without it the touch functions are not being called
        // The reason for this is unknown when this was implemented...?
        let bugFixLayerNode = SKSpriteNode(texture: nil, color: UIColor.clear, size: defaultTexture.size())
        bugFixLayerNode.position = self.position
        addChild(bugFixLayerNode)
        
    }
    
    /**
     * Taking a target object and adding an action that is triggered by a button event.
     */
    func setButtonAction(target: AnyObject, triggerEvent event:FTButtonActionType, action:Selector) {
        
        switch (event) {
        case .TouchUpInside:
            targetTouchUpInside = target
            actionTouchUpInside = action
        case .TouchDown:
            targetTouchDown = target
            actionTouchDown = action
        case .TouchUp:
            targetTouchUp = target
            actionTouchUp = action
        }
        
    }
    
    /*
     New function for setting text. Calling function multiple times does
     not create a ton of new labels, just updates existing label.
     You can set the title, font type and font size with this function
     */
    
    func setButtonLabel(title: NSString, font: String, fontSize: CGFloat) {
        self.label.text = title as String
        self.label.fontSize = fontSize
        self.label.fontName = font
    }
    
    var disabledTexture: SKTexture?
    var actionTouchUpInside: Selector?
    var actionTouchUp: Selector?
    var actionTouchDown: Selector?
    weak var targetTouchUpInside: AnyObject?
    weak var targetTouchUp: AnyObject?
    weak var targetTouchDown: AnyObject?
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if (!isEnabled) {
            return
        }
        isSelected = true
        if (targetTouchDown != nil && targetTouchDown!.responds(to: actionTouchDown)) {
            UIApplication.shared.sendAction(actionTouchDown!, to: targetTouchDown, from: self, for: nil)
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        if (!isEnabled) {
            return
        }
        
        let touch: AnyObject! = touches.first
        let touchLocation = touch.location(in: parent!)
        
        if (frame.contains(touchLocation)) {
            isSelected = true
        } else {
            isSelected = false
        }
        
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if (!isEnabled) {
            return
        }
        
        isSelected = false
        
        if (targetTouchUpInside != nil && targetTouchUpInside!.responds(to: actionTouchUpInside!)) {
            let touch: AnyObject! = touches.first
            let touchLocation = touch.location(in: parent!)
            
            if (frame.contains(touchLocation) ) {
                UIApplication.shared.sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, for: nil)
            }
            
        }
        
        if (targetTouchUp != nil && targetTouchUp!.responds(to: actionTouchUp!)) {
            UIApplication.shared.sendAction(actionTouchUp!, to: targetTouchUp, from: self, for: nil)
        }
    }
    
    }
    

    Usage:

    @objc func buttonTap() {
        print("Button pressed")
    }
    
    override func didMove(to view: SKView)
    {
        backgroundColor = SKColor.white
        let buttonTexture: SKTexture! = SKTexture(imageNamed: "button")
        let buttonTextureSelected: SKTexture! = SKTexture(imageNamed: "buttonSelected.png")
        let button = FTButtonNode(normalTexture: buttonTexture, selectedTexture: buttonTextureSelected, disabledTexture: buttonTexture)
        button.setButtonAction(target: self, triggerEvent: .TouchUpInside, action: #selector(GameScene.buttonTap))
        button.setButtonLabel(title: "Button", font: "Arial", fontSize: 12)
        button.position = CGPoint(x: self.frame.midX,y: self.frame.midY)
        button.zPosition = 1
        button.name = "Button"
        self.addChild(button)
    }
    

    I have created two .png:

    0 讨论(0)
  • 2021-02-05 18:38

    If you need to create a button in SpriteKit, I think this button must have all or some of the available actions to do whatever you want (exactly as UIButton did)

    Here you can find a simple class that build a SpriteKit button, called FTButtonNode:

    class FTButtonNode: SKSpriteNode {
    
        enum FTButtonActionType: Int {
            case TouchUpInside = 1,
            TouchDown, TouchUp
        }
    
        var isEnabled: Bool = true {
            didSet {
                if (disabledTexture != nil) {
                    texture = isEnabled ? defaultTexture : disabledTexture
                }
            }
        }
        var isSelected: Bool = false {
            didSet {
                texture = isSelected ? selectedTexture : defaultTexture
            }
        }
    
        var defaultTexture: SKTexture
        var selectedTexture: SKTexture
        var label: SKLabelNode
    
        required init(coder: NSCoder) {
            fatalError("NSCoding not supported")
        }
    
        init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {
    
            self.defaultTexture = defaultTexture
            self.selectedTexture = selectedTexture
            self.disabledTexture = disabledTexture
            self.label = SKLabelNode(fontNamed: "Helvetica");
    
            super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())
            userInteractionEnabled = true
    
            //Creating and adding a blank label, centered on the button
            self.label.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Center;
            self.label.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center;
            addChild(self.label)
    
            // Adding this node as an empty layer. Without it the touch functions are not being called
            // The reason for this is unknown when this was implemented...?
            let bugFixLayerNode = SKSpriteNode(texture: nil, color: UIColor.clearColor(), size: defaultTexture.size())
            bugFixLayerNode.position = self.position
            addChild(bugFixLayerNode)
    
        }
    
        /**
         * Taking a target object and adding an action that is triggered by a button event.
         */
        func setButtonAction(target: AnyObject, triggerEvent event:FTButtonActionType, action:Selector) {
    
            switch (event) {
            case .TouchUpInside:
                targetTouchUpInside = target
                actionTouchUpInside = action
            case .TouchDown:
                targetTouchDown = target
                actionTouchDown = action
            case .TouchUp:
                targetTouchUp = target
                actionTouchUp = action
            }
    
        }
    
        /*
        New function for setting text. Calling function multiple times does
        not create a ton of new labels, just updates existing label.
        You can set the title, font type and font size with this function
        */
    
        func setButtonLabel(title: NSString, font: String, fontSize: CGFloat) {
            self.label.text = title as String
            self.label.fontSize = fontSize
            self.label.fontName = font
        }
    
        var disabledTexture: SKTexture?
        var actionTouchUpInside: Selector?
        var actionTouchUp: Selector?
        var actionTouchDown: Selector?
        weak var targetTouchUpInside: AnyObject?
        weak var targetTouchUp: AnyObject?
        weak var targetTouchDown: AnyObject?
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            if (!isEnabled) {
                return
            }
            isSelected = true
            if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {
                UIApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self, forEvent: nil)
            }
        }
    
        override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    
            if (!isEnabled) {
                return
            }
    
            let touch: AnyObject! = touches.first
            let touchLocation = touch.locationInNode(parent!)
    
            if (CGRectContainsPoint(frame, touchLocation)) {
                isSelected = true
            } else {
                isSelected = false
            }
    
        }
    
        override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
            if (!isEnabled) {
                return
            }
    
            isSelected = false
    
            if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
                let touch: AnyObject! = touches.first
                let touchLocation = touch.locationInNode(parent!)
    
                if (CGRectContainsPoint(frame, touchLocation) ) {
                    UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)
                }
    
            }
    
            if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
                UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)
            }
        }
    
    }
    

    The source is available in this Gist

    Usage:

    let backTexture: SKTexture! = SKTexture(image:"backBtn.png")
    let backTextureSelected: SKTexture! = SKTexture(image:"backSelBtn.png")  
    let backBtn = FTButtonNode(normalTexture: backTexture, selectedTexture: backTextureSelected, disabledTexture: backTexture,size:backTexture.size())
    backBtn.setButtonAction(self, triggerEvent: .TouchUpInside, action: #selector(GameScene.backBtnTap))
    backBtn.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
    backBtn.zPosition = 1
    backBtn.name = "backBtn"
    self.addChild(backBtn)
    
    func backBtnTap() {
        print("backBtnTap tapped")
        // Here for example you can do:
        let transition = SKTransition.fadeWithDuration(0.5)
        let nextScene = MenuScene(size: self.scene!.size)
        nextScene.scaleMode = .ResizeFill
        self.scene?.view?.presentScene(nextScene, transition: transition)
    }
    
    0 讨论(0)
  • 2021-02-05 18:40

    The simplest solution, but possibly not of the greatest quality, is to use a SpriteNode containing an image and name it. Later, using that scene you can easily program it to transfer the user to the next scene when tapped:

    class GameScene: SKScene {
       let button = SKSpriteNode(imageNamed: "yourImgName")
    
       override func didMoveToView(view: SKView) {
          button.name = "btn"
          button.size.height = 100
          button.size.width = 100
          button.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 50)
          self.addChild(button)
    
          //Adjust button properties (above) as needed
          }
    
       override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
          let touch = touches.first
          let positionInScene = touch!.locationInNode(self)
          let touchedNode = self.nodeAtPoint(positionInScene)
    
        if let name = touchedNode.name {
            if name == "btn" {
    
                let yourNextScene = YourNextScene(fileNamed: "YourNextScene")
                self.view?.presentScene(yourNextScene!)
    
              }
          }
       }
    }
    

    Don't forget to replace "YourNextScene" with the actual name of your next scene.

    0 讨论(0)
提交回复
热议问题