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