[ARKit]オブジェクトにタップ判定を付与する

arkit

XcodeのARKitを使ってARアプリを作成し、そのオブジェクトにタップ判定を付与します。

ARKitでタップした場所にオブジェクトを配置する方法の記事はたくさんヒットしますが、オブジェクトのタップ判定を説明した記事はなかったので備忘録を兼ねてメモ。

ARKitのオブジェクトでタップ判定をする

簡単に説明するために、ベースはARKitのサンプルアプリケーションを使用します。飛行機が表示されるアレですね。

サンプルのコードにはfpsが画面下部に表示されるのですが、これをオブジェクトをタップすると非表示に。オブジェクト以外をタップすると表示するというコードにします。

球形のオブジェクトを作成して、タップすると色が変わるものを作ろうかとも思いましたが、やめました。

サンプルコードを流用して最速でタップ判定を実装します。

サンプルコードを用意する

それではさっそくですが、ARKitの飛行機が表示されるあれを準備しましょう。

Xcodeを起動し、ARアプリを選択します。

Product Nameは適当に。(tapARとでもしましょうか)

Content Technologyの箇所では、「SceneKit」を選択します。

これで飛行機がAR空間上に表示されるアプリケーションが作成されます。

試しに、ビルドして実機確認をしてみます。

[Xcode]実機確認をしている時に「信頼されていないデベロッパです」と言われた時の対処法

Tap Gesture Recognizerを配置

次にUIラベルやUIテキストを配置する感覚で「Tap Gesture Recognizer」をストリーボード上に配置します。

Tap Gesture Recognizerからコード上に繋げます。

ストーリーボード上から操作すると、Scene Viewが選択されるので画像のようにそのままTap Gesture Recognizerから持ってくると良いと思います。

Nameはtapにしました。(何でもいいです。)

今回、タップした際のアクション時に何らかの挙動を実装したいので、ConnectionはActionを選択します。

TypeはUITapGesture~を選択しましょう。

この時点で前半のコードは以下のようになっていると思います。

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    @IBAction func tap(_ sender: UITapGestureRecognizer) {
       //ここにタップ時のアクションを記載していく
    }

オブジェクトをタップした時、オブジェクト外をタップした時の判定

この時点で、何らかの挙動(画面をタップすると音がなるなど)を記載するとタップする度に設定したアクションが実行されます。

しかし、そのままだと画面をタップした場合の挙動であって、画面のどの部分をタップしてもアクションが実行されます。

今回、実装したいのはオブジェクトをタップしたい時に何らかのアクションを実行したいわけですね。

さっそく実装してみます。

// オブジェクトタップ時の挙動
    @IBAction func tap(_ sender: UITapGestureRecognizer) {
        let sceneView = sender.view as! ARSCNView
        let touchLocation = sender.location(in: sceneView)
        let hitResults = sceneView.hitTest(touchLocation, options: [:])
        
        // タップ時にオブジェクトがあれば実行
        if !hitResults.isEmpty {
            // Show statistics such as fps and timing information
            sceneView.showsStatistics = false
        }
        
        // タップ時にオブジェクトがなければ実行
        else {
            sceneView.showsStatistics = true
        }
    }

オブジェクトをタップしたのであれば、fpsの表示を非表示に。それ以外では、表示するように実装しています。

あとは実機確認で実際の挙動を確認してみてください。飛行機オブジェクトをタップした時はfpsが非表示に。それ以外の場所をタップした際はfpsが表示されるようになっています。

最後にコードの全体を掲載します。(viewWillDisappearは削除しています)

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    // オブジェクトタップ時の挙動
    @IBAction func tap(_ sender: UITapGestureRecognizer) {
        let sceneView = sender.view as! ARSCNView
        let touchLocation = sender.location(in: sceneView)
        let hitResults = sceneView.hitTest(touchLocation, options: [:])
        
        // タップ時にオブジェクトがあれば実行
        if !hitResults.isEmpty {
            // Show statistics such as fps and timing information
            sceneView.showsStatistics = false
        }
        
        // タップ時にオブジェクトがなければ実行
        else {
            sceneView.showsStatistics = true
        }
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        
        // Create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // Set the scene to the view
        sceneView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()

        // Run the view's session
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Pause the view's session
        sceneView.session.pause()
    }
}


カテゴリー