Search code examples
iosswiftsprite-kitnodesskscene

swift skscene touch and hold other action than just touching


So I recently made a swift game with sprite kit and now I am stuck. I have a character selection screen and I want to show a description for the character if you hold on the character, but if you just touch it you choose it and play with it. I already have the code to play/show the description. I just need to know when to call the corresponding functions and how to differentiate if the node is held or just touched.

Thanks in advance


Solution

  • So, here is how you can do it using SKActions... Also, there is another way using SKAction but I can't really show you all the possibilities :) Anyways, here is the code which does what you want:

    import SpriteKit
    import GameplayKit
    
    class GameScene: SKScene {
    
        let kLongPressDelayActionKey = "longPressDelay"
        let kLongPressStartedActionKey = "longPressStarted"
        let kStoppingLongPressActionKey = "stoppingLongPressActionKey"
    
        let desc = SKSpriteNode(color: .white, size: CGSize(width: 150, height: 150))
        let button = SKSpriteNode(color: .purple, size: CGSize(width: 150, height: 150))
        let other = SKSpriteNode(color: .yellow, size: CGSize(width: 150, height: 150))
        override func didMove(to view: SKView) {
    
            addChild(desc)
            addChild(button)
            addChild(other)
    
            button.position.y = -160
            button.name = "button"
            desc.alpha = 0.0
            desc.name = "description"
            other.name = "other"
            other.alpha = 0.0
            other.position.y = -320
    
        }
    
        private func singleTap(onNode:SKNode){
    
            other.alpha = other.alpha == 0.0 ? 1.0 : 0.0
        }
    
        private func startLongPress(withDuration duration:TimeInterval){
    
            //How long does it take before long press is fired
            //If user moves his finger of the screen within this delay, the single tap will be fired
            let delay = SKAction.wait(forDuration: duration)
    
            let completion = SKAction.run({
            [unowned self] in
    
                 self.desc.removeAction(forKey: self.kLongPressDelayActionKey)
                 self.desc.run(SKAction.fadeIn(withDuration: 0.5), withKey: self.kLongPressStartedActionKey)
            })
    
            self.desc.run(SKAction.sequence([delay,completion]), withKey: kLongPressDelayActionKey)
    
        }
    
        private func stopLongPress(){
    
            //Fire single tap and stop long press
            if desc.action(forKey: kLongPressDelayActionKey) != nil{
    
                desc.removeAction(forKey: kLongPressDelayActionKey)
                self.singleTap(onNode: self.other)
            //or just stop the long press
            }else{
    
                desc.removeAction(forKey: kLongPressStartedActionKey)
    
                //Start fade out action if it isn't already started
                if desc.action(forKey: kStoppingLongPressActionKey) == nil {
                    desc.run(SKAction.fadeOut(withDuration: 0.2), withKey: kStoppingLongPressActionKey)
                }
            }
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    
            //To stop the long press if we slide a finger of the button
            if let touch = touches.first {
    
                let location = touch.location(in: self)
    
                if !button.contains(location) {
    
                  stopLongPress()
    
                }
    
            }
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
            if let touch = touches.first {
    
                let location = touch.location(in: self)
    
                if button.contains(location) {
                    print("Button tapped")
    
                    startLongPress( withDuration: 1)
                }
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    
           stopLongPress()
        }
    }
    

    You can run the code and you will see that if you tap on the purple button, a yellow box will pop up. If you tap it again, it will hide. Also, if you hold your finger on purple box for 1 second, a white box will fade in. When you release the finger, it will fade out. As an addition, I have implemented touchesMoved, so if you slide your finger of the purple button while blue box is out, it will fade out as well.

    So in this example, I have fused long press with single tap. Single tap is considered to be everything that is not long press. Eg, if user holds his finger 0.01 sec, or 0.5 sec, or 0.99 sec, it will be considered as single tap and yellow box will pop out. If you hold your finger for >= 1 second, the long press action will be triggered.

    Another way would be to use gesture recognizers...And I will probably make an example for that later :)