智能移动设备的手势操作是使用者接受并已习惯的操作方式,在移动端 AR 应用中,对虚拟物体的操竹也基本通过手势操作完成,需要注意的是,本次所讲中手势检源是指用户在手机屏幕上的手指操作检测,不是指利用图像技术对使用者手部运动的检测。
手势检测是指通过检测使用者在手机屏幕上的手指触控运动判断其操作意图的技术,如单击、双击、放、滑动等,ARKit 提供了对触控设备底层 API 的访问权限和高级手势检测功能,可以满足不同的手势定制需要,底层 APTI访问能够获取手指单击的原始位置、压力值、速度信息,高级手势检测功能则借助手勢识别器(Gesture Recognizer)识别预设手势(包括单击、双击、长按、滑动、缩放、平移等)。在AR应用中,对虚拟物体最常见的3种操控方式分别为平移、缩放、旋转,为简化手势使用难度,Reality Kit 使用 installGestures()方法对单物体操控提供快捷支持,该方法的原型为CdiscardableResult func installGestures(_ gestures: ARView. EntityGestures = . all, for entity: HasCoLlision) ->[ EntityGestureRecognizer] 其中,参数 gestures 为 ARView.EntityGestures 枚举类型,用于指定可执行的手势操作,entity 指定需要使用手势操作的实体(Entity)。ARView. EntityGestures 枚举包含 all、rotation、scale、translation 4 个枚举值,涵盖了最常见的旋转、缩放、平移操作。使用 installGestures()方法为 entity 添加手势操作时,entity 需要遵循 HasCollision 协议,简单讲就是虚拟物体必须有碰撞器(CollisionShapes),因为本质上手势操作也首先要用射线检测进行碰撞检查,不带碰撞器的虚拟元素无法参与碰撞检测。在代码清单中,我们创建了一个正方体,然后通过程序的方式生成碰撞器,再调用 installGestures()方法,允许用户对该立方体进行操控。
import SwiftUI
import RealityKit
import ARKit
import Combine
struct GestureControlView : View {
var body: some View {
return ARViewContainer6().edgesIgnoringSafeArea(.all)
}
}
struct ARViewContainer6: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
let config = ARWorldTrackingConfiguration()
config.planeDetection = .horizontal
arView.session.run(config, options:[ ])
arView.session.delegate = arView
arView.createPlane1()
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {
}
}
var cubeEntity1 : ModelEntity?
var gestureStartLocation1: SIMD3<Float>?
extension ARView {
func createPlane1(){
let planeAnchor = AnchorEntity(plane:.horizontal)
do {
let cubeMesh = MeshResource.generateBox(size: 0.1)
var cubeMaterial = SimpleMaterial(color:.white,isMetallic: false)
cubeMaterial.color = try SimpleMaterial.BaseColor(tint:UIColor.yellow.withAlphaComponent(0.9999), texture: MaterialParameters.Texture(TextureResource.load(named: "Box_Texture.jpg")))
cubeEntity1 = ModelEntity(mesh:cubeMesh,materials:[cubeMaterial])
cubeEntity1!.generateCollisionShapes(recursive: false)
cubeEntity1?.name = "this is a cube"
planeAnchor.addChild(cubeEntity1!)
self.scene.addAnchor(planeAnchor)
self.installGestures(.all,for:cubeEntity1!).forEach{
$0.addTarget(self, action: #selector(handleModelGesture1))
}
} catch {
print("找不到文件")
}
}
@objc func handleModelGesture1(_ sender: Any) {
switch sender {
case let rotation as EntityRotationGestureRecognizer:
print("Rotation and name :\(rotation.entity!.name)")
rotation.isEnabled = false
case let translation as EntityTranslationGestureRecognizer:
print("translation and name \(translation.entity!.name)")
if translation.state == .ended || translation.state == .cancelled {
gestureStartLocation1 = nil
return
}
guard let gestureCurrentLocation = translation.entity?.transform.translation else { return }
guard let _ = gestureStartLocation1 else {
gestureStartLocation1 = gestureCurrentLocation
return
}
let delta = gestureStartLocation1! - gestureCurrentLocation
let distance = ((delta.x * delta.x) + (delta.y * delta.y) + (delta.z * delta.z)).squareRoot()
print("startLocation:\(String(describing: gestureStartLocation1)),currentLocation:\(gestureCurrentLocation),the distance is \(distance)")
case let scale1 as EntityScaleGestureRecognizer:
if let scale = scale1.entity?.scale{
cubeEntity1?.scale = scale
}
default:
break
}
}
}