【问题标题】:SceneKit: Hit test not work with billboarded quadsSceneKit:命中测试不适用于广告牌四边形
【发布时间】:2017-02-17 06:11:26
【问题描述】:

我使用 SceneKit 制作了广告牌四边形。 cameraNode 与 UIDeviceMotion 同步,广告牌节点按我预期出现。 问题是,我希望在点击它时调用这些节点。 为此,我使用了带有 hitTest 的 UITapGestureRecognizer。 这是我的一些代码。

// ==== in viewDidLoad

// initialize tap gesture
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onNodeTapped))
    sceneView.addGestureRecognizer(tapGesture)

// initialize scenekit.scene
let scene = SCNScene()
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(worldNode)
sceneView.scene = scene
sceneView.autoenablesDefaultLighting = true
sceneView.pointOfView = cameraNode

这是点击处理程序

func onNodeTapped(_ gestureRecognize: UIGestureRecognizer) {
    let location = gestureRecognize.location(in: sceneView)   // <---- updated
    let hitResults = sceneView.hitTest(location, options: nil)
    for result in hitResults {
        // FOR_TEST: hit test visualization
        if let material = result.node.geometry?.materials.first {
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.5
            SCNTransaction.completionBlock = {
                SCNTransaction.begin()
                SCNTransaction.animationDuration = 0.5
                material.emission.contents = UIColor.black
                SCNTransaction.commit()
            }
            material.emission.contents = UIColor.red
            SCNTransaction.commit()
        }

        // target tap event handling
        if let target = (result.node as? TargetNode)?.target {
            if onTargetTapped(target) {
                return
            }
        }
    }
}

此代码很少使用。我的意思是可视化部分仅响应 20 次中的 1 次,而 onTargetTapped 仅被称为 100 次中的 1 次... 奇怪的是瞄准很好,这意味着这不是坐标问题。

我发现了一些与 SCNHitTestOption.categoryBitMask 相关的东西,但它根本没有帮助。

此外,当我打开此 Scenview 时,控制台上会出现此错误消息。 “[SceneKit] 错误:_C3DUnProjectPoints 中的错误” 也许这个消息与 hitTest 故障有关?


更新

此代码用于构建广告牌 SCNGeometry 和 SCNNode

override func initializeGeometry() -> SCNGeometry {
    let geometry = SCNPlane(width: width, height: height)
    let material = geometry.materials.first
    material?.diffuse.contents = initializeTexture()
    material?.writesToDepthBuffer = false
    material?.readsFromDepthBuffer = false
    return geometry
}

// ==== 构建节点

node = SCNNode()
node.geometry = initializeGeometry()
node.categoryBitMask = MyConstraints.targetNodeHitTestCategoryBitMask
node.constraints = [SCNBillboardConstraint()]

【问题讨论】:

  • 我们可以看看设置广告牌的代码吗?

标签: ios swift scenekit


【解决方案1】:

您正在检索annotationsDisplayView 中的点击坐标,然后将这些坐标传递给sceneView 以进行命中测试查找。除非这些视图完全重叠,否则您会得到不匹配的结果。

我想你想要

    let location = gestureRecognize.location(in: sceneView)

改为。

我很好奇命中测试是否会使用广告牌的旋转版本。我确认确实如此。这是一个工作 (Mac) 示例。

SCNView 子类中:

override func mouseDown(with theEvent: NSEvent) {
    /* Called when a mouse click occurs */

    // check what nodes are clicked
    let p = self.convert(theEvent.locationInWindow, from: nil)
    let hitResults = self.hitTest(p, options: [:])
    // check that we clicked on at least one object
    if hitResults.count > 0 {
        Swift.print("hit me")
    }
    for result in hitResults {
        Swift.print(result.node.name)

        // get its material
        let material = result.node.geometry!.firstMaterial!

        // highlight it
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.5

        // on completion - unhighlight
        SCNTransaction.completionBlock = {
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.5

            material.emission.contents = NSColor.black

            SCNTransaction.commit()
        }

        material.emission.contents = NSColor.yellow

        SCNTransaction.commit()
    }

    super.mouseDown(with: theEvent)
}

在视图控制器中:

override func awakeFromNib(){
    super.awakeFromNib()

    // create a new scene
    let scene = SCNScene()

    let side = CGFloat(2.0)
    let quad1 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
    quad1.name = "green"
    quad1.geometry?.firstMaterial?.diffuse.contents = NSColor.green

    let quad2 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
    quad2.name = "red"
    quad2.geometry?.firstMaterial?.diffuse.contents = NSColor.red

    let quad3 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
    quad3.name = "blue"
    quad3.geometry?.firstMaterial?.diffuse.contents = NSColor.blue

    scene.rootNode.addChildNode(quad1)
    scene.rootNode.addChildNode(quad2)
    scene.rootNode.addChildNode(quad3)

    let rotation = CGFloat(M_PI_2) * 0.9
    quad1.position.y = side/2
    quad1.eulerAngles.y = rotation

    quad2.position.x = side/2
    quad2.eulerAngles.x = rotation

    // comment out these constraints to verify that they matter
    quad1.constraints = [SCNBillboardConstraint()]
    quad2.constraints = [SCNBillboardConstraint()]

    // create and add a camera to the scene
    let cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    scene.rootNode.addChildNode(cameraNode)

    // place the camera
    cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

    // create and add a light to the scene
    let lightNode = SCNNode()
    lightNode.light = SCNLight()
    lightNode.light!.type = .omni
    lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
    scene.rootNode.addChildNode(lightNode)

    // create and add an ambient light to the scene
    let ambientLightNode = SCNNode()
    ambientLightNode.light = SCNLight()
    ambientLightNode.light!.type = .ambient
    ambientLightNode.light!.color = NSColor.darkGray
    scene.rootNode.addChildNode(ambientLightNode)

    // set the scene to the view
    self.gameView!.scene = scene

    // allows the user to manipulate the camera
    self.gameView!.allowsCameraControl = true

    // show statistics such as fps and timing information
    self.gameView!.showsStatistics = true

    // configure the view
    self.gameView!.backgroundColor = NSColor.black
}

【讨论】:

  • 感谢您的回答,这是一个错字。我改了又试了,还是不行。
【解决方案2】:

无论您的节点是否受广告牌约束,命中测试都应该有效。对我有用。

override func viewDidLoad() {
  self.sceneView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap(_:))))
  ...
}

@objc func tap(_ recognizer: UITapGestureRecognizer) {
    let loc = recognizer.location(in: self.sceneView)
    if let hit = self.sceneView.hitTest(loc) {
      ...
    }
}  

【讨论】:

    猜你喜欢
    • 2019-12-27
    • 1970-01-01
    • 2017-08-04
    • 1970-01-01
    • 2018-01-25
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多