使用按钮执行 SCNAction (SwiftUI)

Perform SCNAction using a button (SwiftUI)

我使用 UIKit 创建了一个带有对象的 SceneView,它可以在按下按钮时执行 SCNActions:

import UIKit
import SceneKit

class ViewController: UIViewController {


    @IBOutlet weak var ScenekitView: SCNView!
    
    
    var scene:SCNScene!
    var ship:SCNNode!
    
    
    @IBAction func button(_ sender: Any) {
        let sequence = SCNAction.sequence([SCNAction.moveBy(x: 0, y: 0, z: -10, duration: 0.5),SCNAction.moveBy(x: 0, y: 0, z: 10, duration: 0.5)])
        ship.runAction(sequence)
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        scene = SCNScene(named: "mainScene.scn")!
        
        var cameraNode: SCNNode!
        cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        cameraNode.scale = SCNVector3(1, 1, 0.5)
        cameraNode.eulerAngles = SCNVector3(0, 0, 0)

        ScenekitView.scene = scene
        
        ship = scene.rootNode.childNode(withName: "ship reference", recursively: true)!
        let pos = SCNVector3Make(0, 0, 0)
        
        ship.runAction(SCNAction.move(to: pos, duration: 1))
    }
}

现在我想使用 SwiftUI 实现同样的效果。

虽然在这个例子中,我可以让相机在视图加载后执行一个动作,但我不知道如何让它在按下按钮后执行另一个动作。 特别是因为节点是在另一个子视图的函数中定义的。

这是我目前得到的:

import SwiftUI
import SceneKit


struct ContentView: View { 
    var body: some View{

        VStack {
            ScenekitView().ignoresSafeArea()

            Button("move") {
               //here I would like to call a function including a SCNAction

            }
        }   
    }
}


struct ScenekitView : UIViewRepresentable {

    func makeUIView(context: Context) -> SCNView {

        let scene = SCNScene(named: "SceneFile.scn")!

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        scene.rootNode.addChildNode(cameraNode)
        
        cameraNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 0, z: 5, duration: 1)))

        let scnView = SCNView()
        return scnView
    }

    func updateUIView(_ scnView: SCNView, context: Context) {
        scnView.scene = scene
        scnView.allowsCameraControl = false
    }
}

感谢任何有用的想法。

您可以创建父视图 (ContentView) 拥有的对象,将场景引用分配给 ScenekitView 并允许从 Button.

进行访问

您可能想要调整细节(例如完成相机设置的位置),但这是一般概念:

class SceneManager : ObservableObject {
    let scene = SCNScene(named: "SceneFile.scn")!
    var ship:SCNNode
    
    init() {
        ship = scene.rootNode.childNode(withName: "ship reference", recursively: true)!
    }
    
    func move() {
        let pos = SCNVector3Make(0, 0, 0)
        ship.runAction(SCNAction.move(to: pos, duration: 1))
    }
}

struct ContentView: View {
    @StateObject var sceneManager = SceneManager()
    
    var body: some View{

        VStack {
            ScenekitView(scene: sceneManager.scene)
                .ignoresSafeArea()

            Button("move") {
                sceneManager.move()
            }
        }
    }
}


struct ScenekitView : UIViewRepresentable {
    var scene : SCNScene
    
    func makeUIView(context: Context) -> SCNView {
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        scene.rootNode.addChildNode(cameraNode)
        
        cameraNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 0, z: 5, duration: 1)))
        let scnView = SCNView()
        scnView.scene = scene
        return scnView
    }

    func updateUIView(_ scnView: SCNView, context: Context) {
        scnView.allowsCameraControl = false
    }
}