Swift: ARKit Сохранить ARPlaneAnchor для следующей сессии
ARKit
Я новичок в Свифте... У меня проблемы...
Я хотел бы сохранить ARPlaneAnchor
обнаружен во время сеанса и перезагрузите их, когда я перезапущу свое приложение. Мой телефон всегда будет в одном и том же месте, и я хотел бы сканировать комнату один раз. И вспоминая Якорь, который я находил в комнате каждый раз, когда запускаю приложение.
Я пробовал несколько решений:
Решение1. Сохраните ARPlaneAnchor, используя: NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)
Я получил эту ошибку:
Завершение работы приложения из-за необработанного исключения "NSInvalidArgumentException", причина: '-[ARPlaneAnchor encodeWithCoder:]: нераспознанный селектор, отправленный экземпляру
Я думаю, что, возможно, я не могу сохранить этот вид данных локально
Решение 2: Храните данные ARPlaneAnchor
затем запустите их при запуске приложения. данные в основном плавающие. Я мог бы создать ARAnchor
легко, я мог бы бросить их как ARPlaneAnchor
, но я не смог изменить параметр "center" и "extension" ARPlaneAnchor
потому что у них есть только геттер, а не сеттер. Поэтому я не могу создавать хорошие якоря.
Я открыт для любого решения. Я думаю, что мне нужно сохранить объект ARAnchor, но пока я не мог найти способ сделать это без сбоев! Так что, если кто-то может мне помочь, я был бы очень благодарен.
2 ответа
Во-первых... если ваше приложение ограничено ситуацией, когда устройство постоянно установлено, и пользователь никогда не сможет переместить или повернуть его, использование ARKit для отображения наложенного контента на канал камеры - это своего рода "убийство комаров пушкой". ситуации. Вы также можете определить во время разработки, какой тип проекции камеры необходим вашему 3D-движку, использовать "тупую" подачу камеры при работающем 3D-движке без необходимости использования iOS 11 или устройства с поддержкой ARKit.
Таким образом, вы можете подумать о своем сценарии использования или своем технологическом стеке, прежде чем переходить к конкретным решениям и обходным путям.
Что касается вашей более конкретной проблемы...
ARPlaneAnchor
полностью предназначен только для чтения, поскольку его сценарий использования предназначен только для чтения. Он существует с единственной целью дать ARKit способ предоставить вам информацию об обнаруженных самолетах. Однако, получив эту информацию, вы можете делать с ней все, что захотите. И оттуда вам не нужно держать ARPlaneAnchor
в уравнении больше.
Возможно, вы запутались из-за типичного варианта использования для определения плоскости (и отображения на основе SceneKit):
- Включить обнаружение самолета
- Ответить на
renderer(_:didAdd:for:)
получитьARPlaneAnchor
объекты - В этом методе верните виртуальный контент, чтобы связать его с якорем плоскости
- Позволять
ARSCNView
автоматически размещать этот контент для вас так, чтобы он соответствовал положению самолета
Если положение вашего самолета статично относительно камеры, вам все это не нужно.
ARKit требуется только для обработки размещения вашего контента на сцене, если это размещение требует постоянного управления, как в случае, когда обнаружение плоскости является активным (ARKit уточняет свои оценки местоположения и протяженности плоскости и соответственно обновляет привязку). Если вы заранее нашли самолет, вы не будете получать обновления, поэтому вам не нужен ARKit для управления обновлениями.
Вместо этого ваши шаги могут выглядеть примерно так:
- Знать, где находится самолет (положение в мировом пространстве).
- Установите положение вашего виртуального контента в положение плоскости.
- Добавьте контент на сцену напрямую.
Другими словами, ваше "Решение 2" - это шаг в правильном направлении, но недостаточно далеко. Вы хотите архивировать не ARPlaneAnchor
сам экземпляр, но содержащаяся в нем информация, а затем при разархивировании не нужно заново создавать ARPlaneAnchor
Например, вам просто нужно использовать эту информацию.
Итак, если это то, что вы делаете, чтобы разместить контент с "живым" обнаружением плоскости:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
let extent = planeAnchor.extent
let center = planeAnchor.center
// planeAnchor.transform not used, because ARSCNView automatically applies it
// to the container node, and we make a child of the container node
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center
node.addChildNode(planeNode)
}
Затем вы можете сделать что-то подобное для статического размещения контента:
struct PlaneInfo { // something to save and restore ARPlaneAnchor data
let transform: float4x4
let center: float3
let extent: float3
}
func makePlane(from planeInfo: PlaneInfo) { // call this when you place content
let extent = planeInfo.extent
let center = float4(planeInfo.center, 1) * planeInfo.transform
// we're positioning content in world space, so center is now
// an offset relative to transform
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center.xyz
view.scene.rootNode.addChildNode(planeNode)
}
// convenience vector-width conversions used above
extension float4 {
init(_ xyz: float3, _ w: Float) {
self.init(xyz.x, xyz.y, xyz.z, 1)
}
var xyz: float3 {
return float3(self.x, self.y, self.z)
}
}
Наряду с описанным выше способом фактического сохранения привязок плоскостей в качестве объектов для последующей перезагрузки, более надежный способ перезагрузить плоскости в правильные позиции при втором запуске приложения - это использовать некую точную систему локализации, которая может устойчиво размещать ваши плоскости в правильных позициях.