MagicLeapプロンプターアプリ
発表会とかでカンニングペーパー的な感じに使いたいアプリ
あがり症な人は人前で喋るのが苦手、視界を覆うMagicLeapならいい感じに緊張緩和できるのでは?
実際に登壇する時のスライドと登壇者が喋る言葉はイコールではない、緊張すると話したかった内容が頭から抜けスライドの朗読になって薄ペラな発表になるためMagicLeap側でプロンプターのように話す内容( セリフ? )を表示して登壇者はそれを読む
これ良さそう、これ + Photonなりで通信してやれば上手く行くかも?
仕事終わったらまずはMagicLeap <-> UnityEditorでの通信ができるところまでを確認する
使うやつはとりあえずPhotonでいいかな Photon Boltがお手軽そう
とりあえず同一プロジェクトでWindows用とMagicLeap用ビルドすればいいか、PhotonBoltだとBoltAssetを共有することで別々のプロジェクトでも管理できるけど結構面倒くさかった
ひとまずWindows同士の通信でイベントベースで通信できることを確認した
同一プロジェクトでいじると不具合が起こりそうなのでリポジトリを同一にして一つ下の階層にWindows用のUnityプロジェクトとMagicLeap用のプロジェクトを配置
BoltAssetについてはWindows側で編集して其れをMagicLeap側に適用する形にする、ネットワーク周りのコードも全部Windows側で編集しMagicLeap側はそれを利用する形にする
なんかわからんけどうまくいってない、コード周り整理するか
セッションが決定したのちに遷移するシーンは同一でなければならないなんてことはなかったはずだからこの作り方で一応できるはできると思うんだけどなぁ
Windows_Start : Serverの起動、Server起動後ユーザーがセッションに入ってきたらシーン遷移 -> Windows_Main
MagicLeap_Start : Clientの起動、Serverが開いたセッションを見つけ次第そこに入る -> MagicLeap_Main
こんな感じの作り
遷移先のシーンは同じじゃないとダメだったわ
じゃあ遷移先でシーンの構成を動的に変更すればいいか
#PLATFORM_STANDALONE_WIN
#PLATFORM_LUMIN
これで切り分け
入力周りはジェスチャでいいかも
アプリ閉じる時とスライド戻す時はコントローラーで?
まぁ最初はコントローラ入力でいいか
PhotonのMagicLeapに関するテキスト、雑過ぎんか....
↑の記事をもとに透過ウィンドウを作成
結構いい感じ
マウスのイベント発火にはこれが使えそう..?
Windows側は一段落ついた、これからML側を進める
欲しい機能としてはこんな感じかな
- スピーカーノート
- 経過時間表示
一通りのスピーカーノート機能は実装出来たように思う
note.txtからテキストをロードし表示 ---
この文字列でページの区切り
タイマーのスタートとリセット機能
なんか知らんけど.txtファイルに記述されてる日本語が表示されない...
MagicLeap側の問題なのか.txtファイル側の問題なのか切り分けないとな
とりまuGUIに直接日本語打ち込んで表示されるかどうか見てみるか
日本語フォントに利用する文字はここから引用
TextMeshProで日本語を利用するQiita記事
フォントファイルはここからDL
一応uGUIに日本語フォントを適用して試してみる ( 多分うまくいくだろうけど
一段落ついたかな
PC側の情報をMagicleapに送信したい
今のところリアルタイムである必要はないのでMagicleapからアクション → PCがリアクションとしてスクショを返す的な形がいいかも
そりゃアプリ自体のスクリーンショットだからこうなるか...
凹みTipsを頼りにしてみる
PlayModeに入ったとたんすぐ落ちると思ったらPlayerSettingsのGraphicsAPIの設定がOpenGLだった、ZeroIterationを利用するときはOpenGLにする必要があるのでいちいち切り替えが面倒くさそう( 後でエディタ拡張で自動化したい )
Direct3D11にしたらちゃんとuDesktopDuplicationは動いた
なんでかわからんがTexture2Dから取得したbyte配列を使ってTexture2Dを生成するのがうまくいかない...
↓これでうまくいくと思ったんだけどなぁ...
// uDesktopDuplicationからデスクトップのキャプチャを取得する.
var capture = tex.monitor.texture;
var rawData = capture.GetRawTextureData();
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(rawData); // 一旦生のデータでテクスチャを生成できるのを確認する必要がある.
m.material.mainTexture = texture;
ヨシ、何とかテクスチャをbyte[] -> string -> byte[] -> Texture2D -> 何らかのメッシュに張り付ける
これが出来た
ただbyte[]を圧縮すると画像がおかしくなるから今度はそこが課題かな.
PhotonBoltって文字列140文字までしか送信できないのか...
テクスチャのサイズは720x480
最後の文字 -1 してたけどこれいらないかも? 業務終了後に試してみる
ひとまず↓のような流れが出来た
- RenderTextureでキャプチャ
- RenderTextureの内容をEncodeToPNG()でbyte配列に
- byte配列をstring配列に変換
- string配列を圧縮したstring配列にし、最後の要素に -e のマーカーを付けた
- 受け取った( 想定 )のコードでstring配列を一つのstringにし、byte配列に変換
- byte配列からTexuter2Dを生成、Spriteに変換しuGUIのImageへ適用
作っておいてなんだが何やってるんだろうな俺...
別クラスに分けたところは省略、最初のscreenCapture.CaptureToStrings()の部分が 1 ~ 3 の処理
スクショっとったやつをstring配列に変換している.
public void Capture()
{
// 1 ~ 3 の処理.
string[] captureStrings = screenCapture.CaptureToStrings();
// 4 の処理.
// 受け取ったstringを圧縮.
var compressStrings = new List<string>();
for (var i = 0; i < captureStrings.Length; ++i)
{
if (i == captureStrings.Length - 1)
{
// 最後の要素にはマーカーをつける.
compressStrings.Add(StringCompressor.CompressFromString(captureStrings[i]) + "-e");
}
else
{
compressStrings.Add(StringCompressor.CompressFromString(captureStrings[i]));
}
}
// stringにしたデータを送信.
StartCoroutine(SendCapture(compressStrings));
// 5 の処理.
// 受信、イベントを受け取るときは List にキャッシュし -e マーカーのついているデータが来るまで Listにデータを積む.
string[] recieve = compressStrings.ToArray();
var decompressStrings = new StringBuilder();
for (var i = 0; i < recieve.Length; ++i)
{
// 最後の要素のマーカーを取り除く.
decompressStrings.Append(StringCompressor.DecompressToStr(recieve[i].Replace("-e", "")));
}
File.WriteAllText(Application.persistentDataPath + "/jetson.txt", decompressStrings.ToString());
var texture = screenCapture.StringToTexture2D(decompressStrings.ToString());
// 6 の処理.
// 何らかのテクスチャに張り付ける.
m.material.mainTexture = texture;
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
GameObject go = GameObject.Find("Hoge");
go.GetComponent<UnityEngine.UI.Image>().sprite = sprite;
}
PhotonBoltでデータのやり取りが無理すじっぽかったらどっかにデータを移動してそこから取得するようにしてみるか
ここの最後尾かどうかの判定とかは別のクラスに移動したい
よっしゃ、ひとまずPCで撮影したスクリーンショットをMagicLeapに送信してMagicLeap側で再構築できるようにしたぞ!
強引すぎて強引MyWay
今時間計ったらPCが送信開始してから46秒で構築完了した( MagicLeap側はZeroIterationの環境 )、実機だともっと厳しいかもしれんな
なんか知らんがOutOfRangeの例外が出てるな PCからMagicLeapに送信するとき.
Firebaseを勧められた、よくわかってないから適当に紹介してる記事貼っておく
同僚がUnity SDKのGitHubへのURLくれた
仕事が終わったら試してみるmBaaSについての記事
Firebaseは正式にサポートしてなさそうだからちょっと不安があるわね
FirebaseのUnityのチュートリアル
Firebase Storageのチュートリアルはこれかな?
Unityでのストレージへの参照のチュートリアル
Unityからファイルをアップロードする方法
ファイルをダウンロードする方法
Firebaseだとなんかうまくいかない、ここはPUNでメッセージを送信する方法に切り替えたほうがいいかもしれない
Photon PUNの入門記事があった
とりあえず画像サイズを小さくして試してみる
後スクショをグレースケールにしたらさらに圧縮できそう
モノクロはこれ
ポスタリゼーションと色を灰色、白色の二色にすることで50秒弱かかるところをおよそ3秒ほどまで縮めた
但し、MagicLeap側でガクッっとフレームレートが落ちるのでこの辺は改善したほうがいいかもしれない
現時点でのスクリーンショットの送信時間は2秒ほど
スライドの資料用に特に編集してないテクスチャとモノクロ、ポスタリゼーションでのデータ送信回数の違いのスクリーンショットを作っておく
uDesktopDuplicationと透過ディスプレイが相性が悪いみたい、uDesktopDuplicationを入れただけの差分でブランチ切って試したらうまく動かない、その直前のdevelopブランチではちゃんと動く( なんかMagicLeap云々のエラーが出てるが... )
とりあえずuDesktopDuplicationと透過ディスプレイの組み合わせはよろしくない事がわかった
念のためRunInBackGroundにチェックを入れてPCアプリをディスプレイモードで起動して動くかどうか見てみたらちゃんと動いた、悔しいけどこの方針で行くか、こうすれば何とかスクリーンショットはMagicLeap側に送信できるはずなんだよね
とりあえずPCアプリの方は動いてるみたいだから今度はMagicLeap側でメッセージを受け取ってスクリーンショットを再構築する処理を作らねば
uDesktopDuplicationのプラグインがあるだけでエラーでビルド出来ない、とりあえず今は手動でビルドするときにプロジェクト外に移動してビルド終わったら元に戻すってやつをやるか
今のところネットワークが切断することは起きてないが、今後切断されたら再度接続しなおす処理と現在接続状態か否かのインジケータくらいは欲しい
インジケータは作ったので一応切断した状態はわかるようになった、復帰処理は今のところ出来てない
MagicLeap側のUIは
前ノート | 次ノート | |
前 | ScreenShot | 次 |
前スライド | 次スライド |
こんな感じか?
スライド作成に取りかかる、来週発表だけど俺の場合スローペースだから今日から始めてちょうどいいくらい
画像転送を頑張って時間短くしたよりもUIの工夫とかの方がいいかもしれないな、そっちの方が他の人のニーズに沿ってそうだし