swift SpriteNode and SceneKit multiple touch gestures

256 Views Asked by At

I am making a word game. For this I am using SceneKit and adding a SpriteNodes to represent letter tiles. The idea is that when a user clicks on a letter tile, some extra tiles appear around it with different letter options. My issue is regarding the touch gestures for various interactions.

When a user taps on a letter tile, additional tiles are shown. I have achieved this using the following method in my tile SpriteNode class:

override func touchesBegan(_ touches:Set<UITouch> , with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    delegate?.updateLetter(row: row, column: column, x:xcoord, y:ycoord, useCase: 1)
}

This triggers the delegate correctly which shows another sprite node. What I would like to achieve is for a long press to remove the sprite node from parent. I have found the .removeFromParent() method, however I cannot get this to detect a long press gesture.

My understanding is that this type of gesture must be added using UIGestureRecognizer. I can add the following method to my Scene class:

override func didMove(to view: SKView) {
    let longPress = UILongPressGestureRecognizer(target: self,
        action: #selector(GameScene.longPress(sender:)))
    view.addGestureRecognizer(longPress)
}
@objc func longPress(sender: UILongPressGestureRecognizer) {
    print("Long Press")

This will detect a long press anywhere on the scene. However I need to be able to handle the pressed nodes properties before removing it. I have tried adding the below to the longPress function:

let location = sender.location(in: self)
let touchedNodes = nodes(at: location)
let firstTouchedNode = atPoint(location).name
touchedNodes[0].removeFromParent()

but I get the following error: Cannot convert value of type 'GameScene' to expected argument type 'UIView?'

This seems a little bit of a messy way of doing things, as I have touch methods in different places.

So my question is, how can I keep the current touchesBegan method that is in the tile class, and add a long press gesture to be able to reference and delete the spriteNode?

2

There are 2 best solutions below

1
Voltan On

Long press gestures are continuous gestures that may be called multiple times as you are seeing. Have you tried Recognizer.State.began, .changed, .ended? I solved a similar problem doing things this way.

EDIT - I think one way to get there is to get your object on handleTap and hang on to the object. Then when LongPress happens, you already have your node. If something changes before longPress, obviously you need to reset. Sorry, this is some extra code on here, but look at hitTest.

@objc func handleTap(recognizer: UITapGestureRecognizer)
    {
        let location: CGPoint = recognizer.location(in: gameScene)
        
        if(data.isAirStrikeModeOn == true)
        {
            let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
            let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
            gameControl.airStrike(position: scenePoint)
        }
        else
        {
            let hitResults = gameScene.hitTest(location, options: hitTestOptions)
            for vHit in hitResults
            {
                if(vHit.node.name?.prefix(5) == "Panel")
                {
                    // May have selected an invalid panel or auto upgrade was on
                    if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
                    return
                }
            }
        }
    }
0
samp17 On

So I am not completely satisfied with this answer, however it is a work around for what I need.

What I have done is added two variables ‘touchesStart’ and ‘touchesEnd’ to my tiles class. Then in touchesBegan() I add a call to update touchesStart with CACurrentMediaTime() and update touchesEnd via the touchesEnded() function.

Then in the touchesEnded() I subtract touchesStart from touchesEnd. If the difference is more than 1.0 I call the function for long press. If less than 1.0 I call the function for tap.