锚点的创建、管理与生命周期
什么是锚点
锚点(Anchor)是 ARCore 中把虚拟物体固定到真实世界某个位置的机制。为什么不直接用一个 Pose 存位置?因为 ARCore 的 SLAM 会持续修正设备对自身位置的估计——随着看到更多特征点,它会发现之前算出的位置有误差,然后整体修正整个地图。
如果你只是把虚拟物体放在某个 Pose 上,当 SLAM 修正了设备姿态时,这个虚拟物体就会"漂"。锚点就是为了解决这个问题:
- 锚点创建时关联一个 Pose(可以是 Hit Test 结果、可以是硬编码的坐标)
- SLAM 后续修正姿态时,会保持锚点相对于它参考的 Trackable(平面、特征点等)的相对位置不变
- 虚拟物体的渲染位置 = 锚点 Pose
三种锚点创建方式
1. 基于 Trackable 的锚点(推荐)
从 Hit Test 结果创建锚点,锚点会跟随被 Hit Test 命中的 Trackable(通常是平面或特征点):
// hitResult 是 Hit Test 返回的 Pose
val anchor = session.createAnchor(hitResult)
// 把你的虚拟物体绑定到这个 anchor
virtualObject.createAnchor(anchor)
这种方式的锚点稳定性最高——因为它的位置和一个持续被跟踪的 Trackable 绑定在一起。
2. 世界坐标锚点
直接指定一个绝对世界坐标创建锚点:
val pose = Pose.makeTranslation(0f, -0.3f, -2f) // 相机前方 2 米,下方 30 厘米
val anchor = session.createAnchor(pose)
这种锚点不绑定任何 Trackable,稳定性取决于当前 SLAM 的精度——如果 SLAM 修正了位置,锚点会跟着漂移。适合一次性展示的场景。
3. 相对 Trackable 的锚点
在一个已有的 Trackable(比如检测到的平面)上创建锚点,指定相对于该 Trackable 的偏移:
val plane = session.getAllTrackables<Plane>().first()
val poseOnPlane = plane.centerPose.compose(Pose.makeTranslation(0.1f, 0f, 0.1f))
val anchor = session.createAnchor(poseOnPlane)
锚点稳定性分级
ARCore 的 Anchor 有 TrackingState,反映当前 SLAM 对这个锚点位置的把握程度:
| 状态 | 含义 | 建议 |
|---|---|---|
| TRACKING | 锚点位置稳定可信 | 正常渲染虚拟物体 |
| PAUSED | 跟踪暂时中断(比如用户走出了平面范围) | 可以选择隐藏虚拟物体或淡化它 |
| STOPPED | 锚点已丢失 | 调用 detach() 释放资源,移除虚拟物体 |
生命周期与内存管理
创建与销毁
锚点创建后不会自动释放。Session 中所有锚点持续存在,除非你主动调用 detach()。这意味着:
// 每次 createAnchor 都会增加一个锚点
// 如果用户多次点屏幕放置物体,锚点数量会持续增长
// 用完后必须释放
anchor.detach()
Session 暂停或销毁时,所有锚点会自动 detach。但在长生命周期 Session 中(比如用户在 AR 环境中玩 20 分钟),及时清理不再需要的锚点是好习惯。
锚点池策略
对于需要频繁创建临时锚点的场景(比如让用户拖动虚拟物体预览位置),建议:
// 方案一:用完即弃
private val activeAnchors = mutableListOf<Anchor>()
fun placeObject(pose: Pose) {
activeAnchors.forEach { it.detach() } // 清掉旧的
activeAnchors.clear()
val anchor = session.createAnchor(pose)
activeAnchors.add(anchor)
}
// 方案二:复用同一锚点
private var currentAnchor: Anchor? = null
fun moveObject(newPose: Pose) {
currentAnchor?.detach()
currentAnchor = session.createAnchor(newPose)
}
查询所有锚点
// 获取当前 Session 中所有活动锚点
val allAnchors = session.getAllAnchors()
// 筛选出仍然在跟踪的锚点
val activeAnchors = session.getAllAnchors()
.filter { it.trackingState == TrackingState.TRACKING }
Cloud Anchors(进阶)
普通锚点只在本地设备上有效——换一台设备看不到你放的虚拟物体。Cloud Anchors 解决的就是这个问题,它会把锚点的特征点地图上传到 Google Cloud,其他设备下载地图后就能在同一位置创建同一个锚点。
Cloud Anchors 的 API 使用模式:
// 托管锚点:本地上传
session.hostCloudAnchor(anchor) // 返回 CloudAnchorHostFuture
// 托管完成后获得一个 anchorId(字符串)
// 解析锚点:其他设备用这个 ID 下载
session.resolveCloudAnchor(anchorId) // 返回 CloudAnchorResolveFuture
// 解析完成后得到可以和普通锚点一样用的 Anchor
Cloud Anchors 有配额限制,详见定价授权页面。