ARKit中实现SCNNode随相机旋转始终保持屏幕居中的技术求助
Hey there! Let's get your green dot (or ship) staying firmly in the screen center no matter how you rotate the camera. The issue with your current code is that it relies on detecting real-world feature points—if there's no feature point at the screen center, your node won't move, and even when it does, it's tied to physical environment features instead of the camera's line of sight.
The Correct Approach
Instead of using hitTest, we need to calculate the 3D position that corresponds to the screen center along the camera's viewing direction. This position will sit at a fixed distance in front of the camera, so it stays centered on the screen as the camera rotates, regardless of the surrounding environment.
Step-by-Step Solution
Initialize the Node Once
Don't recreate the node every time the session updates—set it up once when your view loads. This is more efficient and avoids unnecessary scene operations.Calculate Screen Center's 3D World Position
UseARSCNView'sunprojectPoint(_:)method to convert the screen center's 2D coordinate into a 3D world position. We'll define a fixed distance (like 2 meters) for the node to sit in front of the camera.Update Position Every Frame
In the session update method, recalculate this position each frame and smoothly move the node to it.
Updated Code
First, set up your node in viewDidLoad() (or your setup method):
override func viewDidLoad() { super.viewDidLoad() // Configure ARSCNView sceneView.delegate = self sceneView.session.run(ARWorldTrackingConfiguration()) // Create your green dot node (replace with your ship model if needed) let dotGeometry = SCNSphere(radius: 0.05) dotGeometry.firstMaterial?.diffuse.contents = UIColor.green let centerNode = SCNNode(geometry: dotGeometry) centerNode.name = "CenterDot" // Add it to the scene (position will be updated dynamically) sceneView.scene.rootNode.addChildNode(centerNode) }
Then, update the session delegate method to lock the node to the screen center:
func session(_ session: ARSession, didUpdate frame: ARFrame) { guard let centerNode = sceneView.scene.rootNode.childNode(withName: "CenterDot", recursively: false) else { return } // Get screen center coordinates let screenCenter = CGPoint(x: sceneView.bounds.midX, y: sceneView.bounds.midY) // Convert screen center to 3D world position (2 meters in front of camera) // z=0 is camera position, z=1 is far plane—adjust z to control distance let targetPosition = sceneView.unprojectPoint(SCNVector3(screenCenter.x, screenCenter.y, 0.5)) // Smoothly move the node to the target position let moveAction = SCNAction.move(to: targetPosition, duration: 0.1) moveAction.timingMode = .easeInEaseOut centerNode.runAction(moveAction) }
Key Notes
- Adjust Distance: Tweak the
zvalue inunprojectPointto move the node closer (e.g., 0.3) or farther (e.g., 3.0) from the camera. - Smooth Movement: The short duration and ease-in/ease-out timing ensure the node moves fluidly instead of jumping.
- Environment Independence: This method works even in feature-poor environments, since it's based purely on the camera's perspective.
If you want to use your ship model instead of a green dot, just replace the sphere geometry initialization with your ship node setup in viewDidLoad().
内容的提问来源于stack exchange,提问作者Viktor Todorov

