3

Imagine a game world that's nothing other than an SKTileMapNode with 10x10 tiles on the screen.

The user touches a tile.

Does SKTileMapNode provide a way to know which tile has been touched? Or do coordinate hunts need be done to determine which tile is in the location of the touch?

Or is there some other way that this should be done?

Confused
  • 6,048
  • 6
  • 34
  • 75

1 Answers1

4

Using a UITapGestureRecognizer you can retrieve the touched tile using the tileDefinition function from SKTileMapNode.

func handleTapFrom(recognizer: UITapGestureRecognizer) {
    if recognizer.state != .ended {
        return
    }

    let recognizorLocation = recognizer.location(in: recognizer.view!)
    let location = self.convertPoint(fromView: recognizorLocation)

    guard let map = childNode(withName: "background") as? SKTileMapNode else {
        fatalError("Background node not loaded")
    }

    let column = map.tileColumnIndex(fromPosition: location)
    let row = map.tileRowIndex(fromPosition: location)
    let tile = map.tileDefinition(atColumn: column, row: row)
}

Then if you have added userData in the TilemapEditor, this can be retrieved. Values to include in userData might be cost to move through the tile etc.

let data = tile.userData?.value(forKey: "myKey")

The advantage of using Recognizers is that Tap, Pan and Long Press can be handled cleanly in separate functions that don't interfere with each other. You initialise the gesture recognizor in SKScene.

override func didMove(to view: SKView) {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTapFrom(recognizer:)))
    tapGestureRecognizer.numberOfTapsRequired = 1
    view.addGestureRecognizer(tapGestureRecognizer)
}
Mark Brownsword
  • 2,287
  • 2
  • 14
  • 23
  • My sincere apologies. My stupidity is immense. Where should this code go? – Confused Oct 02 '16 at 01:42
  • Do you mean to keep Pan available this way, so the user could pan around a tilemap larger than the screen without having to handle touchesBegun, touchesMoved, etc? – Confused Oct 02 '16 at 01:44
  • I know nothing about Gesture Recognisers. Reading here, it seems they can be attached/written into any View. https://developer.apple.com/library/content/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html .... being that a SKView is SpriteKit's only subclass of UIView, is that where this must go in order to be called by the system? – Confused Oct 02 '16 at 02:00
  • @Confused I updated the answer with an example of initialising a `UITapGestureRecognizor`. This can be used instead of `touchesBegan`. Pan and Long Press are similar, but that's another question :) – Mark Brownsword Oct 02 '16 at 02:45
  • sorry, I still don't see where the main body of code should go. Where should the function handleTapFrom(etc...) be declared? – Confused Oct 02 '16 at 02:49
  • @Confused Put the code in your scene e.g. `GameScene.swift` – Mark Brownsword Oct 02 '16 at 02:51
  • I hate the blend of OOP, POP, Swift's love affair with Value Types , SpriteKit's attempts to be part of Cocoa and GameplayKit's ECS aspirations. – Confused Oct 02 '16 at 02:57
  • and I haven't even started on how much I despise the lameduck efforts to make SpriteKit and SceneKit editors inside Xcode. ;) – Confused Oct 02 '16 at 02:58
  • I read this, many times: http://www.spritekitlessons.com/gesture-recognizer-with-sprite-kit-and-swift/ and think I now have figured out some of what's going on. But not all. Is this part a placeholder? "#selector(self.handleTapFrom(recognizer:")) – Confused Oct 02 '16 at 04:37
  • @confused It's not a placeholder, that links the recognizor to the specified function, though I think this is new syntax for Swift 3. – Mark Brownsword Oct 02 '16 at 06:13
  • Argument of '#selector' refers to instance method 'handleTapFrom(recognizer:)' that is not exposed to Objective-C Add '@objc' to expose this instance method to Objective-C – mvladk Jul 08 '18 at 08:41