Lesson 3A.4 Interacting with Augmented Reality
前回のレッスンでは、ARKitの能力を活かして平面をすばやく簡単に検出できるシーンを構築しました。これらの検出されたサーフェスを使用して、3Dアセットを自動的に拡張された世界に配置します。
しかし、オブジェクトを特定の場所に配置したり、オブジェクトが配置されたらそれらのオブジェクトと対話したい場合はどうなりますか?このレッスンでは、画面上のタップを認識し、現実世界のタップの位置を近似し、既存のアセットと対話できる新しいオブジェクトを作成する方法を学びます。これらの概念を学ぶことの一環として、バスケットボールフープを構築し、ユーザーがフープを撮影できるようにします。
あなたが学ぶこと
ユーザーのタップを現実世界の位置に変換する方法
発見された平面が選択されたときに3Dオブジェクトを配置する方法
拡張現実シーンに物理学を追加する方法
Vocabulary
force
hit test
physics body
physics shape
p.453
プロジェクトのセットアップ
まず、以前のレッスンで使用したARKitテンプレートを使用して、「ARShots」という新しいプロジェクトを作成します。
バスケットボールフープの作成
バスケットボールフープの2つの重要な要素は、バックボードとバスケットの縁です。これらの3Dオブジェクトは、レッスン2で行ったように、SceneKitプリミティブを使用して作成できます。
まず、art.scnassetsにhoop.scnという名前の新しいファイルを作成します。SceneKitエディタでhoop.scnを開き、作成された可能性のあるカメラを削除し、シーンの最上位にあるオブジェクトライブラリから「空のノード」を追加します。ノードインスペクタを使用して、空のノードに「フープ」という名前を付けます。
バックボードを作成するには、フープノードの最初の子として「ボックス」を追加します。属性インスペクタ内のボックスのサイズを変更します。典型的なバックボードは幅約1.8メートル、高さ1.1メートル、厚さ0.1メートルです。
バスケットの縁には、トーラスと呼ばれる3D形状が必要です。トーラスは、リングとリングを囲むパイプの2つの円の積と考えることができます。SceneKitでは、SCNTorusクラスで表されます。
オブジェクトライブラリから、フープノードの2番目の子として「トーラス」を追加します。リング半径を0.45に設定し、パイプ半径を0.1に設定します。次に、リムがバックボードの前にあるが、それでも触れるようにトーラスの位置を調整します。完了すると、フープは次の画像のようになります。
p.484
垂直平面検出
前のレッスンでは、主に水平面を扱いました。予想通り、垂直平面検出も同じように機能します。フープを壁に当てるには、ARKitは垂直平面を検索するために知っておく必要があります。それに応じてARWorldTrackingConfigurationを更新します。
前回のレッスンでは、ARSCNViewDelegateメソッドのいくつかを使用して、発見された平面の上にビジュアルを追加しました。通常、飛行機が発見されたことを示すいくつかの兆候を提供したいと思うでしょう。必要に応じて、これらの方法を追加してください。それ以外の場合、このレッスンでは、シーンで垂直平面を発見し、これらのメソッドの指示が省略されていることを前提としています。
バスケットボールフープの配置
アプリでは、ユーザーは画面をタップしてフープを配置することから始めます。しかし、ユーザーがいつ画面をタップしたかはどうすればわかりますか?このコースの以前の単位に基づいて、UIButtonを使用すると思うかもしれません。結局のところ、SCNView全体に大きくてクリアなボタンを置くことができ、ユーザーが画面を操作しようとするたびにわかります。しかし、それはARプロジェクトにとって最善のアプローチではありません。これが理由です。
まず、Interface Builderでは、UIViewをSCNViewにサブビューとして簡単に追加することはできません。UIButtonをシーンビューの上部に配置する場合は、ビューコントローラのビューが完全に塗りつぶすサブビューとしてSCNViewを持つプレーンなUIViewになるようにスターターテンプレートを変更する必要があります。(これをレッスン1の演習で、シングルビューアプリケーションテンプレートから始めてARKitを追加したことを覚えているかもしれません。)
p.485
第二に、UIButtonはタップされたときに認識できますが、タップされた場所はあまり気にしません。ARシーンでは、フープが配置されるかどうか、どこに配置されるかを判断するには、ユーザーのタップの位置が非常に重要です。UIButtonを使用して画面タップを認識する代わりに、UITapGestureRecognizerメソッドを使用できます。(ジェスチャリコグナイザを確認するには、レッスン2.9を参照してください。)
オブジェクトライブラリから、シーンビューの上に「ジェスチャーリコグナイザをタップ」をドラッグします。次に、アシスタントエディタを開き、Controlキーを押しながらジェスチャリコグナイザからViewControllerクラス定義で利用可能な場所にドラッグします。接続フィールドをアウトレットからアクションに変更し、「screenTapped」という名前を付けます。
ヒットテスト
アプリは、最初に検出された平面上の自動配置に頼るのではなく、ユーザーは簡単な画面タップを使用して、最も近い垂直平面の上にフープを配置できるようにします。これを達成するためには、アプリはヒットテストを実行する必要があります。
画面タップの正確な位置からカメラと同じ角度でビーム(光線のような)を送り出すことを想像してみてください。光線が以前に発見された平面と衝突した場合、ヒットテストには結果が含まれます。
p.486
screenTapped(_:)メソッド内でのヒットテストの実装は次のとおりです。
最初の定数であるtouchLocationは、ユーザーが画面をタップしたx/y位置です。その場所を使用して、hitTest(_:, types:)は、.existingPlaneUsingExtentオプションを使用して指定された既存の平面を検索して虚数線を送信します。そのメソッドの結果、 hitTestResult は、ヒットテストのすべての結果を含む配列、または平面が見つからない場合は空の配列です。複数の結果があるかもしれませんが、通常は1つの結果を発見するだけで済みます。その結果を使用して、シーンにオブジェクトを配置して配置します。
陽性のヒットテスト結果を仮定すると、その場所にフープを追加します。ポジショニングにヒットテスト結果を使用して、シーンにフープを追加するメソッドを作成します。以下は、hoop.scnファイル内のHoopノードを見つけるコードを含む、このメソッドの例です。
p.487
タップ面と垂直面が交差した場所をどのように判断しますか?ARHitTestResultのXcodeドキュメントには、現実世界のx/y/z空間で結果の位置と向きを与えるworldTransformというプロパティが表示されます。worldTransformプロパティは、値の4x4行列です。行列の最初の3列の値は、x軸、y軸、z軸に沿ったオブジェクトの回転を定義します。4番目の列の値は結果の位置を与え、フープを垂直平面に配置するために使用されます。
worldTransform行列の値を使用して、バスケットノードの位置プロパティを設定し、デフォルト(0,0,0)の位置ではなく、ユーザーのタップの位置と一致するようにすることができます。3番目の列の値は照会して、positionプロパティを更新するSCNVector3を初期化するために使用できます。
p.488
アプリをビルドして実行し、検出された垂直平面の上部の画面をタップします。バスケットボールのフープは正しい位置を見つけるはずです。タップがフープを配置できない場合は、レッスン3の平面検出ビジュアライゼーションコードを追加して、垂直面が検出されていることを確認することができます。
バスケットボールの作成とポジショニング
バスケットボールはどうですか?拡張現実のシーンにSCNSphereを追加できます。オレンジ色のボールを作成してシーンに追加する方法は次のとおりです。
フープを撮影するには、ボールをカメラの真正面に置きたいのですが、どのように配置しますか?ボールの位置は既存の平面に対して相対的ではないため、配置にヒットテストを使用することはできません。
p.489
ARKitはカメラから受信した情報を分析すると、セッションのcurrentFrameプロパティに保存されているビデオフレーム画像を生成します。フレームには、画像、タイムスタンプ、camera.transformプロパティを介した原点からカメラの位置の変化など、カメラがキャプチャしたものに関する情報が含まれています。
バスケットボールをカメラと同じ場所に配置するには、トランスフォームプロパティをカメラと同じに設定するだけです。カメラの変換はmatrix_float4x4型で、ボールの変換はSCNMatrix4型であるため、迅速な変換を行う必要があります。
createBasketball()メソッドはいつ呼び出すべきですか?このアプリでは、ユーザーは画面をタップしてフープを配置し、その後のタップごとにバスケットボールが追加されます。画面のタップに反応するジェスチャリコグナイザーをすでに設定しました。シンプルなブール値を使用すると、タップでフープを配置するか、バスケットボールを追加するかを制御できます。
p.490
アプリをビルドして実行し、最初のタップでフープが配置されていることを確認します。その後のタップは、カメラの位置に直接シーンにバスケットボールを配置する必要があります。(ボールはあなたの正確な場所に作成されるので、それを見るために一歩下がる必要があるかもしれません。)
物理学の組み込み
バスケットボールはフープとどのように相互作用しますか?あなたのシーンに物理学を加える時が来ました。
プリミティブシェイプとノードに加えて、SceneKitフレームワークには、ノードの位置と変換プロパティをアニメーション化したり、物理シミュレーションを組み込んだりして、時間の経過とともにオブジェクトの位置を変更するさまざまな方法が含まれています。SceneKitで新しいフレームがレンダリングされるたびに、物理計算が実行され、ノードの位置を更新する方法が決定されます。これらの計算には、重力、摩擦、および他のノードとの衝突が含まれます。
物理ボディ
SceneKitの物理シミュレーションにノードを追加するには、物理ボディをノードに関連付ける必要があります。すべてのSCNNodeは、SCNPhysicsBody型のphysicsBodyプロパティを持つことができます。このプロパティでは、物理シミュレーションでノードが他のノードとどのように相互作用するかについて説明します。
SCNPhysicsBodyの初期化子には、型と形状の2つのパラメータが必要です。型は3つの値のいずれかに設定できます。
p.491
静的 — ノードは力や衝突の影響を受けませんが、それでも物理シミュレーションに参加しています。
動的 - ノードは力や衝突の影響を受ける可能性があります。
Kinematic — ノードは移動できますが、力や衝突の影響を受けません。
バスケットボールはバックボードとリムとの相互作用の影響を受けるため、そのタイプは動的である必要があります。
物理学の形状
物理形状は、物体が他の体との相互作用にどのように反応するかを決定します。異なる形の物体が壁とどのように衝突するか考えてみてください。コーンは、先端またはベースが最初に壁に当たるかどうかによって異なる方法で跳ね返ります。しかし、球体は空間での回転に関係なく同じように反応します。
SCNPhysicsBodyの初期化子の1つは、これを非常に簡単にし、ノード自体を渡して正しい形状を決定することができます。
上記のコードをcreateBasketball()メソッドに追加してから、アプリを実行してください。何が見えますか?iOSデバイスを地面に向けると、バスケットボールが深淵に落ちるのが見えます。physicsBodyには値があるため、SceneKitの重力シミュレーションの影響を受けます。そして、シーンには他のオブジェクトは存在しないので、永遠に落ち続けます。
重力がボールを下に引っ張るのは驚くことではない。でも、どうやってフープを撃てるの?バスケットボールがフープに向かって(カメラと同じ方向に)移動するには、その後ろに何らかのプッシュまたは力が必要です。
カメラの詳細はすでに現在のフレームのcamera.transformプロパティに保存されています。Transformプロパティの値の3番目の列はカメラの向きを示します。1行目(m31)はx値、2行目(m32)はy値、3行目(m33)はz値を表します。これらの値にある程度の「力」を掛けると、ボールは適切な
p.492
方向性。applyForce(_:, asImpulse:) メソッドを使用して、ボールに目的の力を与えます。asImpulseをtrueに設定すると、瞬時に力が加えられます
ボールは意図した方向に前方に発射されますが、バックボードを通り抜けます。理由がわかりますか?少し時間を取って、SceneKitの物理とこれまでに定められたルールについて知っていることを考えてみてください。どのオブジェクトがSceneKitの物理シミュレーションに参加し、その理由は何ですか?
いくつかの考えの後、あなたはまだフープにphysicsBodyを与えていないことに気付くかもしれません。つまり、バックボードとリムは物理シミュレーションに参加し、バスケットボールと対話することはできません。フープは動かないので、物理ボディのタイプは静的に設定し、その形状はノードの形状に等しくする必要があります。
しかし、別の問題があります。バックボードとリムを組み合わせると、不規則な形のボディが得られます。フープの形状に関する高レベルの物理学の詳細を実現するには、フープとボールのSCNPhysicsShapeを初期化するときにいくつかのオプションを追加する必要があります。
フープから始めて、SCNPhysicsShape.ShapeType.concavePolyhedronの値をキーSCNPhysicsShape.Option.typeに設定できます。そうすることで、物理学の体に幾何学の表面に密接に従うように指示しています。これは、ボールが実際の生活と同じように、バックボードとリムとは異なる反応をすることを意味します。
p.493
ボールには、衝突マージンというオプションがあります。名前が示すように、物体が衝突に反応する前に、それ自体と他の物理体との距離を定義します。これを0.01などの非常に小さな数に設定すると、ボールが跳ね返る前にフープまたはリムの1センチ以内である必要があります。
p.494
すべてが整っている状態で、バスケットボールをフープに向かって撃ち、跳ね返り、バックボードとリムと正確に相互作用する機能するアプリが必要です。
運動
床に沿ってゴルフグリーンを作成するアプリを構築します。緑が置かれた後、その後のタップはゴルフボールをグリーンの上に配置し、ボールをカメラと同じ方向に向けます。