UnityのARCoreを使って長さ計測Androidアプリを作ってみる!
2021.12.20
Unity で ARcorを使い「長さ測定アプリ」を作る!
今回は「AR(拡張現実)」を用いた、「長さ計測 Android アプリ」を開発をしていきます!
開発については、Unity(ユニティ)上で、「ARCore(エーアールコア)」を用います。
※記事を読む上での注意※
- すべてのAndroidでARCoreが使えるわけではありません。ARCore対応端末については以下URLを参照してください。
https://developers.google.com/ar/discover/supported-devices - 今回の記事ではARCoreのインストール方法についてはサポートしていません。
ARcoreが公表している、Quickstart for Andoid
https://developers.google.com/ar/develop/unity/quickstart-android
を参照していただければなと思います。
ARCore(エーアールコア)とは?
「ARCore」は、Google がアンドロイド端末向けに提供している、ARプラットフォームです。
特別なカメラやセンサーを用いずに、既存のスマートフォンに内蔵されているカメラや、モーションセンサーのみでARコンテンツを構築できます。
技術的には、「VIO(visual inertial odmetry)」が用いられています。
ざっくりいうと、「カメラ画像」+「加速度センサ」+「ジャイロセンサ」で、自己位置推定をしています。
ちなみに、iOS 向けの ARプラットフォームには、Apple が開発している ARKit(エーアールキット)があります。
関連記事
ARcore で長さを計測するための全体の流れ
今回は、ARで計測したい個所の、「始点」と「終点」にオブジェクトを生成して、その間の長さを計算したいと思います。
この記事で紹介する内容はコチラ!
1.ゲームオブジェクトの座標を管理する
2.座標をほかのクラスに引用する
3.引用した座標の間の距離を計算して、UIで表示する。
ARCore のサンプルアプリ「HelloAR」をカスタムする
今回は、ARCore 内のサンプルアプリ「HelloAR(ハローエーアール)」をカスタムしていきます。
HelloAR は、水平面を検出して、その水平面上をタップすることで、「ARオブジェクト」を表示することができるアプリです。
早速 Unity 上で、「HelloAR」のシーンを開いてみましょう。
まず、「Unity Project Window」上で
「Assets」>「GoogleARCore」>「Examples」>「HelloAR」>「Scenes」>「HelloAR」
と選択します。
「HelloAR」のアイコンをダブルクリックすると、HelloAR のシーンが開きます。
ARCoreに長さ測定機能を実装
早速、実装していきましょう!
「HelloAR Controller.cs」を元にして作成します。
オブジェクトの座標を管理する
「HelloARController.cs」内で、「座標を格納するリスト」を作成します。
オブジェクトが表示された地点の「ワールド座標」を、リストで管理します。
一旦、「ゲームオブジェクト」をリストに入れて、リストから位置情報を引き出しています。
HelloARController.cs のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | namespace GoogleARCore.Examples.HelloAR { using System.Collections.Generic; using GoogleARCore; using GoogleARCore.Examples.Common; using UnityEngine; using UnityEngine.EventSystems; #if UNITY_EDITOR // Set up touch input propagation while using Instant Preview in the editor. using Input = InstantPreviewInput; #endif /// <summary> /// Controls the HelloAR example. /// </summary> public class HelloARController : MonoBehaviour { /// <summary> /// The first-person camera being used to render the passthrough camera image (i.e. AR /// background). /// </summary> public Camera FirstPersonCamera; /// <summary> /// A prefab to place when a raycast from a user touch hits a vertical plane. /// </summary> public GameObject GameObjectVerticalPlanePrefab; /// <summary> /// A prefab to place when a raycast from a user touch hits a horizontal plane. /// </summary> public GameObject GameObjectHorizontalPlanePrefab; /// <summary> /// A prefab to place when a raycast from a user touch hits a feature point. /// </summary> public GameObject GameObjectPointPrefab; /// <summary> /// The rotation in degrees need to apply to prefab when it is placed. /// </summary> private const float k_PrefabRotation = 180.0f; /// <summary> /// True if the app is in the process of quitting due to an ARCore connection error, /// otherwise false. /// </summary> private bool m_IsQuitting = false; //オブジェクトの座標を入れる箱を作る public Vector3 pos1 = new Vector3(0,0,0); public Vector3 pos2 = new Vector3(0,0,0); //リスト作成 List<GameObject> list_toggle_ = new List<GameObject>(); /// <summary> /// The Unity Awake() method. /// </summary> public void Awake() { // Enable ARCore to target 60fps camera capture frame rate on supported devices. // Note, Application.targetFrameRate is ignored when QualitySettings.vSyncCount != 0. Application.targetFrameRate = 60; } /// <summary> /// The Unity Update() method. /// </summary> public void Update() { _UpdateApplicationLifecycle(); // If the player has not touched the screen, we are done with this update. Touch touch; if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began) { return; } // Should not handle input if the player is pointing on UI. if (EventSystem.current.IsPointerOverGameObject(touch.fingerId)) { return; } // Raycast against the location the player touched to search for planes. TrackableHit hit; TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon | TrackableHitFlags.FeaturePointWithSurfaceNormal; if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit)) { // Use hit pose and camera pose to check if hittest is from the // back of the plane, if it is, no need to create the anchor. if ((hit.Trackable is DetectedPlane) && Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position, hit.Pose.rotation * Vector3.up) < 0) { Debug.Log("Hit at back of the current DetectedPlane"); } else { // Choose the prefab based on the Trackable that got hit. GameObject prefab; if (hit.Trackable is FeaturePoint) { prefab = GameObjectPointPrefab; } else if (hit.Trackable is DetectedPlane) { DetectedPlane detectedPlane = hit.Trackable as DetectedPlane; if (detectedPlane.PlaneType == DetectedPlaneType.Vertical) { prefab = GameObjectVerticalPlanePrefab; } else { prefab = GameObjectHorizontalPlanePrefab; } } else { prefab = GameObjectHorizontalPlanePrefab; } // Instantiate prefab at the hit pose. var gameObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation); //listにオブジェクトを追加 list_toggle_.Add(gameObject); // Compensate for the hitPose rotation facing away from the raycast (i.e. // camera). gameObject.transform.Rotate(0, k_PrefabRotation, 0, Space.Self); // Create an anchor to allow ARCore to track the hitpoint as understanding of // the physical world evolves. var anchor = hit.Trackable.CreateAnchor(hit.Pose); // Make game object a child of the anchor. gameObject.transform.parent = anchor.transform; } } //オブジェクトの座標 pos1 = list_toggle_[0].transform.position; pos2 = list_toggle_[1].transform.position; } /// <summary> /// Check and update the application lifecycle. /// </summary> /// private voidからpublic voidに変更 public void _UpdateApplicationLifecycle() { // Exit the app when the 'back' button is pressed. if (Input.GetKey(KeyCode.Escape)) { Application.Quit(); } // Only allow the screen to sleep when not tracking. if (Session.Status != SessionStatus.Tracking) { Screen.sleepTimeout = SleepTimeout.SystemSetting; } else { Screen.sleepTimeout = SleepTimeout.NeverSleep; } if (m_IsQuitting) { return; } // Quit if ARCore was unable to connect and give Unity some time for the toast to // appear. if (Session.Status == SessionStatus.ErrorPermissionNotGranted) { _ShowAndroidToastMessage("Camera permission is needed to run this application."); m_IsQuitting = true; Invoke("_DoQuit", 0.5f); } else if (Session.Status.IsError()) { _ShowAndroidToastMessage( "ARCore encountered a problem connecting. Please start the app again."); m_IsQuitting = true; Invoke("_DoQuit", 0.5f); } } /// <summary> /// Actually quit the application. /// </summary> private void _DoQuit() { Application.Quit(); } /// <summary> /// Show an Android toast message. /// </summary> /// <param name="message">Message string to show in the toast.</param> private void _ShowAndroidToastMessage(string message) { AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); if (unityActivity != null) { AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast"); unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() => { AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>( "makeText", unityActivity, message, 0); toastObject.Call("show"); })); } } } } |
UIを設定
「2地点座標」から、オブジェクト間の長さを計算します。
位置情報「pos1 , pos2」を HelloARController.cs から引用するため、「名前空間」を取得したり、HelloARcontroller を「script変数」に入れたりしています。
まず、スクリプトを作りましょう。
「Project」ウィンドウ上で、「create」>「C# Script」でスクリプトを作成。
そして、この名前を「UIcontroller」に変更します。
UIcontroller.cs コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //UI使用する using GoogleARCore.Examples.HelloAR; //名前空間を取得する public class UITest : MonoBehaviour { GameObject AR; //HelloARcontrollerを入れる変数 GameObject distance; //UIを入れる HelloARController script; //HelloARControllerが入る変数 // Start is called before the first frame update void Start() { this.distance = GameObject.Find("Distance"); this.AR = GameObject.Find("HelloAR Controller"); script = AR.GetComponent<HelloARController>(); //GameObjectのHelloAR Controllerの中にあるHelloARController.csを取得して変数に格納 } // Update is called once per frame void Update() { //HelloARController.csから座標を取得 Vector3 p1 = script.pos1; Vector3 p2 = script.pos2; //長さを計算 float length = (p1 - p2).magnitude; //UIのdistanceを変更 this.distance.GetComponent<Text>().text = (length * 100f).ToString("F2") + "cm"; } } |
GameObject を作成
次に、「UIcontroller」を動かす「GameObject」を作成します。
「Hierarcy」ウィンドウから、「Create」>「Creat Empty」で作成します。
「GameObject」の名前を、「UIcontroller」に変更して、「UIcontroller」のスクリプトをアタッチします。
最後に Distance
「Hierarcy」ウィンドウから、「Create」>「UI」>「Text」と選択。
名前を「Distance」に変更します。
下の写真の、赤枠部分のようになっていれば問題ありません。
クオリティアップ
上の実装で、長さは計測できるようになりましたが、少し体裁を整えたいと思います!
既存の「prefab」だとビジュアル的に良くないので、「footprint」に変更したいと思います。
こちら「Project」ウィンドウから、「Favorites」>「All Models」>「footprint」で見つけられます。
また、「footprint」はそのままだと大きいので、スケールを「0.1」に変更します。
ARcoreで完成した「長さ測定アプリ」を使ってみる
さっそく計測してみましょう!
試しに「1万円札」を計測してみたいと思います。
画像の上部に、赤字で「15.74cm」と表示されました!
1万円札の一辺は 16cm なので、「誤差2.6mm」での計測となりました。
大体、「誤差1~3mm」ぐらいの精度のようです。
この精度は、「対象のテクスチャ」に依るところが大きいようです。
※例えば、金属は光を反射し計測しづらいなど。
さいごに
今回は、簡易的にできる「長さ計測アプリ」を作成してみました。
今後は、「特徴点」から計算される「記述子」を用いた対応付けを変えたりしていこうと思っています。
また、「物体検出」や AR の技術的な所もまとめていけたらなと思います。
お楽しみに!
こちらの記事もオススメ!
書いた人はこんな人
- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
- ライトコードの日常2月 29, 2024座談会はじめました!ライトコードの話ちょっと聞いてみませんか?
- ライトコードの日常12月 27, 2023年忘れ!ライトコード大忘年会2023
- ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
- ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?