如何将SCNNode旋转多个角度?

时间:2019-12-13 11:55:06

标签: rotation swiftui scenekit

我在SCNCylinder中有一个圆柱作为SCNView。我想将圆柱体旋转多个给定角度。我正在使用SwiftUI提供所需的输入(旋转角度)。假设我输入为90°。目前,它可以轻松旋转90°,但是如果我再给它输入180°,它会回到其原始位置,然后旋转180°。我希望它从90度旋转后的位置旋转180度。

这是我的代码:

struct ContentView: View {
 @State var rotationAngle: Float = 0
 var body: some View {

    VStack{

        Text("180°").onTapGesture {
            self.rotationAngle = 180.0
        }

        Divider()

        Text("90°").onTapGesture {
            self.rotationAngle = 90.0
        }

        SceneKitView(angle: $rotationAngle)
            .position(x: 225.0, y: 175)
            .frame(width: 300, height: 300, alignment: .center)

    }
  }    
}

struct SceneKitView: UIViewRepresentable {
  @Binding var angle: Float


func degreesToRadians(_ degrees: Float) -> CGFloat {
    return CGFloat(degrees * .pi / 180)
}

func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {

    let sceneView = SCNView()
    sceneView.scene = SCNScene()
    sceneView.allowsCameraControl = true
    sceneView.autoenablesDefaultLighting = true
    sceneView.backgroundColor = UIColor.white
    sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)

    return sceneView
}

func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {

        sceneView.scene?.rootNode.enumerateChildNodes { (node, stop) in
            node.removeFromParentNode() }


        let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
        let cylindernode = SCNNode(geometry: cylinder)
        cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
        cylinder.firstMaterial?.diffuse.contents = UIColor.green


        cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)




        let rotation = SCNAction.rotate(by: self.degreesToRadians(self.angle),
                                        around: SCNVector3(1, 0, 0), duration: 5)



         cylindernode.runAction(rotation)


        sceneView.scene?.rootNode.addChildNode(cylindernode)

   }      

无论我给它几个角度,我都希望它能正确旋转。

1 个答案:

答案 0 :(得分:1)

要清楚一点,您希望每次按下按钮时圆柱体都从其当前位置继续旋转。不要使圆柱体固定在90或180的位置。

因为您的当前代码似乎已设置为执行第二件事(rotate:by除外)。 rotationAngle保持为固定状态。确实,您应该向节点发送旋转命令,而不必将其旋转量保持在状态变量中。

但是,使用现在的代码:

updateUIView内部,坚持只对节点进行更新。现在,您正在拆除并在每次rotationAngle更改时将整个节点层次结构重新创建到其原始位置。这可能就是为什么要重绘的原因。 而是在makeUIViewSceneKitView的初始化程序内进行设置,而在updateUIView内进行设置,只需应用新的旋转方式即可。

第二个问题是,当您通过按{180}来设置rotationAngle时,如果再次按下相同的按钮,则该值将再次设置为180。就SwiftUI而言,该值没有改变。而言,不需要调用updateUIView。有几种方法可以使它更新,但是一种快速简便的方法可能只是旋转0,然后立即旋转所需的角度。 但是同样,您实际上不应该将该角度保留在状态变量中。最好只在rotateCylinder(angle: Angle)中创建一个SceneKitView函数,然后从按钮中调用它。

此外,为什么不使用新的Angle类型呢?这是其中一些更改的代码。我认为您正在尝试这样做,但是我可能是错的。

import SwiftUI
import UIKit
import SceneKit

struct ContentView: View {

  @State var rotationAngle: Angle = .zero

  var body: some View {

    VStack{

      HStack {

        Spacer()
        Text("180°").onTapGesture {
          self.rotationAngle = .zero
          self.rotationAngle = .degrees(180)
        }
        Spacer()

        Divider()

        Spacer()
        Text("90°").onTapGesture {
          self.rotationAngle = .zero
          self.rotationAngle = .degrees(90)
        }
        Spacer()

        }.frame(height: 100).padding()

      SceneKitView(radius: 0.2, height: 2, angle: $rotationAngle)

    }
  }
}

struct SceneKitView: UIViewRepresentable {

  @Binding var angle: Angle

  let node: SCNNode

  init(radius: CGFloat, height: CGFloat, angle: Binding<Angle>) {

    let cylinder = SCNCylinder(radius: radius, height: height)
    cylinder.firstMaterial?.diffuse.contents = UIColor.green
    self.node = SCNNode(geometry: cylinder)

    self._angle = angle
  }

  func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {

    let sceneView = SCNView()
    sceneView.scene = SCNScene()
    sceneView.autoenablesDefaultLighting = true
    sceneView.scene?.rootNode.addChildNode(node)

    return sceneView
  }

  func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
    print("Updating view \(angle.degrees)")

    // To continue rotating
    let rotation = SCNAction.rotate(by: CGFloat(angle.radians), around: SCNVector3(1, 0, 0), duration: 3)

    // To go to a fixed angle state
    //let rotation = SCNAction.rotate(toAxisAngle: SCNVector4(1, 0, 0, angle.radians), duration: 3)

    node.runAction(rotation)

  }

}