我正在试用新的ARKit,以取代我现有的另一个类似的解决方案。这真的很棒!但我似乎想不出如何以编程方式移动ARAnchor。我想慢慢地将锚点移动到用户的左侧。
将锚创建在用户前面2米处:
var translation = matrix_identity_float4x4
translation.columns.3.z = -2.0
let transform = simd_mul(currentFrame.camera.transform, translation)
let anchor = ARAnchor(transform: transform)
sceneView.session.add(anchor: anchor)稍后,将对象移动到用户的左侧/右侧(x轴)...
anchor.transform.columns.3.x = anchor.transform.columns.3.x + 0.1每50毫秒(或其他任何时间)重复一次。
上面的方法不起作用,因为transform是get-only属性。
我需要一种方法来改变AR对象在空间中相对于用户的位置,以保持AR体验的完整性-这意味着,如果你移动你的设备,AR对象将会移动,但也不会像它简单地画在上面那样“粘”在相机上,而是像你看到一个人在你走过的时候移动一样-他们在移动,你也在移动,看起来很自然。
请注意,此问题的范围仅涉及如何相对于用户(ARAnchor)在空间中移动对象,而不涉及平面(ARPlaneAnchor)或另一个检测到的表面(ARHitTestResult)。
谢谢!
发布于 2017-06-07 11:35:02
你不需要移动锚点。(手挥手)这不是你要找的API ...
将ARAnchor对象添加到会话实际上就是“标记”现实世界空间中的一个点,以便您以后可以引用它。点(1,1,1) (例如)始终是点(1,1,1) -您不能将其移动到其他地方,因为它不再是点(1,1,1)。
做个2D类比:锚点是参考点,有点像视图的边界。系统(或您的另一段代码)告诉视图它的边界在哪里,视图根据这些边界绘制其内容。AR中的锚点为您提供了可用于在3D中绘制内容的参考点。
你所问的实际上是关于在两点之间移动(和动画移动)虚拟内容。而且ARKit本身实际上并不是为了显示或动画虚拟内容--外面有很多很棒的图形引擎,所以ARKit不需要重新发明这个轮子。ARKit所做的是为您提供一个真实的参考框架,以便您使用现有的图形技术,如SceneKit或SpriteKit (或统一或虚幻,或使用金属或GL构建的自定义引擎)来显示内容或设置内容的动画。
自从你提到尝试用SpriteKit做这件事..。当心,它会变得很乱。SpriteKit是一个2D引擎,虽然ARSKView提供了一些方法来在其中硬塞第三维,但这些方法有其局限性。
ARSKView会自动更新与ARAnchor关联的每个精灵的xScale、yScale和zRotation,从而提供3D透视的错觉。但这只适用于连接到锚点的节点,并且如上所述,锚点是静态的。
但是,可以将其他节点添加到场景中,并使用这些相同的特性使这些节点与ARSKView-managed节点相匹配。以下是您可以在ARKit/SpriteKit Xcode模板项目中添加/替换的一些代码。我们将从一些基本逻辑开始,在第三次点击时运行反弹动画(在使用前两次点击放置锚点之后)。
var anchors: [ARAnchor] = []
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Start bouncing on touch after placing 2 anchors (don't allow more)
if anchors.count > 1 {
startBouncing(time: 1)
return
}
// Create anchor using the camera's current position
guard let sceneView = self.view as? ARSKView else { return }
if let currentFrame = sceneView.session.currentFrame {
// Create a transform with a translation of 30 cm in front of the camera
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.3
let transform = simd_mul(currentFrame.camera.transform, translation)
// Add a new anchor to the session
let anchor = ARAnchor(transform: transform)
sceneView.session.add(anchor: anchor)
anchors.append(anchor)
}
}然后,一些SpriteKit的乐趣,使动画发生:
var ballNode: SKLabelNode = {
let labelNode = SKLabelNode(text: "")
labelNode.horizontalAlignmentMode = .center
labelNode.verticalAlignmentMode = .center
return labelNode
}()
func startBouncing(time: TimeInterval) {
guard
let sceneView = self.view as? ARSKView,
let first = anchors.first, let start = sceneView.node(for: first),
let last = anchors.last, let end = sceneView.node(for: last)
else { return }
if ballNode.parent == nil {
addChild(ballNode)
}
ballNode.setScale(start.xScale)
ballNode.zRotation = start.zRotation
ballNode.position = start.position
let scale = SKAction.scale(to: end.xScale, duration: time)
let rotate = SKAction.rotate(toAngle: end.zRotation, duration: time)
let move = SKAction.move(to: end.position, duration: time)
let scaleBack = SKAction.scale(to: start.xScale, duration: time)
let rotateBack = SKAction.rotate(toAngle: start.zRotation, duration: time)
let moveBack = SKAction.move(to: start.position, duration: time)
let action = SKAction.repeatForever(.sequence([
.group([scale, rotate, move]),
.group([scaleBack, rotateBack, moveBack])
]))
ballNode.removeAllActions()
ballNode.run(action)
}Here's a video,这样您就可以看到这段代码的实际效果。你会注意到,只有当你不移动相机的时候,这种错觉才能起作用--对AR来说就不是那么好了。当使用SKAction时,我们不能在动画中调整动画的开始/结束状态,因此球在其原始(屏幕空间)位置/旋转/缩放之间来回反弹。
您可以通过直接设置球的动画来做得更好,但这需要大量的工作。您需要在每个帧(或每个view(_:didUpdate:for:)委托回调)上:
didUpdate回调需要执行两次此操作,因为将为每个节点获取一个回调。要在2D图形工具包中硬塞一个假的3D幻觉需要做很多工作--因此我认为SpriteKit不是ARKit的第一步。
如果你想要你的AR覆盖的3D定位和动画,使用3D图形工具包要容易得多。下面是上一个示例的重复,但使用了SceneKit。从ARKit/SceneKit Xcode模板开始,取出宇宙飞船,并将上面相同的touchesBegan函数粘贴到ViewController中。(也将as ARSKView强制转换更改为as ARSCNView。)
然后,一些用于放置2D广告牌精灵的快速代码,通过SceneKit匹配ARKit/SpriteKit模板的行为:
// in global scope
func makeBillboardNode(image: UIImage) -> SCNNode {
let plane = SCNPlane(width: 0.1, height: 0.1)
plane.firstMaterial!.diffuse.contents = image
let node = SCNNode(geometry: plane)
node.constraints = [SCNBillboardConstraint()]
return node
}
// inside ViewController
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// emoji to image based on https://stackoverflow.com/a/41021662/957768
let billboard = makeBillboardNode(image: "⛹️".image())
node.addChildNode(billboard)
}最后,添加弹跳球的动画:
let ballNode = makeBillboardNode(image: "".image())
func startBouncing(time: TimeInterval) {
guard
let sceneView = self.view as? ARSCNView,
let first = anchors.first, let start = sceneView.node(for: first),
let last = anchors.last, let end = sceneView.node(for: last)
else { return }
if ballNode.parent == nil {
sceneView.scene.rootNode.addChildNode(ballNode)
}
let animation = CABasicAnimation(keyPath: #keyPath(SCNNode.transform))
animation.fromValue = start.transform
animation.toValue = end.transform
animation.duration = time
animation.autoreverses = true
animation.repeatCount = .infinity
ballNode.removeAllAnimations()
ballNode.addAnimation(animation, forKey: nil)
}这一次,动画代码比SpriteKit版本短得多。Here's how it looks in action.
因为我们首先在3D中工作,所以我们实际上是在两个3D位置之间进行动画-不像在SpriteKit版本中,动画停留在它应该在的地方。(并且不需要直接插值属性和设置属性动画的额外工作。)
https://stackoverflow.com/questions/44401173
复制相似问题