Can I make SCNBox tappable and interactive?(我能让SCNBox变得可点击和互动吗?)
问题描述
我正在尝试使用SwiftUI中的SCNBox创建旋转立方体,您可以在其中点击框的每一侧,不同的弹出屏幕/视图将出现文本等。我有旋转的SCNBox立方体,但如何使其可点击和交互,以及如何使其重定向到其他视图?
这就是我当前尝试将Box场景插入到SwiftUI视图中的方法,然后该视图将进入正在退出的应用程序的视图层次结构。
import SwiftUI
import SceneKit
class BoxNode: SCNNode {
    override init() {
        super.init()
        self.geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.0)
        
        self.geometry?.firstMaterial?.shininess = 50
        
        let action = SCNAction.rotate(by: 360 * CGFloat(Double.pi / 180), around: SCNVector3(x:0, y:1, z:0), duration: 8)
        
        let repeatAction = SCNAction.repeatForever(action)
        
        self.runAction(repeatAction)
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
struct BoxScene: View {
        var scene = SCNScene(named: "MyScene")
        
        var cameraNode: SCNNode? {
                scene?.rootNode.childNode(withName: "camera", recursively: false)
        }
    
        var boxNode: SCNNode? {
                scene?.rootNode.addChildNode(BoxNode())
        }
        
        var body: some View {
                SceneView(
                        scene: scene,
                        pointOfView: cameraNode,
                        options: []
                )
        }
}
但是,此代码无法编译-它在以下行中显示,在没有更多上下文的情况下,表达式类型不明确:
var boxNode: SCNNode? {
                scene?.rootNode.addChildNode(BoxNode())
        }
推荐答案
下面是一个使用SCNBox可能有帮助的示例。
BoxNode是使用SCNBox作为其几何体的自定义SCNNode。每一面都设置了不同的颜色,以便很容易看到,如果您有想要显示的更精细的东西,可以使用UIImages。由于我们正在使用SceneKit、UIKit和SwiftUI,因此请确保在需要的位置导入它们。
class BoxNode: SCNNode {
    
    override init() {
        let length: CGFloat = 5
        let box = SCNBox(width: length, height: length, length: length, chamferRadius: 0)
        
        box.materials = [UIColor.red, .yellow, .green, .blue, .purple, .brown].map {
            let material = SCNMaterial()
            material.diffuse.contents = $0
            material.isDoubleSided = true
            material.transparencyMode = .aOne
            return material
        }
        
        super.init()
        
        self.geometry = box
        self.name = "boxNode"
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
然后在自定义SCNScene中使用它,BoxNode将BoxNode添加为根节点,并设置旋转动画并旋转多维数据集。
class GameScene: SCNScene {
    
    override init() {
        super.init()
        
        let cubeNode = BoxNode()
        self.rootNode.addChildNode(cubeNode)
        
        let xAngle = SCNMatrix4MakeRotation(.pi / 3.8, 1, 0, 0)
        let zAngle = SCNMatrix4MakeRotation(-.pi / 4, 0, 0, 1)
        cubeNode.pivot = SCNMatrix4Mult(SCNMatrix4Mult(xAngle, zAngle), cubeNode.transform)
        
        // Rotate the cube
        let animation = CAKeyframeAnimation(keyPath: "rotation")
        animation.values = [SCNVector4(1, 1, 0.3, 0 * .pi),
                            SCNVector4(1, 1, 0.3, 1 * .pi),
                            SCNVector4(1, 1, 0.3, 2 * .pi)]
        animation.duration = 5
        animation.repeatCount = HUGE
        cubeNode.addAnimation(animation, forKey: "rotation")
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
然后将GameScene设置在SCNView内的UIViewController内。SCNView固定在视图控制器的边缘,我们覆盖touchesBegan,以便可以与SceneView交互。我还创建了一个变量sideTapped,因为这将允许您注入回调,这样您就可以知道立方体上的哪一侧被点击了。
class GameSceneViewController: UIViewController {
    
    private let sceneView: SCNView = .init(frame: .zero)
    private let scene = GameScene()
    
    var sideTapped: ((Int) -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(sceneView)
        
        NSLayoutConstraint.activate([
            sceneView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            sceneView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            sceneView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            sceneView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        ])
        sceneView.scene = scene
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touchPoint = touches.first?.location(in: sceneView) else { return }
        guard let hitTestResult = sceneView.hitTest(touchPoint, options: nil).first else { return }
        guard hitTestResult.node is BoxNode else { return }
        
        sideTapped?(hitTestResult.geometryIndex)
    }
}
因为我们在内部SwiftUI使用它,所以我们需要一个UIViewControllerRepresentable。这里我们将sideTapped函数传递给GameSceneViewController。
struct GSViewControllerRepresentable: UIViewControllerRepresentable {
    
    let sideTapped: ((Int) -> Void)?
    
    func makeUIViewController(context: Context) -> GameSceneViewController {
        let vc = GameSceneViewController()
        vc.sideTapped = sideTapped
        return vc
    }
    
    func updateUIViewController(_ uiViewController: GameSceneViewController, context: Context) {
    
    }
}
现在我们可以将其添加到SwiftUI视图。
struct ContentView: View {
    var body: some View {
        GSViewControllerRepresentable { side in
            print(side)
            // Perform additional logic here
        }
        .frame(width: 200, height: 200)
    }
}
最终结果应如下所示:
点击立方体的一侧将打印一个从0到5的数字,表示您点击的是哪一侧。根据所使用的材料,数字应与材料的索引相对应,因此在本例中:- 0为红色
 - %1为黄色
 - %2为绿色
 - 3为蓝色
 - 4为紫色
 - 5为棕色
 
这应该会为您提供一些您应该能够关闭导航的内容。
我不是SceneKit方面的专家,但周末我一直在使用它,并且基本上完成了您所要求的操作。希望这能给你一个想法,有很多方法可以实现你想做的事情,这只是一种可能性。
如果您想要将立方体滑动到变更面,而不是让它自己旋转,您可能需要查看此answer。
这篇关于我能让SCNBox变得可点击和互动吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:我能让SCNBox变得可点击和互动吗?
				
        
 
            
        - 在测试浓缩咖啡时,Android设备不会在屏幕上启动活动 2022-01-01
 - Android - 拆分 Drawable 2022-01-01
 - android 4中的android RadioButton问题 2022-01-01
 - MalformedJsonException:在第1行第1列路径中使用JsonReader.setLenient(True)接受格式错误的JSON 2022-01-01
 - Android viewpager检测滑动超出范围 2022-01-01
 - Android - 我如何找出用户有多少未读电子邮件? 2022-01-01
 - 使用自定义动画时在 iOS9 上忽略 edgesForExtendedLayout 2022-01-01
 - 想使用ViewPager,无法识别android.support.*? 2022-01-01
 - 用 Swift 实现 UITextFieldDelegate 2022-01-01
 - 如何检查发送到 Android 应用程序的 Firebase 消息的传递状态? 2022-01-01
 
				
				
				
				