PlayGround Article 開発, アプリ UnityでARカメラアプリを作る ricchan 2018年10月5日 Created with Sketch. 1 Created with Sketch. 496 tsutsumi ユニプロ # 概要 先日ユニプロ関連でJCのためにUnityを使ったARカメラアプリを作ったので紹介します。 AndroidとiOS両対応なものを作ったのですが主に苦労したのはAndroidの方だったのでそちらについて書いていきます。 ~~正直Androidわかってないのでだいぶゴリ押しです。~~ ※UntiyからAndroidへのビルドの仕方は以下の記事に掲載されていますので割愛します。 https://qiita.com/relzx/items/7f8e7817c9edd11c5023 # 環境 * MacOS High Sierra * Unity 2017.2.1f1 * Vuforia(Unityに内蔵されているもの) * Android 7.0(Nougat) # 作るよ AR部分はVuforiaをぶっこんで表示できれば完成です。ビルドした際にカメラが起動できていれば自動的にARモードになっています。簡単です。 参考URL:http://bibinbaleo.hatenablog.com/entry/2017/10/14/181531 なのでネイティブ側のカメラアプリ制作について記載していきます。 カメラアプリのざっくりと流れとしてはこんな感じ。 1. Android端末内のディレクトリを読み込んで、オリジナル保存フォルダを生成する 2. スクリーンショットを撮る 3. バイナリファイルに書き込む 4. メディアスキャンをする ## Android端末内のディレクトリを読み込んで、オリジナル保存フォルダを生成する ``` void Scan(string fileName) { #if !UNITY_EDITOR && UNITY_ANDROID && !UNITY_IOS using (AndroidJavaClass jcUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject joActivity = jcUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) using (AndroidJavaObject joContext = joActivity.Call<AndroidJavaObject>("getApplicationContext")) using (AndroidJavaObject jcMediaScannerConnection = new AndroidJavaClass("android.media.MediaScannerConnection")) using (AndroidJavaClass jcEnvironment = new AndroidJavaClass("android.os.Environment")) using (AndroidJavaObject joExDir = jcEnvironment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory")) { string imageNameDirectory = joExDir.Call<string>("toString") + "/DCIM/Sample/"; //ディレクトリ内に指定フォルダが存在していなかったら生成する if (!Directory.Exists(imageNameDirectory)) { System.IO.Directory.CreateDirectory(imageNameDirectory); return; } imageName = joExDir.Call<string>("toString") + "/DCIM/Sample/" + fileName; } #endif } ``` 後述するメディアスキャンの際にも似たようなことをしますが、AndroidのギャラリーのディレクトリはiOSのように統一されていないためルートディレクトリを取得するには上記の方法をとって取得します。(もっといい方法がある気がするが) ギャラリーのルートディレクトリを取得したら ``` System.IO.Directory.CreateDirectory(imageNameDirectory) ``` で指定したフォルダを生成します。今回はSampleというフォルダを生成してその中にスクショデータを保存していきます。 ## スクリーンショットをとる ``` public class UnityAndroid : MonoBehaviour { private string originName = ""; private string imageName = ""; ... IEnumerator Captcha() { originName = System.DateTime.Now.ToString ("gyyyyMMddtthhmmssfff") + ".png"; imageName = originName; //Android時パスを追加 #if !UNITY_EDITOR && UNITY_ANDROID && !UNITY_IOS Scan(imageName); #endif yield return new WaitForEndOfFrame(); //スクリーンショットをとる ScreenCapture.CaptureScreenshot(imageName); //↑モバイルではうまく動かないようなので保険でFileStreamで保存する //(スクリーン画面からテクスチャデータへと保存するためのピクセルデータを読み込む) var tex = new Texture2D (Screen.width, Screen.height, TextureFormat.RGB24, false); tex.ReadPixels (new Rect (0, 0, Screen.width, Screen.height), 0, 0); tex.Apply(); ... } ... } ``` ## バイナリファイルに書き込む ``` public class UnityAndroid : MonoBehaviour { ... IEnumerator Captcha() { ... using (FileStream BinaryFile = new FileStream (imageName, FileMode.Create, FileAccess.Write)) { using (BinaryWriter Writer = new BinaryWriter (BinaryFile)) { Writer.Write (tex.EncodeToPNG ()); } } yield return new WaitForEndOfFrame (); //保存まで待機 float latency = 0, latencyLimit=2; while (latency < latencyLimit) { //ファイルが存在していればループ終了 if (System.IO.File.Exists (imageName)) { break; } latency += Time.deltaTime; yield return null; } ... } ... } ``` ## メディアスキャンをする メディアスキャンとはなんぞと思って調べましたが、まぁつまり画像保存をギャラリーに反映させるために実行することだそうです。 ここで最初に生成したスクショ保存用フォルダのディレクトリを取得して保存してギャラリーに反映させます。 ``` void ScanMedia(string fileName) { if (Application.platform != RuntimePlatform.Android) return; //メディアスキャン #if !UNITY_EDITOR && UNITY_ANDROID && !UNITY_IOS using (AndroidJavaClass jcUnityPlayer = new AndroidJavaClass ("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject joActivity = jcUnityPlayer.GetStatic<AndroidJavaObject> ("currentActivity")) using (AndroidJavaObject joContext = joActivity.Call<AndroidJavaObject> ("getApplicationContext")) using (AndroidJavaObject jcMediaScannerConnection = new AndroidJavaClass ("android.media.MediaScannerConnection")) using (AndroidJavaClass jcEnvironment = new AndroidJavaClass ("android.os.Environment")) using (AndroidJavaObject joExDir = jcEnvironment.CallStatic<AndroidJavaObject> ("getExternalStorageDirectory")) { jcMediaScannerConnection.CallStatic ("scanFile", joContext, new string[] { fileName }, new string[] { "image/png" }, null); } Handheld.StopActivityIndicator(); #endif } ``` これをCaptchaメソッドの一番最後で実行してAndroidカメラアプリの巨大メソッドは完成です。 あとはuGUI(ボタンなど)で実行してもらうためのメソッドを以下のような感じで作成します。 ``` public void Cap() { #if UNITY_ANDROID canvasGroup.alpha = 0; StartCoroutine ("Captcha"); #endif } ``` "canvasGroup.alpha"はCanvasGroupコンポーネントをつけてあるCanvasの子オブジェクトのuGUIの透明度を操作することができます。 これを利用して写真をとる瞬間だけUI非表示にしてあります。 # iOSについて iOSは基本的にこちらのサイトのgithubをクローンしてそのまま実行すれば同じようなものができると思います。 https://qiita.com/Ryopon/items/a2cf618eb570637deffb ただ、これを使用するにあたってUnityのPlayerSettings(プラットフォームはiOSの状態で) -> Other Settings -> Configurationの中の"Camera Usage Description"を記入しておかないとカメラの使用許可が出せず永遠に画面が真っ黒状態になりますのでそこだけ注意してください。 # 参考 https://thattown.wixsite.com/thattown/single-post/2017/08/08/cUnity%E3%81%A7%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%82%92%E6%92%AE%E3%81%A3%E3%81%A6%E4%BF%9D%E5%AD%98%E3%81%97%E3%81%A6%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%B9%E3%82%AD%E3%83%B3 http://marunouchi-tech.i-studio.co.jp/1190/ https://blog.narumium.net/2016/12/09/unity%E3%81%A7%E7%94%BB%E5%83%8F%E3%82%92android%E6%9C%AC%E4%BD%93%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B/