Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Button in SpriteKit: Swift

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 identifier ", however apparently an SKScene doesn't support this. How would I create a button that sends the view to another view with SpriteKit?

This is the code that I've tried using to perform this action.

The line with "HomeButton.prepareForSegueWithIdentifier()" is just an example. It won't actually let me add the "prepareForSegue" part, it doesn't support it <--- What I mean by that is when I go to add it, it is unrecognized.

class GameOverScene: SKScene {

     var HomeButton: SKNode! = nil


    init(size: CGSize, won: Bool) {
        super.init(size: size)

        backgroundColor = SKColor.whiteColor()

        HomeButton = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 100, height: 100))
        HomeButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
        HomeButton.userInteractionEnabled = true
        self.addChild(HomeButton)

        let message = won ? "You Won!" : "You Lose!"





        let label = SKLabelNode(fontNamed: "Title 1")
        label.text = message
        label.fontSize = 40
        label.fontColor = SKColor.blackColor()
        label.position = CGPoint(x: size.width/2, y: size.height/2)
        addChild(label)

        runAction(SKAction.sequence([SKAction.waitForDuration(3.0), SKAction.runBlock() {
            let reveal = SKTransition.flipHorizontalWithDuration(0.5)
            let scene = GameScene(size: size)
            self.view?.presentScene(scene, transition: reveal)
        }
        ]))


    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)

            if HomeButton.containsPoint(location) {

                HomeButton.prepareForSegueWithIdentifier()

            }
        }
    }

Note: I've tried using a button, but they don't work in and SKScene.

I'll be on to respond if there is any confusion.

like image 251
Nick Griffith Avatar asked Jul 04 '16 21:07

Nick Griffith


People also ask

What is the difference between SceneKit and SpriteKit?

You can assume SpriteKit is at Top of the SceneKit, As using SceneKit you can add 3D models into Augmented Reality while SpriteKit is used to add extra sprites onto the model. In short SpriteKit is revolution in Gaming.

Can you use SpriteKit with SwiftUI?

Even though the default Game Xcode Template creates the project based on a UIKit application, you can create a SwiftUI app and put your SpriteKit game inside it without any hustle thanks to the SpriteView view!

How to create a new UIButton in Swift?

A new UIButton can be created of different types: ButtonType. close. To create a regular, system type button in Swift you will need to create a new instance of UIButton class. To create a button of a different type like for example ButtonType. close, you will do:

How to create a game scene in SwiftUI using spriteview?

Create a variable that will initialize and host the Game Scene var scene: SKScene { let scene = GameScene() scene.size = CGSize(width: 216, height: 216) scene.scaleMode = .fill return scene } var body: some View { // 3. Using the SpriteView, show the game scene in your SwiftUI view // You can even use modifiers!

How to create a system type button in Swift?

To create a regular, system type button in Swift you will need to create a new instance of UIButton class. To create a button of a different type like for example ButtonType. close, you will do: To create a new UIButton, set its width, height, and position the button within a view programmatically, you can do with CGRect class. For example:

Is it possible to create a SpriteKit game in Xcode?

Even though the default Game Xcode Template creates the project based on a UIKit application, you can create a SwiftUI app and put your SpriteKit game inside it without any hustle thanks to the SpriteView view! The documentation page for the SpriteView SwiftUI view.


2 Answers

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:

button.png buttonSelected

like image 73
laserman Avatar answered Oct 06 '22 08:10

laserman


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)
}
like image 30
Alessandro Ornano Avatar answered Oct 06 '22 08:10

Alessandro Ornano