问题
I followed this code from @rickster which 100% works and looks great. In the video he's animating a SCNNode that is set using an ARAnchor from 1 position to another and back. I tried to do something similar except I want the node that is set with the ARAnchor to follow/update it's position to another node that is a child of the camera.
I'm having a problem updating the position in func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) { }
I tried to animate the node that is set with the ARAnchor to follow the other node but it's not working, it follows backwards and in reverse:
let animation = CABasicAnimation(keyPath: #keyPath(SCNNode.transform))
animation.fromValue = nodeSetWithARAnchor.transform
animation.toValue = nodeTiedToCamera.transform
animation.duration = 1
nodeSetWithARAnchor.removeAllAnimations()
nodeSetWithARAnchor.addAnimation(animation, forKey: nil)
I then tried to remove the ARAnchor and reset its node's .worldPostion
and .simdWorldTransform
but the node diappears. It's in steps 7 & 8 below.
How can I get the nodeSetWithARAnchor
to update its ARAnchor and position to always follow the nodeTiedToCamera
?
Update In Step 6 now that I set the nodeSetWithARAnchor SCVector3
to match the nodeTiedToCameradWorld's SCVector3
and set its .transform
to match the nodeTiedToCameradWorldTransform
@rickster's animation code works the best because I don't I have to remove any anchors. There is another problem though. The nodeSetWithARAnchor
responds when I move the device but it responds backwards and in reverse.
When I turn the device up the image goes right and when I turn the device down the image goes left. When I turn the device left the image goes up and when I turn the device right the image goes down. It's following the image I have tied to the camera but it's following it incorrectly.
let configuration = ARWorldTrackingConfiguration()
var nodeSetWithARAnchor: SCNNode?
var nodeTiedToCamera: SCNNode?
var anchors: [ARAnchor] = []
override func viewDidLoad() {
super.viewDidLoad()
configuration.planeDetection = [.horizontal, .vertical]
configuration.maximumNumberOfTrackedImages = 1
// 1. once this anchor is set inside renderer(_:didAdd:for:) I initialize the nodeSetWithARAnchor at 30cm behind the device's camera's initial position
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.3
let transform = simd_mul(self.sceneView.session.currentFrame!.camera.transform, translation)
let anchor = ARAnchor(transform: transform)
self.sceneView.session.add(anchor: anchor)
}
// 2. the nodeTiedToCamera will always go where ever the device's camera goes
let plane = SCNPlane(width: 0.1, height: 0.1)
nodeTiedToCamera = SCNNode(geometry: plane)
nodeTiedToCamera!.position = SCNVector3(x: -0.15, y: 0.45, z: -1.25) // I don't want it directly in front of the camera
nodeTiedToCamera!.geometry?.fisrtMaterial?.diffuse.contents = UIColor.red
sceneView.pointOfView.addChildNode(nodeTiedToCamera!)
}
// 3. I init the nodeSetWithARAnchor, add it to the sceneView's root node, and keep a copy of it's anchor
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
DispatchQueue.main.async {
if self.nodeSetWithARAnchor == nil {
// create geometry ...
self.nodeSetWithARAnchor = SCNNode(geometry: geometry)
node.addChildNode(self.nodeSetWithARAnchor!)
}
self.anchors.removeAll()
self.anchors.append(anchor)
}
}
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
DispatchQueue.main.async {
// 4. get the only child that is tied to the camera which is the nodeTiedToCamera
guard let pointOfView = self.sceneView.pointOfView else { return }
guard let child = pointOfView.childNodes.first else { return }
// 5. get it's .worldPosition && it's .simdWorldTransform
let nodeTiedToCameradWorldPosition = child.worldPosition
let nodeTiedToCameradWorldTransform = child.worldTransform
if let nodeSetWithARAnchor = self.nodeSetWithARAnchor, let anchorToRemove = self.anchors.first {
// 6. set the nodeSetWithARAnchor SCVector3 to match the nodeTiedToCameradWorld's SCVector3 and set its .transform to match the nodeTiedToCameradWorldTransform
nodeSetWithARAnchor.position = nodeTiedToCameradWorldPosition
nodeSetWithARAnchor.transform = nodeTiedToCameradWorldTransform
let animation = CABasicAnimation(keyPath: #keyPath(SCNNode.transform))
animation.fromValue = nodeSetWithARAnchor.transform
animation.toValue = nodeTiedToCamera.transform
animation.duration = 1
nodeSetWithARAnchor.removeAllAnimations()
nodeSetWithARAnchor.addAnimation(animation, forKey: nil)
// 7. remove all ARAnchors
//self.sceneView.session.remove(anchor: anchorToRemove)
//self.anchors.removeAll()
// 8. add a new anchor to the session and set it with the nodeSetWithARAnchor.simdWorldTransform
//let anchor = ARAnchor(transform: nodeSetWithARAnchor.simdWorldTransform)
//self.sceneView.session.add(anchor: anchor)
}
}
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
guard let node = self.nodeSetWithARAnchor else { return }
if let pointOfView = sceneView.pointOfView {
let isVisible = sceneView.isNode(node, insideFrustumOf: pointOfView)
print("Is node visible: \(isVisible)")
}
}
回答1:
It was an ALL day thing but got it working. I only had to switch around 2 lines of code in renderer(:willRenderScene:atTime:)
and run them in the exact order below. I didn't have to remove and add anchors or run any animation code.
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
DispatchQueue.main.async { [weak self] in
guard let safeSelf = self else { return }
guard let pointOfView = safeSelf.sceneView.pointOfView else { return }
guard let child = pointOfView.childNodes.first else { return } // child is the nodeTiedToCamera
if let nodeSetWithARAnchor = safeSelf.nodeSetWithARAnchor {
// *** I just had switch around these 2 lines of code and run them in this exact order ***
nodeSetWithARAnchor.transform = child.worldTransform
nodeSetWithARAnchor.worldPosition = child.worldPosition
}
}
}
来源:https://stackoverflow.com/questions/60327523/swift-how-to-update-aranchor-to-follow-cameras-position