[Swift][ARKit] GestureRecognizer で Nodeを前後左右に動かす

2019年4月1日ITARKit, Swift

例として、UILongPressGestureRecognizer で Nodeを前後左右に動かします。

例:

private var localTranslatePosition :CGPoint!

@objc func longPressed(recognizer: UILongPressGestureRecognizer) {
    guard let longPressedView = recognizer.view as? ARSCNView else { return }
    let touch = recognizer.location(in: longPressedView)
    self.sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
        if node.name == "robo" {
            switch recognizer.state {
            case .began:
                localTranslatePosition = touch
            case .changed:
                let deltaX = Float(touch.x - self.localTranslatePosition.x)/700
                let deltaY = Float(touch.y - self.localTranslatePosition.y)/700
                node.localTranslate(by: SCNVector3(deltaX,0.0,deltaY))
                self.localTranslatePosition = touch
            default:
                break
            }
        }
    }
}

まず、LongPress している View が ARSCNView(ARのSceneView) かどうかチェックします。

guard let longPressedView = recognizer.view as? ARSCNView else { return }

ARSCNViewだったら、そのARSCNView内でタップしているx,y座標を取ります。

let touch = recognizer.location(in: longPressedView)

ここでtouch は CGPointなので、(x, y)で返って来ます。

次に、enumerateChildNodes メソッドを使って、全ノードを調べて、名前が “robo” のノードに対して、移動の処理を行います。

self.sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
    if node.name == "robo" {
        // ここでノードの移動の処理
    }
}

今回は GestureRecognizer の状態が .began(開始した)、.changed(変更があった)の場合に処理を行います。

localTranslatePosition という変数をCGPoint型で持って、タッチが開始されたときにそのx, y座標を代入して、

その後変更がある度に(ドラッグしている時に)、変数 localTranslatePosition を更新します。

LongPress を開始した時の touch の値はそのままで、変数 localTranslatePosition はドラッグされる度に更新されます。

その差分でノードの位置を更新し、動いているように見せます。

node.localTranslate(by: SCNVector3(deltaX,0.0,deltaY))

Y座標の移動距離をSCNVector3のZ座標に渡しています。

「え、なんで?」と思った方は下記リンクを参考にしてください。ARKitの座標系について詳しく解説されてます。

参考:
ARKitのための3D数学 – ARKitで使う座標系

長押し開始と長押し中の差分を700で割っていますが、このくらいの数値がドラッグした距離とノードが動く距離が同じくらいでした。

この数値が大きいとドラッグした距離の割にはあまり動かなくなり、数値が小さいとドラッグした距離の割にノードの移動距離が大きくなります。

switch recognizer.state {
case .began:
    localTranslatePosition = touch
case .changed:
    let deltaX = Float(touch.x - self.localTranslatePosition.x)/700
    let deltaY = Float(touch.y - self.localTranslatePosition.y)/700
    node.localTranslate(by: SCNVector3(deltaX,0.0,deltaY))
    self.localTranslatePosition = touch
default:
    break
}

以上になります。

サンプルプロジェクト:
https://github.com/nobuhiroharada/ARKit-beginner/tree/master/ARKit-beginner/3-Gesture

Nodeを前後左右に動かす処理を記述している箇所:
https://github.com/nobuhiroharada/ARKit-beginner/blob/master/ARKit-beginner/3-Gesture/GestureViewController.swift#L128

スポンサーリンク

Posted by nobuhiro harada