FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unityプラグイン
このページの記事一覧

【Unity】【C#】Android で VRM(VRoid)を動的に読み込む  


 VRM Live Viewer にも利用しているが、元々は「プラグインを使って VRM を Android で読み込めるか?」という実験をしてみたら、スンナリと行けてしまったので次々とアイデアが浮かび、VRM Live Viewer をリリースするまでに至ってしまったという…(タイムスタンプを見ると、試しにライブステージ導入してから、アプリリリースまで4日しかかかってない←夢中になるといつの間にかアプリを完成させてしまうことも多い(笑))。


 まぁせっかくなので、VRMVRoid を Android でも読み込み、利用する方法を書いておこう。ちなみに VRM は VRChat やバーチャルキャストで使われるアバターフォーマットではあるが、リアルタイムで読み込むことができるので、あらかじめモデルをアプリに入れてビルドする必要もなく、読み込みもそれほど時間はかからないので、応用範囲は広いと思う。

 私は Unity4 の時代から MMD を Unity で動かしたり、一般公開されているモデルを実験で使ってたりしてたが、Unity ではいつもキャラのバリエーションが少ないな~と感じていたので、VRM で動的に読み込めるのは画期的だとさえ思う。例えば RPG でもアクションでも、好きなキャラで遊べるゲームとかも作れそうだしね(もちろん、大きさやコライダの判定などの問題もあるが、あくまで可能性として(笑))。アイデアは常に新しい発想から生まれるので、既成概念に捕らわれずに色々やってみると良いと思う。それがいつか新たな作品に繋がる。

 今回はあくまで Android で VRMVRoid を読み込む方法だけだが(どちらも "~.vrm" で扱うとして)、私が試したところ、一度 Unity 内に読み込んでしまえば、プラットフォームに関係なく扱えると思うので(見た目はシェーダなどのせいで多少変わることもあるが)、ひとつの方法として覚えておけば色々活用できるだろう。ちなみに VRM Live Viewer は Android版と Windows版を出しているが、ファイル読み込みやダイアログなどプラットフォーム固有のもの以外は全て同じだ。実際にシーン1つだけでビルドしている。つまり複数のプラットフォーム対応も簡単にできることがわかる。


(※) Unity 5.6.3p1 - 2018.2.1f1 / UniVRM 0.40 - 0.43 / VRoid Studio 0.1.1 - 0.2.8 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認



■UniVRM をインポートする

 Unity で VRM を読み込むには UniVRM というオープンソースが必要となる。ライセンスは「MIT License」となるので、その辺りは各自で確認して欲しい。ちなみにライセンス形態にも色々あるが、MIT License は比較的緩いライセンスだ。ついでに参考資料も載せておこう。

(参考)
GPL, LGPL, BSD などのOSSライセンスの違いと注意点まとめ
知らないと損をする6つのライセンスまとめ



 なお、新規プロジェクトで Android プラットフォームでビルドして試すなら、パッケージをインポートする前に「File>Build Settings...」であらかじめ「Switch Platform」で Android プラットフォームに切り替えておいた方が良いかも知れない。UniVRM に内包されているシェーダ(MToon 等)を再コンパイルしたりするのに結構時間がかかる(笑)。


 プロジェクトの準備ができたら、まずは UniVRM をダウンロードしよう。今回はアプリに動的に VRM を読み込むので UniVRM の本体「UniVRM-x.xx_xxx.unitypackage」(xxx はバージョンなど)の他に「UniVRM-RuntimeLoaderSample-x.xx_xx.unitypackage」のインポートも必要になる。本体「UniVRM-x.xx_xxx.unitypackage」を先にインポートしてから、ランタイムローダ「UniVRM-RuntimeLoaderSample-x.xx_xx.unitypackage」をインポートしよう。とりあえず VRM の動的読み込みに必要なものはこれだけで良い。






●API のアップデートが促されたら、「Go Ahead!」する




■VRM を動的に読み込んでみる

 UniVRM のインポートが終わったら、次にプロジェクトビューで「Assets/VRM.Samples/Scenes」で、シーン「VRMViewer」を開いてみよう。ビューワ自体は PC 用なのだが、これを改造することにより、Android 等他のプラットフォームの読み込み方法もわかると思う。



 ちなみに「VRM Live Viewer」はこのシーンを元ベースとしている(見た目もたいして変わってないのでわかると思うが(笑))。他の VRM 利用アプリを見てみると、たぶん同じようにこれを改造してるものが多い気がする。エクスポートできるアプリを作るなら、シーン「VRMRuntimeExporterSample」あたりを見てみると良いと思う。せっかくのオープンソースなのだから、遠慮なく使わせて頂こう(笑)。


 このシーンでは左上部にある「Open」ボタンを押すことにより、VRM を動的に読み込んで、シーン上にモデル(アバター)をロードすることができる。ただ、Windows 上なら 「PC, Mac & Linux Standalone」プラットフォームになってればそのまま使えるが、Android では無視される。この辺りから少し改造していこう。


1.スクリプトとしてはヒエラルキーで「Canvas」をクリックして、インスペクタで表示される「Viewer UI」にそのコードが書かれている。これを編集しよう。グレーアウトしてる「Script>ViewerUI」をダブルクリックすれば、Visual Studio で開かれる(シングルクリックなら、プロジェクトビューで移動できる)。



2.「ViewerUI.cs」を開いたら、検索で「OnOpenClicked」を探してみよう。これが前述した「Open」ボタンのイベントハンドラとなっている。ここのコードを見てみるとプリプロセッサディレクティブ(#if~文)でプラットフォームが分けられている。とりあえず Unity エディタ上でもテストできるようにディレクティブ(UNITY_EDITOR_WIN)を付け加えておこう。

void OnOpenClicked()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
var path = FileDialogForWindows.FileDialog("open VRM", "vrm", "glb", "bvh");
#else
var path = Application.dataPath + "/default.vrm";
#endif
・・・(略)・・・
}

 ちなみに「UNITY_EDITOR_WIN」とは「Unityエディタ上でかつ Windows である場合の条件」である。プラットフォーム依存コンパイルを上手く使えば、複数のプラットフォームを分別することも可能だ。まぁしかし、コードは見づらくなるので、機能まるごとみたいな場合は、クラスごとに用意するという手もある。今回は一部を改造して使うので、この方法でやっていこう。

プラットフォーム依存コンパイル


3.「UNITY_EDITOR_WIN」を入れたら、グレーアウトしていた文字が見えるようになったと思う。しかし「FileDialogForWindows.FileDialog」の方にエラーが出たかも知れない。まぁ、これも同じプラットフォーム依存なので、「FileDialogForWindows」部分にカーソルを合わせ、「F12」を押せば、クラスがまるごとグレーアウトしてるのがわかる。手順2と同じように「UNITY_EDITOR_WIN」を #if~文に追加しよう。

#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
using System;
・・・(略)・・・
#endif

namespace VRM
{
public static class FileDialogForWindows
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
#region GetOpenFileName
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileName
{
・・・(略)・・・
}
・・・(略)・・・
#endif
}
}


 これでコンパイルが通るようになったと思う。一旦、Unityエディタに戻ってプレイしてみよう。「Open」ボタンを押して適当な「~.vrm」を読み込んで見ると良い。VRMニコニ立体で多く配布されているので、いくつかダウンロードしておくと良いだろう。「ニコニ立体ちゃん」ことアリシア・ソリッドはとても軽いのでテストするにはもってこいだ。自分で作った VRoid でも可能だが、髪の毛などメッシュが多いものほど、生成に時間がかかるようだ(なので「VRM Live Viewer」では非同期読み込みの方を利用している。非同期読み込みを使うには「.NET4.x」にする必要があるので、ここでは割愛)。

ニコニ立体ちゃん (VRM)





■VRM の動的に読み込みを Android に対応させる

 VRM を動的に読み込みに成功したなら、後は Android に対応させるだけだ。ファイル選択などはプラットフォームに依存するので、先に出てきた「FileDialogForWindows」のようなものが必要になるが、Unity の標準機能には無いので、ここではプラグインを使うことにする(自分で作ったものがあれば、それでも良い)。



 ここで紹介するプラグインは元々私がブログで公開していたものだが、様々なアプリで利用して貰えてるようなのでアセットストアにも提出したというものだ(既に GooglePlay 等で公開されてるアプリなどにも利用されている。「〇〇というアプリを作ってるんですが、使わせて貰って良いですか?」と聞かれるようになったので、気兼ねなしに使えるようにアセットストアにも出したという経緯もある)。AssetStore版GoogleDrive版に機能的な違いはないので(AssetStore版 はアセットストアの規約に合わせただけ)、どちらを利用しても構わない(※ここでは AssetStore版を例にしている)。



 セットアップは以前の記事にあるので、そちらを参照して欲しい。AssetStore版GoogleDrive版では一部ファイル名やパス、素材が違うくらいで、内容的には同じだ。注意点は「Plugins」フォルダを「Assets」直下に移動し、「Plugins/Android」フォルダにあるサンプルのマニフェストファイル(AndroidManifest.xml)を用意しておくということだ(テストだけなら、"AndroidManifest_demo.xml"[AssetStore版]、または"AndroidManifest_test.xml"[GoogleDrive版]を複製してリネームすれば良い)。

AssetStore版のセットアップ
GoogleDrive版のセットアップ


1.プラグインのインポートとセットアップの準備が済んだら、プロジェクトビューの検索で「StorageOpenFileController」のプレファブを見つけよう。見つけたら、これをヒエラルキーに置き、後述のコードを書くことにより、Android でもファイルの情報を受け取れるようになる。本来なら Android でストレージの読み取りなどにはパーミッションなども必要になるが、前述のデモのマニフェスト("AndroidManifest_demo.xml"など)を使ってる分には既に含まれている(「READ_EXTERNAL_STORAGE」または「WRITE_EXTERNAL_STORAGE」が必要。デモにはそれ以外の権限も含まれているが、通常は不要な権限は削除した方が良い→ユーザーにインストを拒否られる確率が高くなるため)。

(パーミッション)
READ_EXTERNAL_STORAGE(ファイル読み取り権限)
WRITE_EXTERNAL_STORAGE(ファイル読み書き権限)



2.次に「StorageOpenFileController」で取得したファイル名を受け取るハンドラを、元のコード「ViewerUI.cs」の「OnOpenClicked」に追加しよう。書き方は前述のコードに追加する形となる。Android の場合ディレクティブは「UNITY_ANDROID」となるので、それを追加し、「StorageOpenFileController」でストレージを開くコードを Android プラットフォーム用に書いておこう

プラットフォーム依存コンパイル

using FantomLib;

・・・(略)・・・

void OnOpenClicked()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
var path = FileDialogForWindows.FileDialog("open VRM", "vrm", "glb", "bvh");
#elif UNITY_ANDROID
var path = "";
StorageOpenFileController storageOpenFileController = FindObjectOfType<StorageOpenFileController>(); //ここはインスペクタで登録できるようにしても良い
storageOpenFileController.Show(); //実機ではエクスプローラのようなもので、ファイル選択ができるようになる
#else
var path = Application.dataPath + "/default.vrm";
#endif
if (string.IsNullOrEmpty(path))
{
return;
}
・・・(略)・・・
}

※この例はやっつけ的なコードなので、「StorageOpenFileController」をインスペクタで登録できるようにしたり、任意にまとめたりして使って下さい(笑)。


3.ランタイム時では「StorageOpenFileController」は閉じられてから、コールバックで結果(選択されたファイルパス名)が返ってくるので、「UNITY_ANDROID」ディレクティブ内ではパスを空(path = "")にしていることに注意して欲しい。これはすぐ下にある「string.IsNullOrEmpty(path)」で一旦終了することを意味する。

 なので、取得したパスを受け取るハンドラを作成する必要がある。ここでは簡略のため、元の「OnOpenClicked()」内のコードを一部まるっとコピーして、もう1つ「OnStorageOpenFile()」というメソッドを定義した(メソッド名は任意)。

public void OnStorageOpenFile(string path)
{
if (string.IsNullOrEmpty(path))
{
return;
}

var ext = Path.GetExtension(path).ToLower();
switch (ext)
{
case ".gltf":
case ".glb":
case ".vrm":
LoadModel(path);
break;

case ".bvh":
LoadMotion(path);
break;
}
}

※UniVRM v0.40 以前は拡張子分岐は無いが、同じように「LoadModel(path)」を呼べば良い。

 実際には「StorageOpenFileController」をインスペクタで登録できるようにしたり、拡張子による分岐などは重複してるので「OnOpenClicked() → OnStorageOpenFile(path)」へ行くように書き換えても良いだろう。その辺りはお任せする(笑)。とりあえずはコード自体はこれで良い。


4.後はヒエラルキーに戻って「StorageOpenFileController」のコールバック「OnResult」に先程の「OnStorageOpenFile(String)」に登録しよう。これで一応完成である。ただし、実機でしか確認できないので、Android ビルドして動作確認してみよう。



5.「File>Build Settings...」を開いてシーン「VRMViewer」を追加してビルドしよう。ビルドに関してはいくつか注意点があるので、以下を参照して欲しい。

「要求 API Level」の設定
シーンを追加してビルドする
Unity 2018.1.0~1.6 での Gradle ビルドにおいて、「Cannot read packageName from~(パス)\AndroidManifest.xml」と出る。




 ここまでできれば、例えば以前の「VRoid(VRM)を動かす」のようにして、ゲームに使うことも可能だろう。1つ1つの技術は結構手間のかかるものだと思うが、プラグインも含め、全て無料でできるので、これを使わない手はない(笑)。今までにない新たな利用法を考えてみるのも良いだろう。

●実機(Android)で「ニコニ立体ちゃん (VRM)」を読み込んでみた所

ニコニ立体ちゃん (VRM)
(c) DWANGO Co.,Ltd. ニコニ立体ちゃんライセンス


 今回はただ VRM を読み込んで動的にアバターを召喚(笑)しただけだが、実際にスマートフォンで利用するには画面解像度・回転の対応やピンチなど、使い勝手を良くした方が良いだろう。プラグインにはそういったスマホらしい操作(ピンチ・スワイプ・ロングタップ等)の例も入っている。VRM Live Viewer の Android 版はまさにその使用例なので、動作確認にインストして動かしてみるのも良いだろう(またはプラグインのデモもQRコードからインストできるようにしてあるので参考に)。











(関連記事)
【Unity】VRM(VRoid)をライブステージで踊らせるアプリを作ってみた
【Unity】VRoid(VRM)をインポートして動かす
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた


スポンサーサイト

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: VRM  VRoid  Unityプラグイン  C# 
tb: 0   cm: --

【Unity】【C#】テキストファイルの読み書きをする(リソース/ストレージ)  


 Unity では内部リソースからテキストファイルを読み込む方法が非常に簡単に用意されているが、それとは別に外部のリソース(ストレージ等)でテキストファイルを読み書きする方法も書いておこう。

 またついでに Android でプラグインを利用して読み書きする方法も紹介しておく。ちなみに Unity から C# で直接 SDカードに書き込もうとすると、アクセス拒否で失敗するが、プラグインを利用する場合は、SDカードにも書き込むことが可能だ。エクスプローラみたいなファイル選択も使えるので、ユーザーが自由にファイルの読み書きを行えるようなシステムを作るには役に立つだろう。

 ここでは C# スクリプト例はインスタンスメソッドとして書いておくが、static なライブラリとして利用したいなら、例外処理を throw などすれば良いと思う。とりあえず簡単な実装を書いておこう。自由に改造でもして使って欲しい。


(※) Unity 5.6.3p1 - 2018.1.8f1 / Windows10(x64) で確認



■リソースからテキストアセットを読み込む(Unity ビルトイン機能)

 ここでは Unity の機能を使って、アプリに内包したテキストファイルを読み込んでみよう。

 これは Unity のエディタ上であらかじめリソースとして持っておき、ランタイム時にロードする方法だ。Unity では「Resources」というフォルダを作っておくと、起動後にリソースとしてテクスチャやデータなどを読み込むことができる。ここにテキストファイルを置いておけば、「TextAsset」として簡単に扱える。それを利用して読み込んでみよう。

●リソースからテキストアセットを読み込む(メソッド定義)
using UnityEngine;
using UnityEngine.UI;

public string textAssetName = "Texts/テスト"; //拡張子はいらない
public Text displayText; //表示するUI-Text

//リソースからテキストアセットを読み込む
void LoadTextAsset(string name)
{
if (string.IsNullOrEmpty(name) || displayText == null)
return;

TextAsset textAsset = (TextAsset)Resources.Load(name, typeof(TextAsset));
if (textAsset != null)
displayText.text = textAsset.text;
else
Debug.Log("Not found TextAsset : " + name);
}

●使用例(メインでのコードなど)
using UnityEngine;

//ロードボタンのコールバックハンドラ
public void OnLoadTextAsset()
{
LoadTextAsset(textAssetName);
}

 読み込み先テキストには UI-Text(displayText)を用いているが、もちろん string 型の引数のメソッドを作って直接読み込んでも良い。これはあくまでも例なので、その辺りは用途に応じて書き換えて欲しい。

●「Resources」フォルダを作り、その配下にテキストファイル(UTF-8)をインポートしておく。

 テストするには UI-Button などを適当に配置し、「Button」の「OnClick()」にスクリプトの「OnLoadTextAsset()」を登録すれば良い。





●「テスト用スクリプトのアタッチ例


●ファイルの内容は任意(UTF-8)

※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。



■ローカルストレージなどでテキストファイルの読み書きをする(C# スクリプト)

 次にパス(ファイル名)を指定してテキストファイル(UTF-8)を読み書きすることをやってみよう。

 例ではフォルダ(ディレクトリ)に「Application.persistentDataPath」を使っているが、これはプラットフォームによって変わるデフォルトの永続的な保存先となる。また、会社名(company name)やアプリ名(product name)でもフォルダ分けされる。詳しくは公式マニュアルで確認して欲しい。

(参考)Application.persistentDataPath

●ローカルストレージなどでテキストファイルの読み書きをする(メソッド定義)
using System;
using System.IO;
using System.Text;

//テキストをファイルから読み込む(行読み)
//※Android で SD カードから読み込みをするには、「AndroidManifest.xml」にパーミッション("READ_EXTERNAL_STORAGE" または "WRITE_EXTERNAL_STORAGE")が必要。
string LoadText(string path)
{
StringBuilder sb = new StringBuilder(1024); //※capacity は任意

try
{
using (StreamReader reader = new StreamReader(path))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
sb.Append(line).Append("\n");
}
}
}
catch (Exception e)
{
Debug.Log(e.Message);
return null;
}

return sb.ToString();
}

//テキストをファイルに保存
//※Android で External Storage に書き込みをするには、「AndroidManifest.xml」にパーミッション("WRITE_EXTERNAL_STORAGE")が必要。
//※セキュリティ上、Unity から直接 SD カードには保存できない。
bool SaveText(string text, string path)
{
try
{
using (StreamWriter writer = new StreamWriter(path))
{
writer.Write(text);
writer.Flush();
writer.Close();
}
}
catch (Exception e)
{
Debug.Log(e.Message); //Access to the path "filename" is denied. → パーミッションが無い, 書き込みアクセス不可(SDカードなど)
return false;
}
return true;
}

●使用例(メインでのコードなど)
using UnityEngine;
using UnityEngine.UI;

public string filename = "test.txt";
public Text displayText; //表示するUI-Text

//ロードボタンのコールバックハンドラ
public void OnLoadClick()
{
if (string.IsNullOrEmpty(filename) || displayText == null)
return;

string path = Path.Combine(Application.persistentDataPath, filename); //プラットフォームによってパスは異なる

if (!File.Exists(path))
{
Debug.Log("Not found : " + path);
return;
}

string text = LoadText(path);
if (!string.IsNullOrEmpty(text))
displayText.text = text;
}

//セーブボタンのコールバックハンドラ
public void OnSaveClick()
{
if (string.IsNullOrEmpty(filename) || displayText == null)
return;

string path = Path.Combine(Application.persistentDataPath, filename); //プラットフォームによってパスは異なる

if (SaveText(displayText.text, path))
Debug.Log("Save to : " + path);
}

 テストするには UI-Button などを適当に配置し、「Button」の「OnClick()」にスクリプトの「OnLoadClick()」「OnSaveClick()」をそれぞれ登録すれば良い。
●UI-Button 配置とテスト用スクリプトのアタッチ例

●UI-Button のコールバック登録例

 読み書きするテキストには UI-Text(displayText)を用いているが、もちろん string 型の引数のメソッドを作って直接読み書きしても良い。これはあくまでも例なので、その辺りは用途に応じて書き換えて欲しい。

 なお、Android でSDカードに読み書きする場合は以下のパーミッションが必要になる。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />'

 パーミッションについての説明が必要なら以前の記事を参照して欲しい。


※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。



■Android でファイル選択+テキストファイルの読み書きをする(プラグイン利用)

 ついでにプラグインに入っている「StorageLoadTextController」と「StorageSaveTextController」(~/FantomLib/Prefabs/System/ 以下。検索で探すと簡単)というテキストファイル読み書きの機能を使ってみよう。これら「~Controller」は Android 標準のファイル選択機能を使い、テキストファイルを読み込み・書き込みをしてくれるものだ。これら機能はプラグイン内部に実装されているので、自分でコードを書く部分は、機能呼び出しと結果受け取りだけで良い。

 ではまず、その機能呼び出しと結果受け取りのコードだけ書いてしまおう。名前は任意で良い。これらは各「~Controller」や UI-Button のコールバックハンドラとなる。

●コールバックハンドラ例(メインでのコードなど)
using UnityEngine;
using UnityEngine.UI;
using FantomLib;

public Text displayText; //表示するUI-Text

//読み込んだテキストを UI-Text に表示する
public void OnStorageLoad(string text)
{
if (string.IsNullOrEmpty(text) || displayText == null)
return;

displayText.text = text;
}

public StorageSaveTextController storageSaveTextController; //※インスペクタで登録

//UI-Text のテキストをファイルに保存する
public void OnStorageSave()
{
if (displayText == null || string.IsNullOrEmpty(displayText.text)) //※空は保存しない場合
return;

if (storageSaveTextController != null)
storageSaveTextController.Show(displayText.text);
}

//エラーステータス用コールバックハンドラ
public void OnError(string message)
{
Debug.Log(message);
}

 あとはヒエラルキーに「StorageLoadTextController」と「StorageSaveTextController」(~/FantomLib/Prefabs/System/ 以下。検索で探すと簡単)を置き、UI-Button など(※名前は任意)を Canvas に配置したら、インスペクタでコールバックを設定するだけだ。

●「StorageLoadTextController」と「StorageSaveTextController」を置く。テスト用スクリプトのアタッチ例。

●スクリプト(TextFileTest)へコールバック登録する
※または、StorageLoadTextController.OnResult に直接 UI.Text.text を登録しても良い。

●UI-Button から機能呼び出しをする


 あとはビルドするだけだが、プラグインを含む場合のビルドは「AndroidManifest.xml」が必要になる。その辺りは以前の記事にまとめてあるので参照して欲しい。もちろん「Build Settings...」にシーンを追加するのを忘れずに。

●ファイルの内容は任意(UTF-8)

※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。


※この記事のUnityアセットはプラグインとして配布されています。


※とりあえず試してみたい方は、最新版をビルドした apk デモをダウンロードできます。動作確認にもどうぞ。

プラグインデモをダウンロード
(Google Drive を利用)


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。


(関連記事)
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  Unityプラグイン  C# 
tb: 0   cm: --

【Unity】【C#】動的にオーディオファイルの読み込みと再生をする  


 プラグイン ver.1.16 の簡易音楽プレイヤーを作る際にちょっと調べたんだけど、VideoPlayer と違って、基本的に WWW を使うのが常套みたいだね。プラグインのデモスクリプトとしては「EasyMusicPlayer.cs」と「ExternalStorageTest2.cs」の2種類で書かれているが、基本は同じなので再利用しやすい部分を抜粋して書いておくことにした。実際に使う際にはもう少し手を加えた方が便利かも知れない。



(※) Unity 5.6.3p1 - 2018.1.8f1 / Windows10(x64) で確認



■オーディオファイルの読み込みと再生のコード

●オーディオファイルの読み込みと再生
using System.Collections;
using System.IO;
using UnityEngine;

public class AudioLoadTest : MonoBehaviour //※クラス名は任意
{
public AudioSource audioSource; //インスペクタで AudioSource をセット
public string path = "/storage/emulated/0/Music/sample.mp3"; //※ファイルは任意

//外部からの呼び出し用メソッド
public void LoadAudio(string path)
{
if (Path.GetExtension(path) == ".m4a") //※"m4a"は再生できないっぽい
{
Debug.Log("Not supported audio format.");
return;
}

StartCoroutine(LoadToAudioClipAndPlay(path));
}

//ファイルの読み込み(ダウンロード)と再生
IEnumerator LoadToAudioClipAndPlay(string path)
{
if (audioSource == null || string.IsNullOrEmpty(path))
yield break;

if (!File.Exists(path)) {
//ここにファイルが見つからない処理
Debug.Log("File not found.");
yield break;
}

using(WWW www = new WWW("file://" + path)) //※あくまでローカルファイルとする
{
while (!www.isDone)
yield return null;

AudioClip audioClip = www.GetAudioClip(false, true);
if (audioClip.loadState != AudioDataLoadState.Loaded)
{
//ここにロード失敗処理
Debug.Log("Failed to load AudioClip.");
yield break;
}

//ここにロード成功処理
audioSource.clip = audioClip;
audioSource.Play();
Debug.Log("Load success : " + path);
}
}

// Use this for initialization
private void Start () {
//起動時に読み込むときなど
LoadAudio(path); //※メインからの呼び出し例
}
}

 ここではインスペクタに path を入力し読み込むようになっているが、外部呼び出し用のメソッド「LoadAudio()」の引数にパスを渡すようになっているので、通常は「Start()」内の呼び出しは削除した方が良いだろう(ただのメインからの呼び出し例のため)。

 オーディオファイルの読み込みには WWW を使っているが、あくまでローカルストレージを想定しているので、頭に "file://" を付けているのに注意して欲しい。インターネットからダウンロードしたい場合は "file://" を取り除いて URL形式("http://~")を直接与えれば良いと思う。しかしその場合、タイムアウトを付けたり、キャッシュ付きなどを使う必要があるかも知れない(パケ代もかかるので)。

 WWW を使っている分にはオーディオ形式は気にしなくても良いが、再生するには "mp3" や "ogg", "wav" などが良いようだ。"m4a" は読み込み(ダウンロード)はできるが、再生はできなかった(Unity2018.1.5f1時点)。

 スマホで使うにはアクセス権などが必要になることもあるが、とりあえずはこれだけで読み込みと再生はできる。以降では更にプラグインを利用して、自由にファイルを選択して読み込む方法もやってみよう。



■スマホ(Android)のストレージから読み込んで再生する

 ここではプラグインを使ってストレージを開き、再生する方法を書いておこう。といってもスクリプト自体は上記の例そのままで良いので、後はプレファブをシーンに置き、コールバックをセットするだけだ。プラグインのインストールからはじめる場合は、以前の記事を参考にして欲しい。


1.先に作った「AudioLoadTest.cs」スクリプトを適当な GameObjectにアタッチし、「AudioSource」を追加し、インスペクタでセットドラッグ&ドロップでセットしよう。「Play On Awake」はオフに、「Loop」はオンにしておくと良い(※曲を繰り返し再生する場合)。



2.シーンにプレファブ「StorageOpenAudioController」(~/FantomLib/Prefabs/System/ 以下。検索で探すと楽)を置き、インスペクタで「Mime Types」に読み込むオーディオ形式の MIME type を入れておく。ここでは mp3/m4a = "audio/mpeg", ogg = "audio/ogg","application/ogg" を入れているが、任意で良い(※デフォルト=空の状態ではすべてのオーディオ形式となる)。




 なお、ogg の MIME type が2種類書かれているが、プロバイダ(ストレージ)の種類によって、同じ拡張子でも MIME type が違ってたりするので注意が必要だ。例えばローカルストレージでは "application/ogg" となり、GoogleDrive などでは "audio/ogg" で認識される。今回の再生の場合、ローカルストレージの "application/ogg" だけでも良い(複数指定して置けば、どのプロバイダ[ストレージ]でも見えるようになる)。直接開けるか否かはアプリの対応状況によるため、実際に使う際にはあらかじめ絞り込んでおくのも良いだろう。

※ここで言う「プロバイダ」とはアプリが持つ情報提供プログラムのことである。Android アプリはそれぞれアプリごとにプロバイダを持つことができ、それによりユーザーに与える情報を制限することができる。この例の場合、ローカルストレージや GoogleDrive(これは1つのアプリである)、OneDrive(これもアプリ)ごとということになるので、「ストレージ」ごとになることと同義になる。


3.シーンにロード用のボタンを置き、コールバック「OnClick」に「StorageOpenAudioController.Show」を登録する。これでボタンを押すことにより、ストレージが開く。






4.プレファブ「StorageOpenAudioController」に戻り、「OnResult」に先に作ったスクリプト「AudioLoadTest.LoadAudio」を登録する(※手順2の画像のようになる)。これでストレージから選択したオーディオファイルのパスがスクリプトに渡され、読み込みが成功すれば再生される(※"m4a" は再生できないようなので(Unity2018.1.5f1 時点)、LoadAudio() で拡張子チェックをしてるが、実際に使うには拡張子で絞るようにしても良い。「EasyMusicPlayer.cs」(~/FantomLib/Scripts/Example/ 以下)にはその例も書かれているので必要なら参照)。なお、GoogleDrive などクラウドストレージからは直接再生(情報取得)できないので注意。その場合は一旦ローカルストレージにコピー(ダウンロード)すれば再生できるようになる。これで読み込みと再生だけなら完成だ。


※実機でログを確認するにはシーンに「DebugConsole」(~/FantomLib/Prefabs/System/ 以下)を置き、「AudioLoadTest.cs」内の「Debug.Log()」→「XDebug.Log()」にしておく。ビルドするには「デモのビルド」と基本的に同じ(シーンだけ変える)なので必要なら参照。


 あとは「プレイ/停止」など操作できるとなお良いね。このあたりのサンプルはデモシーン「ExternalStorageTest」の「ExternalStorageTest2.cs」に、またはシーン「MusicPlayerExample」の「EasyMusicPlayer.cs」(~/FantomLib/Scripts/Example/ 以下)に書かれているので、コピペしても良いと思う。「EasyMusicPlayer.cs」には連続再生やシャッフル、次の曲/前の曲、プレイリストに追加/削除など、一般的な操作の例を入れてあるので、必要なら参照して欲しい(最低限の機能で良いなら、そのまま使っても良い→「UnityChanInOtakuCity」はそのまま使ってる)。



※他、様々な機能を使ったデモが同梱。Unity×Androidアプリ開発に!

※サンプルで使っているループ曲を含んだライブラリも配信中。全31曲!


※とりあえず試してみたい方は、最新版をビルドした apk デモをダウンロードできます。動作確認にもどうぞ。

プラグインデモをダウンロード
(Google Drive を利用)


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。


(関連記事)
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】【C#】VideoPlayer で動画の終了判定をする


このコンテンツの一部には『ユニティちゃんライセンス』で許諾されたアセットが使用されています。
Portions of this work are licensed under Unity-Chan License


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityプラグイン  C# 
tb: 0   cm: --

【Unity】スマホで簡易360度(パノラマ, 全天球)ビューワを作る  


 タイトルは "スマホ"(※Android)となっているが、実際には他のプラットフォームでも関係ないので、作り方を覚えおけば色々応用が効くだろう。特に全天球への画像(動画)貼り付けは VR に使えばそのまま背景にできるので、Cardboard VRGearVROculus Go などにも簡単に流用できる(というより、元々「VRコンテンツ開発ガイド 2017」を以前読んでいたので、それをスマホに応用した)。ミクシータ(RICHO THETA) など360度カメラを持っている人は、保存した画像をそのまま使えるので試してみると良い(持ってなくても Google Play のアプリのサンプルでスマホに画像を保存すれば確認できる)。ARに応用してる例もあるね。


 なお、今回の記事はプラグインのギャラリー読み込みデモ(GalleryPickTest)のセットアップも兼ねている。サンプルシーンを使えば1から構築する必要はないので、すぐに試してみたい人はプラグインをダウンロードして欲しい。AssetStore版(ver.1.15以降[無料])GoogleDrive版の2つがあるが、基本的にはどちらも同じものだ。好きな方で構わない。

>>GoogleDrive版 プラグイン&サンプルをダウンロード
(Google Drive を利用。画面右上にあるダウンロードアイコンを押す)

>>AssetStore版をダウンロード


※とりあえず試してみたい方は、最新版をビルドした apk デモをダウンロードできます。動作確認にもどうぞ。

プラグインデモをダウンロード
(Google Drive を利用)


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。


 プラグイン自体のセットアップは以前の記事を参照して欲しい(>>GoogleDrive版 | >>AssetStore版)。デモが入っているパスは GoogleDrive 版では「Assets/_Test/」に、AssetStore版は「Assets/FantomPlugin/Demo/」となっているので適宜置き換えて欲しい。ファイル名は基本的に同じだ。


(※) Unity 2018.1.0f2 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認



■全天球素材のダウンロードと設定(360度ビューワ)

 360度のビューワを作る際には法線が内側を向いている球状メッシュを使う必要がある(Unityのデフォルトの球状メッシュは外側向き)。要するに球の内側から覗いたとき映像が見える必要があるので、通常は3Dモデリングツールなどで素材を作らなくてはならない。しかし幸いにも素材を提供してくれている方がいるので、有り難く使わせて頂こう(素材を提供してくれる人は神ですね(笑))。以下のページにある「Sphere100.fbx」(※ページ内で検索するとすぐ見つかる)をダウンロードして欲しい。

UnityとOculusで360度パノラマ全天周動画を見る方法【無料編】 で「Sphere100.fbx」をダウンロード(※直接ダウンロードが簡単)。


 ダウンロードが完了したら、プラグインのサンプルシーンを使って実際にセットアップしてみよう。プラグインを利用している箇所はスマホから画像や動画のパスを取得しているだけなので、あらかじめプロジェクトに素材を含んでいる分にはプラグイン機能を使う必要はない。画像や動画を動的に貼り付ける部分は Unity の機能なので、コピペして色々なアプリに応用すれば良いだろう(「GalleryPickTest2.cs」にまとめてある)。それでは以下の方法で、サクッと簡単に360度のビューワを作ってみよう(笑)。


1.ダウンロードした「Sphere100.fbx」を Unity のプロジェクトにドラッグ&ドロップしよう(任意のフォルダで良い)。またインポートした「Sphere100」をクリックして、インスペクタで「Model>Scale Factor」を「1000」にしておく(大きさは任意でも良い)。





2.次にギャラリーから画像を取得するデモシーン「GalleryPickTest」(GoogleDrive版は「Assets/_Test/Scenes/」, AssetStore版は「Assets/FantomPlugin/Demo/Scenes/」内にある。検索を使うと簡単)を開こう。このデモはスマホの標準ギャラリーアプリを開き、画像や動画のパスを取得して Unity に取り込むものだが、オプション(形状)の「360 degrees」にはダミーのテキスト「Please replace with 'Sphere100', and set 'TextureMat' as material.」が表示されるのみで何も表示されない(プレイして「360 degrees」を押してみるとテキストが表示される)。このダミーテキストを全天球メッシュに置き換えることで、簡易360度ビューワにすることができる。



3.ヒエラルキーから「Stage」を開こう。この中にある「Sphere100 (360degrees Dummy)」(画像用)と「Sphere100Video (360degrees Dummy)」(動画用)が全天球ダミーなわけだが、この階層にインポートした「Sphere100」をドラッグ&ドロップしてオブジェクトを配置しよう。「Sphere100 Video」となってる方は「Sphere100」を複製(Ctrl-D)して名前を変えただけのものである。



4.各オブジェクトの Position が (0, 0, 0) となっているのを確認したら、次に Scale を変更しておこう。大きさは任意で良いが、ポイントは X軸を負の値にしておくことだ。これは正の値で試して見ればわかると思うが、カメラは球の内側から覗く感じになるので、正の値だと画像が左右反転して見える。なので、負の値にしておけば人の目には正しい方向に見えるというわけだ。またついでにオブジェクトに影を付ける処理は必要無いので、すべてオフにしておくのも良い(このデモでは影の無いマテリアルを使うので必須ではないが[※後述↓手順5])。

●画像用全天球「Sphere100」


●動画用全天球「Sphere100 Video」

※マテリアルのセットは下記(↓手順5)


5.あとは全天球に画像を映し出すためにマテリアルをセットしよう。GoogleDrive版では「Assets/_Test/Metarials/」に、AssetStore版は「Assets/FantomPlugin/Demo/Metarials/」に画像用の「TextureMat」、動画用の「VideoRenderTextureMat」があると思うので(プラグイン ver1.15以降)、それらをそれぞれ「Sphere100」「Sphere100 Video」の「Materials」にセットして欲しい。上記(↑手順4)のインスペクタのような感じになる。


 ちなみにこの「TextureMat」と「VideoRenderTextureMat」は特別なものではなく、「TextureMat」はプロジェクトビューで右クリックして「Create>Material」で新規マテリアルを作成し、インスペクタで「Shader」を「Unlit>Transparent」にしただけのもので、「VideoRenderTextureMat」の方は、先に「Create>Render Texture」を作っておき、同じように新規マテリアル(Create>Material)で「Shader」を「Unlit>Texture」にして、テクスチャに作っておいた「Render Texture」をセットしただけのものだ。ほぼデフォルトのままなので、必要あれば調整などは適当にやって欲しい(※ただし、このデモでは GalleryPickTest2.fitRenderTexture = true のとき、Render Texture を動画サイズに合わせて動的に生成するので、実際には使ってない。fitRenderTexture = false にするとセットした「VideoRenderTexture」を使うようになる[※解像度が固定サイズで良いときなどに使うと、負荷が少し軽くなる])。


6.最後にデモスクリプトの「GalleryPickTest2」をヒエラルキーで選択して、「Sphere」と「Video Sphere」にそれぞれ「Sphere100」「Sphere100 Video」のオブジェクトをセットしよう(初期状態では「Dummy」がセットされている)。これで準備は完了だ。






 エディター上でプレイして「360 degrees」をクリックしたら、背景が真っ白になったら成功だ(画像が読み込まれてないので白いだけ)。ビルドして実機で確認する方法はこちらの記事を参照して欲しい。

デモのビルド




 通常の画像を読み込んでも良いが、天井と底面の方を見ると収束してるので、せっかくなので全天球画像を読み込んでみよう。なんとあの日本列島360の全天球静止画がBOOTHにて無料で配布されている(まさに神!)。これをダウンロードしてスマホのストレージにコピーしよう。ギャラリーから読み込むと超美麗な360度画像を楽しむことができる(利用方法は規約に従ってね)。




 また以下のミクシータのアプリをインストして、保存したサンプルの画像を読み込んでみるのも良い。VR空間で見ると、まるでその場所にいるようで更に面白い。GearVR はそのままホーム>ギャラリーで観れるが、Cardboard なら色々ビューワアプリが出てるので観てみると良いだろう。ちなみに Unity 使える人なら、Cardboard VRGearVR も作るのはそう難しくはない(むしろVR内操作 UI 作る方が難しいね(笑))。しかし、全天球メッシュにあらかじめ画像を貼り付けておけば簡単に360度にできるので、挑戦してみるのも良いだろう。

RICOH THETA Type HATSUNE MIKU (Google Play) ※カメラが無くても、サンプル画像が入っているので、保存すれば360度画像を試せる。
VU Gallery VR 360 Photo Viewer (Google Play) ※Cardboard の360度ビューワアプリ
【Unity】GearVR アプリをビルドする







(関連記事)
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】GearVR アプリをビルドする
【Unity】ARCore を使って現実世界にプロ生ちゃんを召喚してみる



初音ミク」はクリプトン・フューチャー・メディア株式会社の著作物です。
© Crypton Future Media, INC. www.piapro.net (PCL)

このコンテンツの一部には『ユニティちゃんライセンス』で許諾されたアセットが使用されています。
Portions of this work are licensed under Unity-Chan License

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityプラグイン  VR 
tb: 0   cm: --

【Unity】Unity2018 でビルドエラー「CommandInvokationFailure: Gradle build failed.」が出る  


 Unity2018 はどうも内部的なシステムの開発要件が上がったのか、インストして既存のプロジェクトをビルドしてみようとしたら、いくつかエラーが出たので備忘録。ちなみに 2017.4.2f2 までは何のエラーも出なかった状態からなので、開発環境によってはサードパーティの SDK など(Java SDK, Android SDK/NDK, Android Studio 等)のアップデートも必要になるかも知れない。結構大変なので、開発中であったり、Unity2018 の必要がないのなら、アップデートは先送りした方が良いかもね。またプラグインも使ってみようと思ったらビルド中にエラーが出たので、その辺りの対処方法も書いておこう。



(※) Unity 2018.1.0f2 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認



■新規プロジェクトでいきなりエラー「ArgumentException: Value does not fall within the expected range.」が出る

 インストして起ち上げたら、いきなり出たのがこれ(笑)。ググったらすぐに対処法が出てきたので正確にはメッセージは覚えてないが、要するにインストールされている Visual Studio 2017 のマイナーバージョンが合ってなかったらしい(?)。なので、最新版にアップデートすればエラーは出なくなる

Visual Studio Tools for Unity (最新版をインストール)
Error with Unity and Visual Studio Bridge (VS project not updating when new scripts added)



■「CommandInvokationFailure: Gradle build failed.」以降のエラーメッセージに「failed to find Build Tools revision 28.0.0」のようなバージョン番号が出ている。


 これはどうやらインストしてある Android Studio(ないし SDK)にある Build Tools のバージョンが合ってない(?) みたいなのだが、私の場合「28.0.0rc2」が入っていたのにダメだった(SDK Manager で "28.0.0" を探したが、なぜか "~rc1", "~rc2" しか無い(笑))。仕方がないので試しに "28.0.0rc2" を削除して「27.0.3」を残したらエラーが出なくなった(Android Studio で「Tools>SDK Manager」を開き、「SDK Tools」タブでダイアログの下の方にある「Show Package Details」にチェックを入れると細かいバージョンを追加・削除できる)。ただこれはかなりいい加減な応急処置なので(笑)、バージョンが上がって安定版が出たら設定し直したほうが良いかも知れない。





■「CommandInvokationFailure: Gradle build failed.」以降のエラーメッセージに「Cannot read packageName from~(パス)\AndroidManifest.xml」と出る。

※このエラーは Unity2018.1.7f1 で fix されたようです。
Unity 2018.1.7f1 リリースノート
[ANDROID] $APPLICATIONID TAG IN A MANIFEST CAUSES GRADLE BUILD TO FAIL


 これはプラグインを使ってみようと思ったら出た。普通にビルドする分には関係ないかも知れない。「AndroidManifest.xml」をオーバーライドしている場合、Unity2018 の仕様(バグ?)なのか、「Build Settings...」で「Build System」を「Gradle」にしていると、でパッケージ名が追加されないようだ(Unity2017.4.2f2 までは自動で追加される)。なので独自に「AndroidManifest.xml」を設定している場合、「manifest」タグに「package」属性を手動で追加しておく必要がある。「package」にはプロジェクトのパッケージ名「Edit>Project Settings>Player>Other Settings>Identification>Package Name」と同じものを書いておく。または「Build System」を「Internal」にするとエラーは出なくなる(ビルドできるようになる)。

● "AndroidManifest.xml" に追加する部分(※"package" はプロジェクトのパッケージ名にする)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.productName">
・・・(略)・・・
</manifest>

●PlayerSettings

※パッケージ名(com.company.productNam)は必ずアプリ固有の名前(ID)として設定する必要アリ。

または「Build System」を「Internal」にするとエラーは出ない


 当プラグインでもエラーが出たときは、上記の方法でビルドできるようになる(※スマホ [Android7.0] でも動作確認済み)。




(関連記事)
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】【C#】モバイルビルド中の警告:Game scripts or other custom code contains OnMouse_ event handlers.~ を消す
【Unity】アイコン画像のフォーマット警告:Compressed texture XXX is used as icon. This might compromise visual quality of~ を消す
【Unity】5.6 の Canvas の警告:Shader channels Normal and Tangent are most often used with lighting~ を消す
【Unity】InitializeUnityExtensions: Must have a valid path for the plugin [XX] (XX:番号) というエラーの修正方法


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング  Unityライブラリ  Unityプラグイン 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop