はじめに
リコーのYuukiSです。
弊社ではRICOH THETAという全周囲360度撮れるカメラを出しています。
RICOH THETA V, RICOH THETA Z1, RICOH THETA Xは、OSにAndroidを採用しています。Androidアプリを作る感覚でTHETAをカスタマイズすることもでき、そのカスタマイズ機能を「プラグイン」と呼んでいます(詳細は本記事の末尾を参照)。
ついにTHETAに液晶ディスプレイが!
2022年7月22日に発売されたTHETA Xには、タッチ可能なディスプレイが搭載されています。
これにより、これまでのTHETAでは出来なかったカメラ単体でのライブビューやプレビューが可能になり、撮影体験が向上しています。
さらに、従来のTHETA VやZ1と同様にAndroidを搭載していますので、Androidアプリも画面に表示し操作する事が可能です。
せっかくAndroidが動いてタッチパネルも搭載されたTHETA X。
どうせならリッチな3D表現もしてみたい、と思い立ってUnityアプリを動かす試行錯誤をしてみました。
その結果が、この通り。
無事Unityで作ったアプリケーションが動作しました。
ただ、なかなか工夫したポイントもいくつかあり、今回はそのポイントを紹介します。
※Unityの表示確認にSDユニティちゃんを利用させて頂きました。(© Unity Technologies Japan/UCL)
Unity as a Libraryを使う
今回、まず最初にUnity単体でビルドしたアプリをインストールし動かしてみました。
その結果、アプリの動作は問題ないもののTHETA固有のハードウェアボタンの押し込みを検出できないことが判明しました。
(Input.GetKeyDown等で色々試したのですが駄目だったので、もし動作した方がいましたらコメント下さい)
THETA Xの標準のプラグインは、Modeボタンの長押しで終了する仕様になっているので、このボタンが検出できないとアプリ終了動作を共通に出来ません。
なので、今回は描画部分にのみUnityを利用するUnity as a Libraryという枠組みを利用することにしました。
Unity as a Libraryとは
Unityの機能をフレームワークとしてネイティブアプリから読み出して利用できる機能です。
→公式ページリンク
これを利用すると例えばUI部分はユーザが慣れ親しんだiOSやAndroidのネイティブ表示を利用しつつ、複雑な3D描画はUnityを利用するといったことが可能になります。
今回は下図の様にAndroidのネイティブアプリを新規で構成し、THETAの一般機能の制御にはTHETA Plug-in Library、画面描画はUnity as a Libraryを用いる構成にしました。
これにより、Modeボタン等の振る舞いはネイティブアプリのTHETA Plug-in Libraryで管理することが出来るので、前述したアプリ終了動作の問題が解消できます。
Unity as a Libraryの利用準備
Unity as a Libraryでは、Unity側のシーンはUnityで構築し、それをエクスポートしてネイティブアプリの開発環境(Android Studio)で取り込んでビルドします。
今回、この一連の流れは@cha84rakanalさんの以下の記事を大変参考にさせて頂きました。
AndroidアプリにUnity as a Libraryでリッチな3Dを簡単に組み込む
ほぼ上記の記事を踏襲すれば準備は完了ですが、自分の環境では下記エラーが出てAndroid Studioでビルドが通りませんでした。
Could not get unknown property 'unityStreamingAssets' for object of type com.android.build.gradle.internal.dsl.AaptOptions
調べると、どうやらUnity2020以降で発生するエラーのようでgradle.propertiesファイルに以下の1行を追加することで解消されました。
unityStreamingAssets=.unity3d
THETA Plug-in Libraryの導入
THETAの各機能を便利に利用するため、THETA Plug-in Libraryを導入します。
導入の方法は、GitHubのHow to Useに従えば大丈夫です。
MainActivityの実装
以上でUnity側のシーンの利用とTHETA側のプラグイン利用の準備が整ったので、MainActivityを実装していきます。
と言っても大部分は下記の記事を踏襲します。
AndroidアプリにUnity as a Libraryでリッチな3Dを簡単に組み込む
THETA向けの変更点としては、継承するクラスをPluginActivityに変更し、下記のようなTHETA特有の制御を入れました。
-
onResume()で notificationWebApiCameraOpen()を呼び出し
今回、Unity側からのTHETA制御にはTHETA Web APIを利用しました。
”RICOH THETA X におけるプラグイン開発”の記事にある通り、THETA XではプラグインからのWeb API利用/終了には手続きが追加されました。
よって、onResume時にこの手続きを行い、Unity側からWeb APIを利用できるようにしています。 -
onPause()で、notificationWebApiCameraClose()を呼び出し
同様に終了時の手続きも行います。 -
onDestroy() でunityPlayer?.quit()を呼び出し
Modeボタンが長押しされた場合呼ばれるonDestroy()内で、Unity側の処理も終了する様にしています。
以上でMainActivityの実装も完了です。
(参考までにMainActivity全体のコードも折り畳んで記載しておきます)
package com.yuukis.uaal4thetax
import android.os.Bundle
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import com.theta360.pluginlibrary.activity.PluginActivity
import com.unity3d.player.UnityPlayer
class MainActivity : PluginActivity() {
var unityPlayer: UnityPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
unityPlayer = UnityPlayer(this)
window.clearFlags(1024)
findViewById<ConstraintLayout>(R.id.unity)?.addView(
unityPlayer, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
unityPlayer?.requestFocus()
}
// Notify Unity of the focus change.
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
unityPlayer?.windowFocusChanged(hasFocus)
}
override fun onResume() {
super.onResume()
unityPlayer?.resume()
notificationWebApiCameraOpen()
}
override fun onPause() {
super.onPause()
unityPlayer?.pause()
notificationWebApiCameraClose()
}
override fun onDestroy() {
super.onDestroy()
unityPlayer?.quit()
}
}
Unity側からTHETAの制御をおこなう
ここまでで、ネイティブアプリ側からTHETA Plug-in Libraryを利用しつつUnityのシーンが描画出来るようになりました。
ただ、これだけでは単にディスプレイ上に表示が出るのみなので、画面のボタンを押したらTHETAの撮影が出来るようにしてみましょう。
これには、ネイティブアプリ側の画面にボタンを追加しPlug-in Libraryから撮影をする方法と、Unityシーン側にボタンを追加しWeb APIから撮影する2通りの方法が存在します。
今回はUnity側でUIを完結したかったため、後者を選択しました。
(ちなみにUnity as a LibraryではネイティブとUnity間で通信する仕組みもあるので、これを利用してUnityシーン側のボタンが押されたことをネイティブアプリ側に通知し、ネイティブアプリ側でPlug-in Libraryから撮影をすることも可能だと思います。)
UnityからWeb APIを利用する例は、過去の記事"UnityちゃんとTHETAでMixedRealityな記念撮影を"で紹介しています。
具体的なコードは以下の通りです。
IEnumerator sendTakePictureSignal()
{
string url = "http://127.0.0.1:8080/osc/commands/execute";
takepicObject.name = "camera.takePicture";
settedJson = JsonUtility.ToJson(takepicObject);
byte[] postData_takePic = System.Text.Encoding.UTF8.GetBytes(settedJson);
var request_takePic = new UnityWebRequest(url, "POST");
request_takePic.uploadHandler = (UploadHandler)new UploadHandlerRaw(postData_takePic);
request_takePic.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request_takePic.SetRequestHeader("Content-Type", "application/json");
yield return request_takePic.SendWebRequest();
if (request_takePic.isNetworkError || request_takePic.isHttpError)
{
Debug.Log(request_takePic.error);
Debug.Log(request_takePic.downloadHandler.text);
}
else
{
Debug.Log(request_takePic.downloadHandler.text);
}
}
この関数を画面に配置したボタンのonClickに割り当てて、完成です。
GIFでは分かりにくいですが、画面のボタンを押すとシャッターが切れています。
(せっかくなので、同時にユニティちゃんにジャンプしてもらいました)
まとめ
今回は、THETA XでUnityを活用する試みを紹介しました。
THETAで撮影できる全天球画像と3Dコンテンツは相性が良いので、色々出来ることもありそうです。
皆さんもぜひ、遊んで試してみてください。
RICOH THETAプラグインパートナープログラムについて
THETAプラグインをご存じない方はこちらをご覧ください。
パートナープログラムへの登録方法はこちらにもまとめてあります。
QiitaのRICOH THETAプラグイン開発者コミュニティ TOPページ「About」に便利な記事リンク集もあります。
興味を持たれた方はTwitterのフォローとTHETAプラグイン開発コミュニティ(Slack)への参加もよろしくおねがいします。