I have this minimum repro code:
import SpriteKit
import GameplayKit
class MyGameScene3D: SCNScene {
weak var node3D: MyNode3D!
override init() {
super.init()
background.contents = UIColor.green
let playground = SCNNode()
playground.boundingBox = (
min: SCNVector3(x: 0, y: 0, z: 0),
max: SCNVector3(x: 10, y: 10, z: 10))
let box = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
box.position = SCNVector3(x: 5, y: 5, z: 5)
playground.addChildNode(box)
playground.position = SCNVector3(x: 0, y: 0, z: 0)
rootNode.addChildNode(playground)
let light = SCNLight()
light.type = .ambient
let lightNode = SCNNode()
lightNode.light = light
rootNode.addChildNode(lightNode)
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.eulerAngles = SCNVector3(x: -3.14/2, y: 0, z: 0)
cameraNode.position = SCNVector3(x: 5, y: 11, z: 5)
rootNode.addChildNode(cameraNode)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func handleTouchBegan(_ location: CGPoint) {
let res = node3D.hitTest(location)
print(res)
}
}
class MyNode3D: SK3DNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let scene = scnScene as! MyGameScene3D
let location = touch.location(in: self)
print(location)
scene.handleTouchBegan(location)
}
}
class GameScene: SKScene {
init() {
super.init(size: CGSize(width: 500, height: 1000))
self.backgroundColor = .red
let node3D = MyNode3D()
let scene3D = MyGameScene3D()
node3D.scnScene = scene3D
scene3D.node3D = node3D
node3D.isUserInteractionEnabled = true
node3D.viewportSize = CGSize(width: 100, height: 200)
node3D.position = CGPoint(x: 50, y: 100)
addChild(node3D)
let up = SKSpriteNode(color: .blue, size: CGSize(width: 500, height: 10))
up.anchorPoint = CGPoint(x: 0, y:0)
up.position = CGPoint(x:0, y:200)
addChild(up)
let right = SKSpriteNode(color: .gray, size: CGSize(width: 10, height: 500))
right.anchorPoint = CGPoint(x:0,y: 0)
right.position = CGPoint(x:100, y:0)
addChild(right)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Basically, I have a SK3DNode of size 100x200, positioned at lower left corner of the screen (see screenshot below).
Then in this SK3DNode, I have a SCNScene, where I put a 10x10x10 Playground node at position (0, 0, 0). Then I put a camera node right at the top of the Playground at position (5, 11, 5), and the camera looks down along the -y axis, with euler angle = (-90, 0, 0).
Then in this Playground, I put a small box of size 1x1x1, at the center of the Playground at (5, 5, 5).
The 2 long bars (gray & blue) are just there to indicate the boundary of the SK3DNode.
The result rendering is correct (see screenshot below). However, I can't get the hit test working. I tap on the center 1x1x1 box on screen, get the right coordinate printed out, but the hit test result is empty. I want to be get the center 1x1x1 box when hitting there. How can I do so?
Update:
I tried to loop through all the pixels from -2000 to 2000, and still no hit:
func handleTouchBegan(_ location: CGPoint) {
for x in -2000...2000 {
print("handling x: \(x)")
for y in -2000...2000 {
let res = node3D.hitTest(location)
if !res.isEmpty {
print("\(x), \(y), \(res)")
}
}
}
print("Done")
}