TL; DR;
是否可以向连接到ARImageAnchor的一个SCNNode添加SCNAction,从而将该节点移动到连接到另一个ARImageAnchor的另一个SCNNode? strong>我的本科论文涉及重现此video中的行为。我正在使用ARKit,而iOS版本或设备功能并不是很重要。
我要实现的目标是在两个不同的SCNNode足够接近时加入它们,但是它们的位置信息似乎很不规则,似乎与场景中的实际位置不符。
当我打印有关正在处理的两个节点的信息时,lldb
会给我这样的数字:
锚点.tranform.columns.3
:
SIMD4<Float>(0.019269282, -0.2338481, -0.2880894, 1.0)
节点.worldPosition
:
▿ Optional<SCNVector3>
▿ some : SCNVector3
- x : 0.019269282
- y : -0.2338481
- z : -0.2880894
锚点.tranform.columns.3
:
SIMD4<Float>(-7.6405444, -25.205889, -26.780603, 1.0)
节点.worldPosition
:
▿ Optional<SCNVector3>
▿ some : SCNVector3
- x : -7.6405444
- y : -25.205889
- z : -26.780603
我的意思是,为什么两个物体的位置之间有很大差异? (均针对其锚点和节点的位置)。他们的父母是平等的(场景的根节点)并且在同一场景中,难道他们不应该给我每个节点至少相似的位置吗?
在测量两个对象之间的距离时,我注意到以下代码
guard let p1 = a0.atomAnchor?.transform.simd_vector3,
let p2 = a1.atomAnchor?.transform.simd_vector3 else { return }
let d = simd_distance(p1, p2)
print(d)
我会采取不一致的措施,具体取决于相机在打印图像和手机相机之间的距离。
我的ARSessionDelegate执行以下操作:
extension ViewController: ARSessionDelegate {
var minimumDistanceBetweenAtoms: Float {
return 50
}
// MARK:- Delegate method
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else {
print("anchor not found")
return
}
guard let worldScene = sceneView?.scene else { print("Scene not initialized"); return }
guard let atomFound = atoms.first(where: { $0.referenceImage.name == imageAnchor.referenceImage.name }) else {
print("atom not found in resources")
return
}
guard atomFound.type != .referenceObject else { return }
visibleAtoms.append(atomFound)
atomFound.initializeAtom(inScene: worldScene, withAnchor: imageAnchor)
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else {
print("anchor not found")
return
}
visibleAtoms.forEach({
guard !$0.flag else { return }
$0.didUpdateTo(anchor: imageAnchor)
})
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
visibleAtoms.forEach({ [weak self] a0 in
guard !a0.isMoving(), !a0.flag else { return }
self?.visibleAtoms.forEach({ a1 in
guard !a1.isMoving(), !(a1 === a0), !a0.flag else { return }
guard let p1 = a0.atomAnchor?.transform.simd_vector3,
let p2 = a1.atomAnchor?.transform.simd_vector3 else { return }
let d = simd_distance(p1, p2)
print(d)
if d < minimumDistanceBetweenAtoms {
if let molecule = a0.combination(withAtom: a1) {
a0.flag = true
a1.flag = true
}
}
})
})
}
}
考虑将Atom
类用作节点行为的封装器:
func initializeAtom(inScene scene: SCNScene, withAnchor anchor: ARImageAnchor) {
guard let object = type.atomObject() else { return }
let (min, max) = object.boundingBox
let size = SCNVector3Make(max.x - min.x, max.y - min.y, max.z - min.z)
let widthRatio = Float(anchor.referenceImage.physicalSize.width) / size.x
let heightRatio = Float(anchor.referenceImage.physicalSize.height) / size.z
object.transform = SCNMatrix4(anchor.transform)
guard let finalRatio = [widthRatio, heightRatio].min() else { return }
let appearanceAction = SCNAction.scale(to: CGFloat(finalRatio / 2), duration: 0.4)
appearanceAction.timingMode = .easeOut
object.scale = SCNVector3Make(0, 0, 0)
scene.rootNode.addChildNode(object)
object.runAction(appearanceAction)
atomAnchor = anchor
atomScene = scene
atomObject = object
}
func didUpdateTo(anchor: ARImageAnchor) {
guard anchor == atomAnchor else { return }
atomAnchor = anchor
if isMoving() { atomObject?.removeAction(forKey: "movement") }
moveTo(newAnchor: anchor)
}
private func moveTo(newAnchor: ARAnchor, withDurationOf duration: TimeInterval = 0.3) {
let action = SCNAction.move(to: newAnchor.transform.vector3, duration: duration)
atomObject?.runAction(action, forKey: "movement")
}
我的ARWorldTrackingConfiguration只是设置参考图像并使用参数[.resetTracking, .removeExistingAnchors]
运行。
我有一个Utils
文件来放置我需要的所有可用扩展,这是我在这段代码中使用的部分:
extension simd_float4x4 {
var vector3: SCNVector3 {
return SCNVector3(columns.3.x, columns.3.y, columns.3.z)
}
var simd_vector3: simd_float3 {
return simd_float3(columns.3.x, columns.3.y, columns.3.z)
}
}
PS:Github code。