久し振りのARネタです。簡単かつ応用が効く方法なのでまとめておく。
はじめに
iOS11.3と同時に提供されるARKit1.5で垂直面の検知ができるようになるそうです。
それなら、"以前にTangoで作ったARモグラ叩きが作れるのでは?"と思い、現時点で実装可能なARKit版(水平面のみ)を実装してみました。
ARモグラ叩き。Tango版だと壁や天井が使えるけど、ARKitでどこまでできるかは今後試す予定。#クエリちゃん #Tango pic.twitter.com/idbMlXBfmL
— jyuko (@jyuko49) 2017年9月20日
上記のTango版は透明なメッシュをリアルタイムで作って、メッシュの下からモグラを表示させています。
同じことをARKitやARCoreでやろうとしてもTangoのようにメッシュを作成するのは容易ではないので、透明な箱に隠しておく実装で代用しました。
開発環境
以下の環境で作りました。ARKit1.5にはまだ対応していません。
- macOS High Sierra
- Unity 2017.3.0f2
- Unity ARKit Plugin version 1.0.14
モグラ役には、こちらも久し振りのクエリちゃんSD版モデルを使います。
クエリちゃんアセットは"Creative Commons Attribution 4.0 international License(CC-BY)"で提供されています。
作り方
Unity ARKit Pluginで基本的なシーンを構成する方法は割愛します。
以下の記事などを参考にしてください。
モグラ叩きの処理は、
- ランダムにキャラクターを出現させる
- 平面から出てくるように見せる
の順で実装しています。
ランダムにキャラクターを出現させる
Unity ARKit Pluginに同梱されているExamplesのUnityARBallzで使われているBallMaker.csが参考になります。
上記のスクリプトは、"画面をタップして平面にGameObjectを置く処理"なので、これを改変してランダムにモグラを配置していきます。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.iOS; public class GameController : MonoBehaviour { public GameObject moguraPrefab; private float timeInterval = 2.0f; // モグラの出現間隔(秒) private float timeElapsed; // 経過時間を保存する変数 void Update () { // 一定時間が経過したらHitTestを実行する timeElapsed += Time.deltaTime; if(timeElapsed >= timeInterval) { _executeHitTest (); timeElapsed = 0.0f; } } private void _executeHitTest() { // 画面上のランダムな座標を元にARPointをセット var screenPosition = Camera.main.ScreenToViewportPoint(new Vector3 (Screen.width * Random.value, Screen.height * Random.value)); ARPoint point = new ARPoint { x = screenPosition.x, y = screenPosition.y }; // HitTestを実行する List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent); // 平面と衝突した位置にモグラを出現させる if (hitResults.Count > 0) { foreach (var hitResult in hitResults) { Vector3 position = UnityARMatrixOps.GetPosition (hitResult.worldTransform); _createMogura(position); break; } } } // モグラの生成 private void _createMogura(Vector3 position) { GameObject mogura = Instantiate (moguraPrefab, position, Quaternion.identity); } }
Update()
でタイマー処理を実装し、上記の例では2秒おきにHitTestを実行しています。
_executeHitTest()
はExampleのHitTest処理とほぼ同じですが、HitTestを行う座標のscreenPosition
を作成する処理が異なります。
Random.value
は0.0〜1.0の乱数を返すため、画面の幅・高さに掛け合わせることで画面上の任意の座標をランダムで取得することができます。
このスクリプトを任意のGameObjectにセットして、moguraPrefabを指定すると、キャラクターが平面上にランダムで出現するはずです。
ARKitが垂直面も取れるようになると聞いて、Tangoで作ったARモグラ叩きを移植中。
— jyuko (@jyuko49) 2018年2月7日
ニョキニョキさせる実装はこれから。 pic.twitter.com/co2JeOmrax
平面から出てくるように見せる
せっかくARなので、平面からニョキニョキと現れるようにしたいですよね。
これを実現するため、GameObjectを置いた時点ではキャラクターを透明な箱に隠しておき、キャラクターだけを箱の外に移動させるスクリプトを書きます。
まず、親のGameObjectを作成し、Cubeとキャラクターを配置します。
次に、以下の位置合わせを行います。
- 親のGameObjectの位置を(0, 0, 0)で固定
- キャラクターが収まるようCubeのサイズを調整
- Cubeの上面がy=0になるようy座標を調整
位置合わせ後はこんな感じになります。調整の際は、CubeのMeshRendererをON/OFFすると確認しやすいです。
位置が決まったらCubeのMeshRendererにオクルージョン用のマテリアルをセットします。
"Create" > "Material"で新規マテリアルを作成して、Shaderに"VR/SpatialMapping/Occlusion"を選択します。
エディタ上では単に透明な箱ですが、実機ではARカメラ の映像が表示されるため、中身はそこにあるけれど見えない状態になります。
最後にキャラクターだけが箱の外に出てくるスクリプトを追加します。親のGameObjectではなく、箱の中のキャラクターに"Add Component"で適用されるようにします。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MoguraController : MonoBehaviour { private float moveDistance = 0.5f; // 移動させる距離 private float movedDistance = 0f; // 移動した距離 private float moveSpeed = 0.02f; // 移動させる速さ void Update () { if (movedDistance < moveDistance) { this.transform.position += this.transform.up * moveSpeed; movedDistance += moveSpeed; } } }
スクリプトを保存したら、親のGameObjectごとPrefab化して、最初に作成した"モグラをランダムに出現させるスクリプト"にmoguraPrefabとしてセットします。
ビルドして実行すると、今度は平面からニョキニョキと生えてくるはずです。
ニョキニョキの実装できました。仕組み自体はすごくシンプル。#クエリちゃん #ARKit pic.twitter.com/jYgRH9EBL3
— jyuko (@jyuko49) 2018年2月9日
冒頭のTango版では、これに"一定時間でモグラが消える処理"、"画面タップでボールを投げる処理"、"ボール衝突時のアニメーション&パーティクル"、"スコア計算"などを追加して、ミニゲームっぽくしています。
応用編
今回使ったロジックを少し改変すると、色々なシーンで使えます。
平面に消えていく表現
今回作ったモグラの初期状態と終了状態を入れ替えるだけです。
最初の位置合わせをキャラクターが箱の上に乗っている状態にして、箱の中に隠れていくスクリプト(移動方向をマイナスにするだけ)を書けばOKです。
水面に見立てて沈んでいく表現や忍者(床や天井に消えるイメージ)っぽい表現で使えると思います。
徐々に出てくる表現
キャラクターを箱から出すのではなく、箱の方をスクリプトで上下に移動させることで、キャラクターが徐々に転送されてくるような表現になります。GANTZみたいなやつです。
ARでキャラクターが徐々に出てくるやつもモグラと同じ仕組みだった。#クエリちゃん pic.twitter.com/TeaJQEVz0l
— jyuko (@jyuko49) 2018年2月12日
ARはてなボックス
「?」のテクスチャを貼って、宙に浮かせているだけです。箱を出た後に、スクリプトで移動の方向を変えてそれっぽく見せています。
キノコ(の妖精)のMMDが使いたかったため、UnityではなくWebAR(three.ar.js)で実装しました。
モグラの応用でARはてなボックス。キノコは本物だとアレなので妖精さん。#three.ar.js #コロコロ #gdgd妖精s pic.twitter.com/Z3A3u2bShy
— jyuko (@jyuko49) 2018年2月9日
まとめ
一見難しいことをやっているようで、方法を思い付いてからは簡単でした。平面検知さえできれば実装でき、プラットフォームを問わないので使い勝手がよいです。
平面の上を動かすだけでなく、平面から出たり消えたりさせることで、ARKitで手軽に現実感を増すことができます。
垂直面を扱う場合、Tango版のように配置する平面の応じてモグラの向きを変える必要があります。これはHitTestの際に衝突した平面の法線(normal)が取得できれば実現可能です。
このあたりはUnity ARKit Pluginが垂直面に対応してから実装します。