下面是应用程序的代码,该应用程序让用户放置一个字段,然后根据自己的喜好定向它。我能够实现这一点,但是,我有两个问题。 1)childNodes沿自己的y轴旋转,我如何让它们绕parentNode y轴旋转? 2)一旦用户确定了方向,我如何让他们点击以将其锁定在适当的位置?
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
private var movedField: SCNNode?
private var hud :MBProgressHUD!
var currentAngleY: Float = 0.0
var fieldNode: SCNNode!
var isRotating = false
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.autoenablesDefaultLighting = true
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene()
sceneView.scene = scene
registerGestureRecognizers()
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
if anchor is ARPlaneAnchor {
DispatchQueue.main.async {
self.hud = MBProgressHUD.showAdded(to: self.sceneView, animated: true)
self.hud.label.text = "Detecting Plane..."
self.hud.label.text = "Plane Detected"
self.hud.hide(animated: true, afterDelay: 1.0)
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
private func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(recognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
self.sceneView.addGestureRecognizer(tapGestureRecognizer)
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinched(recognizer:)))
self.sceneView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveField(recognizer:)))
panGestureRecognizer.maximumNumberOfTouches = 1
panGestureRecognizer.minimumNumberOfTouches = 1
self.sceneView.addGestureRecognizer(panGestureRecognizer)
let rotateGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(rotateField(recognizer:)))
rotateGestureRecognizer.maximumNumberOfTouches = 2
rotateGestureRecognizer.minimumNumberOfTouches = 2
self.view.addGestureRecognizer(rotateGestureRecognizer)
}
@objc func pinched(recognizer :UIPinchGestureRecognizer) {
if recognizer.state == .changed {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = self.sceneView.hitTest(touch, options: nil)
if let hitTest = hitTestResults.first {
let fieldNode = hitTest.node
let pinchScaleX = Float(recognizer.scale) * fieldNode.scale.x
let pinchScaleY = Float(recognizer.scale) * fieldNode.scale.y
let pinchScaleZ = Float(recognizer.scale) * fieldNode.scale.z
fieldNode.scale = SCNVector3(pinchScaleX,pinchScaleY,pinchScaleZ)
recognizer.scale = 1
}
}
}
@objc func moveField(recognizer: UIPanGestureRecognizer) {
if recognizer.state == .began {
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let result = sceneView.hitTest(tapPoint ?? CGPoint.zero, options: nil)
if result.count == 0 {
return
}
let hitResult: SCNHitTestResult? = result.first
if (hitResult?.node.name == "Terrain") {
movedField = hitResult?.node
} else if (hitResult?.node.parent?.name == "Terrain") {
movedField = hitResult?.node.parent
}
if (movedField != nil) {
}
}
if recognizer.state == .changed {
if (movedField != nil) {
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(tapPoint ?? CGPoint.zero, types: .featurePoint)
let result: ARHitTestResult? = hitResults.last
let matrix: SCNMatrix4 = SCNMatrix4((result?.worldTransform)!)
//SCNMatrix4FromMat4((result?.worldTransform)!)
let vector: SCNVector3 = SCNVector3Make(matrix.m41, matrix.m42, matrix.m43)
movedField?.position = vector
}
}
if recognizer.state == .ended {
movedField = nil
}
}
@objc func tapped(recognizer :UITapGestureRecognizer) {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = sceneView.hitTest(touch)
guard let hitTest = hitTestResults.first?.node else {
let hitTestResultsWithExistingPlane = sceneView.hitTest(touch, types: .existingPlane)
let fieldScene = SCNScene(named: "Field.dae")!
guard let fieldNode = fieldScene.rootNode.childNode(withName:"Terrain", recursively: true) else {
return
}
if let hitTestAvailable = hitTestResultsWithExistingPlane.first {
fieldNode.position = SCNVector3(hitTestAvailable.worldTransform.columns.3.x,hitTestAvailable.worldTransform.columns.3.y,hitTestAvailable.worldTransform.columns.3.z)
self.sceneView.scene.rootNode.addChildNode(fieldNode)
return
}
return
}
hitTest.removeFromParentNode()
}
@objc func rotateField(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: recognizer.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
DispatchQueue.main.async {
self.sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
node.eulerAngles.y = newAngleY
}
}
if(recognizer.state == .ended) { currentAngleY = newAngleY }
}
}