20210609のUnityに関する記事は6件です。

BoidsアルゴリズムのUnityリンク集

日本語 英語 Sample Project 中国語
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでAssetPackのビルドをビルドパイプラインに組み込む

UnityでPlay Asset Deliveryを使うアプリのビルドを、Googleが提供するビルド方法(Unityエディタの専用メニューからビルド)ではなく、一般的なビルドパイプラインから呼び出す方法をまとめました。 これで BuildSettingsウィンドウのビルドや、Unity Cloud Buildによるビルドでも、AppBundleにAssetPackが含まれるようになります。 Google Play Pluginに含まれるPlay Asset Delivery Unity APIというライブラリを使用しますが、一部コードを書き換えます。 お行儀が悪いですが、Unityエディタの専用メニューからビルドするようにしか作らないGoogleさんが悪い。嘘です、ライブラリがあるだけでもとても助かります。 検証環境 Unity 2019.4.22f1 Google Play Plugin ver 1.4.0 コード AppBundleBuilderを修正する 1. AndroidPlayerFilePathをpublicにする Before AppBundleBuilder.cs private string AndroidPlayerFilePath { get { return Path.Combine(_workingDirectoryPath, AndroidPlayerFileName); } } After AppBundleBuilder.cs public string AndroidPlayerFilePath { get { return Path.Combine(_workingDirectoryPath, AndroidPlayerFileName); } } 2. BuildAndroidPlayerメソッドの冒頭にあるディレクトリ作成処理をメソッドに切り出す Before AppBundleBuilder.cs public virtual bool BuildAndroidPlayer(BuildPlayerOptions buildPlayerOptions) { var workingDirectory = new DirectoryInfo(_workingDirectoryPath); if (workingDirectory.Exists) { workingDirectory.Delete(true); } workingDirectory.Create(); : : After AppBundlePublisher.cs public void EnsureWorkingDirectory() { var workingDirectory = new DirectoryInfo(_workingDirectoryPath); if (workingDirectory.Exists) { workingDirectory.Delete(true); } workingDirectory.Create(); } public virtual bool BuildAndroidPlayer(BuildPlayerOptions buildPlayerOptions) { EnsureWorkingDirectory(); AppBundlePublisher.csを修正する AppBundlePublisherの適当な箇所にメソッドを追加します。 もしかしたらSystem.IOあたりのusingを追加する必要もあるかもしれません。 AppBundlePublisher.cs public static void PackAsset(string aabFilePath, AssetPackConfig config) { var appBundleBuilder = CreateAppBundleBuilder(); if (!appBundleBuilder.Initialize(new BuildToolLogger())) { return; } appBundleBuilder.EnsureWorkingDirectory(); File.Copy(aabFilePath, appBundleBuilder.AndroidPlayerFilePath, true); Debug.LogFormat("Building app bundle: {0}", aabFilePath); appBundleBuilder.CreateBundle(aabFilePath, config); } ポストプロセスビルド処理を追加する 適当なクラス名でIPostprocessBuildWithReportを実装します。 public class PublishPlayAssetDelivery : IPostprocessBuildWithReport { public int callbackOrder => 0; public void OnPostprocessBuild(BuildReport report) { if (report.summary.platform != BuildTarget.Android) { return; } var config = AssetPackConfigSerializer.LoadConfig(); AppBundlePublisher.PackAsset(report.summary.outputPath, config); } } 最後に Googleさんが提供している専用のビルドメニューでは下記のような動作をしています。 AppBundleをビルドしてテンポラリディレクトリに出力する AssetPackをビルドする AppBundleを解凍して、必要なAssetPackを組み込む 再度パッキングして、本来の出力先に保存する 上記の手続きが一連の流れとしてコーディングされていて、自分にとって都合が悪かったため、今回は3と4を抜き出してみました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unityで自動でiosのローカライズ対応をする

やりたいこと アプリ名とかのローカライズの設定を毎回xcodeで設定が面倒なのでunity内で完結したい アプリ名や、ios固有のATT表示の赤枠のメッセージなどが対象 ATTの中のテキスト アプリ名 検証環境 unity 2019.3.0f6 / Xcode 12.4 手順 1,プロジェクトにローカライズのデータを設置 何処に設置してもよいが下記2点は守ること  ・ファイル名はInfoList.strings  (末尾のsを忘れないこと)  ・格納するフォルダは対応言語に合わせること 日本語ならja (jpと間違えないこと)    言語コードはコチラ参照https://qiita.com/hirobe/items/cb906cca486675d02a87 Base.lproj は用意されてない言語が端末で選択されているときに参照されるデフォルト用 (デフォルト用のつもりでしたが参照されて無い感じがしたのでbaseは見なかったことにしてください) 2, InfoList.stringsの編集 例としてfr.lproj/InfoList.stringsの中を記載 CFBundleDisplayName = "フランス";  // アプリ名 ATTのメッセージを変えるなら NSUserTrackingUsageDescriptionを指定 このあたりの設定のkey 的なものの一覧がどこかにあるのだろうけどパッと見つからなかったので割愛 3、PostProcessBuildで1で作成したデータを紐づける PostProcessBuildとは、という説明は割愛 public class PostProcessBuild : IPostprocessBuildWithReport { const string IOSPbxProjectPath = "/Unity-iPhone.xcodeproj/project.pbxproj"; int IOrderedCallback.callbackOrder => default; void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report) { var summary = report.summary; if (summary.platformGroup != BuildTargetGroup.iOS) { return; } string pbxPath = summary.outputPath + IOSPbxProjectPath; var project = new PBXProject(); project.ReadFromFile(pbxPath); string targetGuid = project.GetUnityMainTargetGuid(); // 指定パス以下にあるローカライズデータを紐づける string[] languageArray = new string[] { "Base", "ja", "fr" }; // 紐づけたい言語を記載 foreach (string lang in languageArray) { string dirPath = Application.dataPath + $"/Editor/Localisations/{lang}.lproj"; // project内に設置したパスを指定 var lGuid = project.AddFolderReference(dirPath, $"{lang}.lproj"); project.AddFileToBuild(targetGuid, lGuid); } project.WriteToFile(pbxPath); } } 間違えやすいこと(やってて間違えてたこと) // 似たような関数があるので注意 project.GetUnityFrameworkTargetGuidではない string targetGuid = project.GetUnityMainTargetGuid(); ビルドして完了 いつものようにビルドをして書き出されたxcodeプロジェクトを開くと 指定したフォルダを見つけることができるはず あとは端末の言語を切り替えながら動作チェックしてみてください まとめ androidはコチラでできるみたい https://qiita.com/ptkyoku/items/3f25872ae0f356966c88 デフォルトの設定は後ほど探しておきます その他まちがってたらおしえてください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列とリストの基本操作(Unity)をまとめて行きます。

配列とリストの基本操作(Unity)をまとめて行きます。 配列編 ①宣言の仕方 //長さが5の空の配列を宣言 int[] numbers = new int[5]; //長さが5の3,5,4,2,1を含む配列を宣言 int[] numbers = new int[] { 3, 5, 4, 2, 1 }; //これはダメ、バグになる int[] numbers = new int[5] { 3, 5}; ②配列の長さを確認する方法 //長さが5の空の配列を宣言 int[] numbers = new int[5]; Debug.Log(numbers.Length);//結果は5 ③配列の要素を取り出す //長さが5の空の配列を宣言 int[] numbers = new int[5]; int number = numbers[3]; Debug.Log(number);//結果はintのデフォ値0、参照型だと結果はnull ④配列の要素を書き込み //長さが5の空の配列を宣言 int[] numbers = new int[5]; numbers[3] = 4; Debug.Log(numbers[3]);//結果は4 ⑤配列をリサイズ //長さが5の配列を宣言 int[] numbers = new int[] { 3, 5, 4, 2, 1 }; //長さ5の配列を長さ3にリサイズ、後ろの入りきれない要素の2と1はなくなる System.Array.Resize(ref numbers, 3);//{3, 5, 4} //長さ3の配列を長さ5にリサイズ、後ろに空の要素を二つ追加する、intだと0が追加される System.Array.Resize(ref numbers, 5);//{ 3, 5, 4, 0, 0 }; ⑥配列の並べ替え //長さが5の配列を宣言 int[] numbers = new int[] { 3, 5, 4, 2, 1 }; //並べ替え、結果は{ 1, 2, 3, 4, 5 },floatの配列にも使える System.Array.Sort(numbers); ⑦配列から同じ要素を保持するリストを作る //長さが5の配列を宣言 int[] numbers = new int[] { 3, 5, 4, 2, 1 }; List<int> numList = new List<int>(); numList.AddRange(numbers); リスト編 ①宣言の仕方 //空のリストを宣言 List<int> numbers = new List<int>(); //空のリストだが、5要素分のメモリーを確保したリストを宣言 List<int> numbers = new List<int>(5); //長さが5の3,5,4,2,1を含むリストを宣言 List<int> numbers = new List<int> { 3, 5, 4, 2, 1 }; ②リストの要素数とキャパシティーの確認の仕方 リストの要素数はCount(読み取り専用)、 確保したメモリー数はCapacity、こっちは書き込み可能だが、Countより小さい数字を指定するとバグる //空のリストだが、5要素分のメモリーを確保したリストを宣言 List<int> numbers = new List<int>(5); Debug.Log(numbers.Count);//結果は0 Debug.Log(numbers.Capacity);//結果は5 //長さが5の3,5,4,2,1を含むリストを宣言 List<int> numbers = new List<int> { 3, 5, 4, 2, 1 }; Debug.Log(numbers.Count);//結果は5 numbers.Capacity = 3;//5より小さいため、これはバグる ③リストの要素を取り出す、書き込み、配列と一緒 //長さが5の3,5,4,2,1を含むリストを宣言 List<int> numbers = new List<int> { 3, 5, 4, 2, 1 }; Debug.Log(numbers[3]);//結果は2 numbers[3] = 100; Debug.Log(numbers[3]);//結果は100 ④リストに要素を追加、複数追加 リストには要素を最後尾に追加するAddと、要素を複数まとめて最後尾に追加するAddRangeがある List<int> numbers = new List<int>(); numbers.Add(3); int[] addNumbers = new int[]{4,5,6}; numbers.AddRange(addNumbers); Debug.Log(numbers.Count);//結果は4//リストの中身は{3,4,5,6} ⑤リストから要素を削除 リストには配列にはない、特定の要素を削除するRemoveと、指定番号の要素を削除するRemoveAtがある。 Removeは同じ要素が複数あっても、一番左にある一つだけを削除する。 RemoveもRemoveAtも空欄を残さず、右の要素は左に一つシフトして空欄を埋める List<int> numbers = new List<int> {1,2,3,3,5 }; numbers.Remove(3);//3を削除、リストの中身は{1,2,3,5}になる numbers.RemoveAt(3);//リストの中身は{1,2,3}になる ⑥リストに要素を挿入 List<int> numbers = new List<int> {1,2,3,3,5 }; numbers.Insert(3,9);//3番目に9を挿入する //リストの中身は{1,2,3,9,3,5}に ⑦リストに特定要素があるかどうか List<int> numbers = new List<int> {1,2,3,3,5 }; Debug.Log(numbers.Contains(6));//結果はfalse Debug.Log(numbers.Contains(5));//結果はtrue ⑧リスト要素の並べ替え List<int> numbers = new List<int> {3,2,4,5,1 }; numbers.Sort();//リストの中身が{1,2,3,4,5}に ⑨リストから同じ要素を持つ配列を作る List<int> numbers = new List<int> {3,2,4,5,1 }; int[] numArray = numbers.ToArray(); #unity #indiedev #gamedev #プログラミング初心者 前回の話のおまけw【Unityコラム】番外編:配列とリストとメモリーの話 https://t.co/uOhT5KT6jJ @YouTubeより pic.twitter.com/vsj7ObLZKH— 徒ちゃん(キャットカベツ) (@catcubbage) June 9, 2021
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OnTriggerEnter2Dが呼ばれないときの対処法

はじめに いくつかサイトに記載している呼ばれなかったときの対処法を試してみたのですが、確認すべきポイントが他にも見つかったので記事にしてまとめます。 これはOnTriggerEnter2Dのメソッドにかかわらず、OnTrigger2D系メソッド(OnTriggerEnter2D(), OnTriggerStay2D(), OnTriggerExit2D())すべてに当てはまります。 確認すべきポイント GameObjectにCollider 2Dがアタッチされてるか 接触するGameObjectのどちらかにRigidbody2Dのコンポーネントがアタッチされてるか 接触するGameObject両方のLayerがLayer Collision Matrixに適切なチェックが入っているか Edit -> Project Settings -> Physics2D -> Layer Collision Matrixから確認 OnTrigger2Dメソッドを含むGameObjectの2DColliderにisTriggerのチェックが入っているかどうか Rigidbody2DのSimulatedにチェックが入っているか 私はこの「Rigidbody2DのSimulatedにチェックが入っているか」が入っておらず、OnTrigger2D系のメソッドが呼ばれませんでした。 上4つの項目に関しては、いくつかの記事で書かれていたのですが、5つ目に関しては、ほとんどの記事で書かれておらず、引っかかってしまいました。 それぞれのポイントの詳細 GameObjectにCollider 2Dがアタッチされてるか Collider 2DとはBox Collider 2DやCapsule Collider 2Dなどのことを指しています。 Colliderのコンポーネントはいくつか存在していますが、OnTrigger2D系メソッドの呼び出しに対応しているのはCollider 2Dのコンポーネントしか対応していないので注意してください。 Collider 2Dの詳細は以下のURLを参考にしてください。 Collider 2D - Unity マニュアル 接触するGameObjectのどちらかにRigidbody2Dのコンポーネントがアタッチされてるか タイトルの通り、OnTrigger2D系のメソッドを呼び出したいのであれば、接触するGameObjectのどちらかにRigidbody2Dのコンポーネントがアタッチされてるかを確認してください。 これも、OnTrigger2D系のメソッドを呼び出したい場合は、Rigidbody2Dコンポーネントを使用してください。 Rigidbodyコンポーネントだと呼ばれないので気を付けてください。 Rigidbody 2D - Unity マニュアル 接触するGameObject両方のLayerがLayer Collision Matrixに適切なチェックが入っているか 接触するGameObjectのLayerそれぞれが何か確認してください。 もし、確認したLayerそれぞれがLayer Collision Matrixでのそれぞれに対応していない場合は、GameObjectのLayerを変更するか、それぞれのLayerと交差している部分にチェックを入れてください。 Layer Collision Matrixは Edit -> Project Settings -> Physics2D -> Layer Collision Matrix から確認できます。 Layer Collision Matrix 例えば、Layer Collision Matrixが以下のようになっていた場合、GameObjectのLayerがDefaultとUIの接触判定は行われません。 レイヤーベースの衝突検出 - Unity マニュアル OnTrigger2D系メソッドを含むGameObjectの2DColliderにisTriggerのチェックが入っているかどうか 以下の画像のようにOnTrigger系メソッドを呼び出すGameObjectの2DColliderにisTriggerのチェックが入っているか確認してください。 Rigidbody2DのSimulatedにチェックが入っているか アタッチしているRigidbody2DのコンポーネントのSimulatedにチェックが入っているかどうか確認してください。 このチェックが入っていないとOnTrigger2D系のメソッドは呼ばれません。 Rigidbody2Dのスクリプトリファレンスにも書いているのですが、 Simulated を有効に (ボックスをチェック) すると、ランタイムに、Rigidbody 2D とそれにアタッチされたすべての Collider 2D と Joint 2D を物理的シミュレーションと相互作用させます。無効にすると、これらのコンポーネントはシミュレーションと相互作用しません。 また、こちらでは、Simulationのチェックを入れた場合と外した場合の発生内容が詳しく書かれています。 これらから、このチェックを外すと物理シミュレーションがされないのでそもそも接触という事象が起きません。 なので、OnTrigger2D系のメソッドが呼ばれなくなります。 このチェックを外すと物理シミュレーションが無効になるのでその場で固定されるので、RigidbodyのUse Gravityと同じ効果かと勘違いしてしまう可能性があります。しかし、このSimulatedは物理シミュレーション自体がされないので重力のオンオフと同じ意味ではなさそうです。 もし、重力シミュレーションをオフにしたように表現したい場合は、Rigidbody2DのBody TypeをKinematicに変更するか、Freeze Positionにチェックを入れることで固定することが可能なので、そのような方法で固定してください。 Rigidbody2Dのスクリプトリファレンス
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでHRTF Audioを使う話

はじめに Unityにはデフォルト搭載のAudio Sourceに3DSound (カメラと音源の座標から、どこから鳴っているかをシミュレートする機能)が存在します。 しかし、デフォルト3DSoundはあくまでもカメラ座標と音源座標しか反映されません。 (Audio屋ではないので詳しくは存じませんが) 人間の耳は同じ音でも前からなのか、後ろからなのか、上か下かを聞き分ける能力があります。 耳たぶの存在意義はまさにこのためであり、耳たぶが無いと音の方向が分からないらしい・・・? なんでも、後方から鳴る音は少しくぐもって聞こえるとか・・・? 一言でいうと「ASMR」です。 Unityデフォルトの3DSoundはこの「方向による音のくぐもり」を再現できません。 そこで、様々なプラットフォームが「音のくぐもり」をシミュレートするための Audio Spatializerプラグインを公開しています。 今回はそのうちからGitHub等で公開されているオープンソースの物をいくつか紹介します。 TL;DR ・UnityでAudio Spatializerを使うのはそこそこ簡単 ・Unity公式、Unity派生、Steam Audioなど結構存在する ・SOFAデータを読み込めるものが多い気がする ・現状、公式でiOS対応しているSpatializerは存在しなさそう 各種プラグイン紹介 Unity公式・Native Audio Plugin 我らがUnity公式が提供しているAudio関連プラグイン郡のNative Audio Pluginです。 この中の1機能としてSpatializerが存在します。 他にも使い勝手の良さそうな機能が多数ありますが、 このプラグイン紹介のみで1本記事が書けるレベルなので、 今回はSpatializerのみ扱います。 特徴 Nativeの名の通り、プラグインの実体はC++のコードです。(C++から)逃げちゃダメだ Unityで実際に使う際はDLLを呼んでくる形になりますが、 オープンソースなのでC++コードに自分で改変を加えて使うことももちろん可能です。 手元でリビルドすることも簡単でした。 Unity公式によるとこのプラグインはあくまでも試験的なもので、 エディタ・Windows Standalone(86/64)用のDLLしか存在しないようです。 ただ、ざっと実装を見た所Windows専用APIなども叩いていないようなので、 各プラットフォームごとにインターフェイスを用意してあげれば AndroidやMacOSからでも利用できそうな気配がします。 HRTFのデータはKEMARダミーヘッドです。 MITが公開しているデータをそのまま活用しているようです。 (ただ、これに限りませんがHRTFデータのバイトオーダーがリトルだかビッグだか OSによって読み込み結果が異なってくる箇所が存在しそうな記述をどっかで読んだ。 めちゃくちゃうろ覚え、勘違いだったらいいな) HRTF実装 実際に音源に対してエフェクトを掛けているスクリプトがPlugin_Spatializer.cppです。 実装を読んでみてもWindowsでないと動かないようなコードは見当たらないので、 iOS向けに呼び出しインターフェイスと、Xcodeを介してプラグインのリビルドをしてあげれば 動きそうな気配を感じます。 一番重要なコードが55行目辺りで、 Plugin_Spatializer.cpp extern float hrtfSrcData[]; 40行くらい中略 public: HRTFData() { float* p = hrtfSrcData; for (int c = 0; c < 2; c++) { for (int e = 0; e < 14; e++) { 中略 for (int a = 0; a < coeffs.numangles; a++) { 中略 for (int n = 0; n < HRTFLEN; n++) h[n + HRTFLEN].re = p[n];              中略 for (int n = 0; n < HRTFLEN * 2; n++) { 中略 } } } } } }; HRTFDataの中で位相をいじっていそうな気配がします。 C++はからっきしなので、それぞれのコードがどんな処理をしてるのかはまるで分かりませんが、 hrtfSrcDataの中身によって処理を走らせていることだけは分かります。 コードの先頭を見るとexternが付いており、 データそのものはhrtftable.cppと別ファイルになっています。 そのhrtftableですが、なんと文字列なのに8MBと超絶巨大ファイルです。 中身は5730行の配列データとなっており、GitHub上ですらRawDataを見ることができません。 こちらがそのコードです。(View raw押すとダウンロード始まるから非推奨) ざっと読んでみた所、どうも1700行ごとくらいに1ブロックを構成しているようで 耳・音源の各角度が6,7度ずつに周波数補正の数値が入っているように見えます。 冒頭には配列がどのような順番で置かれているかの実装がコメントアウトで残されており struct Channels // left and right ears { struct Elevations // 14 elevation angles from -90..40 degrees { float numangles; // small integer stored as float float angles[numangles]; struct Coefficients { float impulse[HRTFLEN]; } coeffs[numangles]; } elevations[14]; } channels[2]; C++配列の読み方は分からないので推測になりますが、それぞれ channels[2]=左右耳 elevations[14]=耳と音源の高低差 (角度のはずだけど、なんでfrom -90..40 degreesっていう微妙な数字なのかは不明) coeffs[numangles]=係数(MATLAB側の用語らしい、角度の整数がfloat扱いで収められている?) impulse[HRTFLEN]=周波数補正用floatの実体データ こんな感じでしょうか。 これらが累乗で増えていくので、5700行とかいうクソデカ配列になるのだろうと思います。 恐らくオーディオエンジニアの方なら それぞれどのようなデータがどの順番で並んでいるかが読めると思われますので、 任意のダミーヘッドマイクの数値で書き換えることが可能です。(現実的にやるかはともかく) また、このhrtftable.cppを読んでいるPlugin_Spatializer.cppでの参照は public: HRTFData() { float* p = hrtfSrcData; メソッドの一番冒頭でextern配列をロードしているだけなので、ここの参照を書き換えれば 該当配列以外の外部ファイルから読んでくることも可能だと思われます。 詳しくは後述しますが、 この部分を書き換えてsofa形式のデータを読み込めるプラグインも存在します。 Unityでの使い方 Unityからの呼び方は極めて簡単で、Edit/Project Settings/Audioから Spatializer PluginドロップダウンをDemo Spatializerに指定するだけです。 (なんでそんな貧相な名前なのかは不明、テスト用途だから?) ドロップダウンを指定するとAudio Sourceコンポーネントに新たにフラグが追加されます。 Spatialize がその音源をSpatializer経由で再生するかどうか、 Spatialize Post Effects は同様に コンポーネントによるポストエフェクトをSpatializer経由させるかのフラグになります。 コンポーネントによるポストエフェクトとは Audio Echo FilterコンポーネントやAudio Chorus Filterコンポーネントが該当します。 Audio Mixerによるポストエフェクトには発動しません。 恐らく処理順序が Audio Source > Spatializer Plugin > Audio Mixer になっているからだと思います。 Spatialize以外のパラメータに変化はありません。 3D Sound Settingsもほぼ関係ありません。既存3D Sound Settingsは音量調整のみに作用します。 (流石に完全2D音源はSpatializeも無効化されます。 BGMとかの聞こえ方を調整する際はおとなしくFilterコンポーネントを使いましょう。) 懸念ポイント このプラグインには既知の問題点が複数存在します。 1.初期状態ではKEMAR以外のダミーヘッドが扱えない 問題点というか、試験用プラグインである以上仕方ない点ではありますが、 音の表現幅に限界があります。 KEMARは日本人平均の頭部形状と違うんでしょうか(よく知りません) この点は次の紹介するプラグインを使うことで回避できます。 とりあえずSpatializerを使ってみたい!という方はこのプラグインが一番お手軽なはずなので、 表現幅を追求したい方は以降に紹介するプラグインに乗り換えていくのが良いと思います。 2.Windows以外のOSに対応していない こちらは恐らく頑張れば回避可能な点です。 公式提供の状態ではDLLの形で配布されているので、 DLLの対象となっているx86,x86-64環境でのみ動きます。 実装を読む限りOS機能を使った複雑な実装などはしていないように見えるので、 適切にインターフェイスを組んであげればAndroidやiOSでも動かせる気がします。 実際にNative Audio PluginをiOS対応させた方がいらっしゃるので、 この記事に従って実装していけばiOS系も、応用すればAndroidにも対応できるはずです。 (自分はMac環境がないので未検証です、ごめんね) 3.Z軸回転に対応していない(恐らくX軸も) これは公式ドキュメントでも案内されている既知の問題点です。 どうやらこのPluginではカメラ座標と音源座標からのみエフェクトを掛けているようで、 Unity空間Z軸回転(首をかしげる動作)によるズレは表現されません。 また、同様にX軸回転(うなづく動作)も表現されていないように感じられます。 (耳がガバガバなので分かりませんが、実装的にXも取ってないと思います。) 恐らくプラグインを噛ませる際にPosition情報のみを使い、 Rotation情報を参照していないからだと思います。 エフェクトを掛けている箇所は判明しているので、 Position情報を渡す前にRotationを計算してあげれば対処可能です。 (簡単に解決可能であると思われます。って公式ドキュメントにも書いてあるし) そんなに問題か? 通常の平面ゲームではカメラがかしげる動きはほとんど実装されないはずなので (そんな回転したら絶対酔う)さほど問題にはなりません。 しかし、VRゲームやARアプリケーションの場合は人間が操作する以上 ありとあらゆる首の動きが発生します。 当然かしげますし、うなづきます。 どのくらい聞こえ方が変わってくるのかはよく知りませんが、 VR/AR環境で使う際は注意が必要です。 SOFAlizer-for-Unity SOFAフォーマットを策定したSofaconventionsが公式提供するSOFAlizer-for-Unityです。 前述のNative Audio Pluginで述べたSOFAデータを読み込めるプラグインがこちらです。 中身の実装としてはほとんどNative Audio Pluginと同じ、というかほぼForkプロジェクトで Spatializer Pluginに関係ない部分のソースコードがそのままリポジトリに存在します。 (コミットメッセージがfirst commit, no changes towards SOFA yetになっててちょっとおもしろい) 特徴 Native Audio Pluginの問題点1.で述べた、 KEMAR以外のダミーヘッドが使えない問題を解消できるプラグインで SOFA形式であればどこから持ってきたものでも使うことができます。 特徴はほとんどNative Audio Pluginと同じで、実装はC++のオープンソースで いくらでも自分で改良を加えることができます。 ただ、後述しますがこちらのプラグインを他OSに対応させることは少し厳しい気がします。 HRTF実装 こちらの実装もPlugin_Spatializer.cppを読めば分かります。 が、Native Audio Pluginに比べて実装が大きく変わっており、 SOFAデータの仕様も理解しないと読めなさそうな気配を感じたので、完全理解は諦めました。 一応重要そうなメソッドとして void LoadSOFAs(UnityAudioEffectState* state) 中略 void UnloadSOFAs(void) といったメソッドが70行目、200行目に見えています。 せっかく規格が決まっているので、おとなしく提供されているものを使う くらいの姿勢が良いと思います。(敗北宣言) Unityでの使い方 Native Audio Pluginと同様にSpatializer Pluginドロップダウンを指定するだけで 使えるようになります。 Audio Sourceに出現するフラグも全く同じです。 同じリポジトリから派生したプラグインなので、当然といえば当然ですね。 悔しいのでUnity側のC#実装も一応読んでおきましょう。 公式サンプルとしてあるコードでSpatializerUserParams.csというコードがあります。 Spatializer Pluginを導入することによってAudio Sourceクラスに追加されるメソッドで SetSpatializerFloatというものがあります。 これはDLL側に任意のデータを転送するためのメソッドで 第一引数がintでindex、第二引数がfloatでvalueです。 source.SetSpatializerFloat(0, DistanceAttn); source.SetSpatializerFloat(1, FixedVolume); source.SetSpatializerFloat(2, CustomRolloff); source.SetSpatializerFloat(3, SOFASelector); Update内にこういったコードが存在し、 どのSOFAデータを参照するかを最後の行で指定しているようです。 このプラグインでは同時に10個のSOFAデータを保持しておくことが可能で、 プロジェクトのRootフォルダ(Assetsと同じ階層、プロジェクト内ではないので注意)に hrtfX.sofaのXを0~9の数字に書き換えた名称で配置することでロード可能になります。 SetSpatializerFloatで渡しているSOFASelectorはそのXの値のintです。 再ビルドをせず10個のSOFAデータを差し替えられる利点として考えられそうなのは、 例えば体験会などで複数人を対象に実行する際に アプリケーションを終了せず次体験者に備えることができる、 などでしょうか。(ニッチすぎる需要) ただ同時に、プロジェクト内階層に設置しないという点が問題点にもなります。 懸念ポイント こちらはNative Audio Pluginよりも根深そうな問題があります。 1.Windows以外のOSに対応していない Native Audio Pluginとは違い、こちらはゲーム起動後にコンソール画面が一枚開きます。 Plugin_Spatializer.cppの実装を読む限り、ただの実行ログのようですが、 Windows以外のOSに対応する際は こういったデバッグ用出力コードを全て剥がさないといけません。 結構頻繁にfprintfメソッドが存在するので、注意して剥がさないとバグりそうな気配がします。 とはいえ、そのコンソール画面さえ封印できてしまえば他OSにも移植できそう、、、 そうは問屋が卸しません。 2.SOFAデータがプロジェクト内配置ではない。 これが一番致命的かもしれません。 SOFAデータへの参照を取っている箇所を見つけられていないので推測になりますが、 UnityビルドからDLLに対してSOFAデータを渡せていないんじゃないかと考えています。 Spatializer Pluginを導入することで SetSpatializerFloatというメソッドが追加されると紹介しましたが、 ここで参照を渡しているのはあくまでもどのSOFAデータを使うかのintだけであり、 SOFAデータ実体ではありません。 恐らく参照番号のみをDLLに渡し、 DLL側から相対パス参照でSOFAデータにアクセスしているのではないでしょうか。 その実装の場合、もしインターフェイスを整えてiOS,Androidから呼べるようになったとしても DLLがSOFAデータにアクセスする際に躓くことになるでしょう。 この問題は参考になりそうなブログも知見も何故か見つかっていないため、 Windows以外の環境で当プラグインを使うのは絶望的と言えます。 (需要がなさすぎるから?先駆者が少なすぎるから?) なので 以上の2点より、 このプラグインを実装を改めて他OSに対応させるという方法は取れないと考えてよいでしょう。 せっかくいいプラグインなのに、、、 issueでも投げてみます、、、? ちなみにこれまた後述しますが、他プラグインのiOS対応issueは黙殺されています。そんな殺生な Steam Audio 我らが最大ゲームプラットフォーム、SteamによるAudio Pluginです。 公式対応プラットフォームはこれが最大級で、PC/MacOS/SteamOS/Linux/Androidと モリモリの対応幅です。 開発環境サイドも強力で、Unity/UE4/FMOD Studio/C api(!?)と主流環境は網羅しています。 WwiseもComing Soonが付いており、いつか使えるようになる日が来るでしょう。 (ただ、このComing Soonかなり長い期間付いてる気がするんですが、、、) リポジトリとしても活発で、2021/06/09時点の最終更新は2021/04/13と、 そこそこ生きているプロジェクトであると言えます。 (感覚がおかしい?上2つは年単位で過去ですよ) さらにおもしろポイントとして、実装や使い方の議論がGitHubのissueより SteamCommunityのスレッドのほうが活発です。 流石はお膝元。 特徴 特徴はなんといっても、そのすさまじい機能量です。 今回はSpatializer機能にのみ絞って紹介しますが、 このプラグインだけで一本記事が書けるほど大量の機能があります。 使いこなすのも大変です。いつか書きたいね。 Spatializer機能にのみ絞ると言いましたが、 そのSpatializerの中にも今まで紹介してきた耳・音源の座標によるエフェクトや 空間反響・閉塞・大気減衰といった、リアリティあふれる音表現まで使うことができます。 その分実装選択肢と作業が増えるので大変です。(当然) このプラグインではSOFAデータを読み込むことができます。 Windows以外に対応していなかった上記SOFAlizer Unityのシンプル上位互換ですね。 HRTF実装 流石にこのクソデカプロジェクトの完全理解は無理です。 一応Spatialize用メソッドは読みましたが、これまた巨大でした。 条件分岐がとにかく多すぎる。 コードとしてはspatialize_effect.cppが該当します。 これの640行目がエフェクトを掛けているメソッドです。 /** Applies the Spatialize effect to audio flowing through an Audio Source.*/ void process(float* inBuffer, float* outBuffer, unsigned int numSamples, int inChannels, int outChannels, int samplingRate, int frameSize, unsigned int flags, UnityAudioSpatializerData* spatializerData) { 中略 // Unity provides the world-space position of the source and the listener's transform matrix. auto S = spatializerData->sourcematrix; auto L = spatializerData->listenermatrix; auto sourcePosition = convertVector(S[12], S[13], S[14]); auto directionX = L[0] * S[12] + L[4] * S[13] + L[8] * S[14] + L[12]; auto directionY = L[1] * S[12] + L[5] * S[13] + L[9] * S[14] + L[13]; auto directionZ = L[2] * S[12] + L[6] * S[13] + L[10] * S[14] + L[14]; auto lengthSquared = directionX * directionX + directionY * directionY + directionZ * directionZ; auto direction = (lengthSquared < 1e-4) ? IPLVector3{ .0f, 1.0f, .0f } : convertVector(directionX, directionY, directionZ); auto sourceAhead = unitVector(convertVector(S[8], S[9], S[10])); auto sourceUp = unitVector(convertVector(S[4], S[5], S[6])); auto sourceDirectivity = IPLDirectivity{ dipoleWeight, dipolePower, nullptr, nullptr }; 細かい検証はしていませんが、Steam Audioは前2プラグインとは違い、 頭のX軸Z軸回転まで含めて計算しているような気がします。 うなづきと首かしげに対応していないというのはやはり問題だったのでしょう。 こちらはVR対応を大々的に謳うプラグインなので、安心して使えますね。 Unityでの使い方 これまでと同様にProjectSettingsのAudioからSpatializer Pluginのドロップダウンを Steam Audio Spatializerに変更しておきましょう。 また、他のプラグインとは違いAmbisonic Decoder Pluginという物もSteamが提供していますが こちらの効果はよく分かっていません。一応選択してはいますが、、、 Steam Audioを使う場合、 まず上部メニューから Window/Steam Audio を選択し、マネージャー管理画面を開きます。 手動でSteam Audio Managerコンポーネントを設置してはいけないそうです。 このウィンドウを開くと自動的にシーンにSteam Audio Managerが追加されます。 恐らくこのオブジェクトへの参照を維持するために、Window経由で開かせているのでしょう。 その上でAudio Sourceコンポーネントを持つオブジェクトに Steam Audio Sourceコンポーネントを追加しましょう。 それぞれのパラメータをざっくりと説明しますと、 ・Direct Binaural:HRTFデータに基づいた音で鳴らすかどうか ・HRTF Interpolation:複数の音が聞こえる際、それぞれを別計算するかどうか(未検証) ・Physics Based Attenuation:物理ベースの距離減衰をするかどうか ・Direct Sound Occlusion:閉塞の計算、密室とかで音の聞こえ方を変えたい場合に使う ・Air Absorption:大気減衰させるかどうか ・Direct Mix Level:エフェクトを掛ける前の音とミックスする度合い(Dry,Wetみたいな) ・Dipole Weight:下記 ・Dipole Power:双極子音源のミックス度合い(音屋じゃないのでよく分からず) ・Reflections:音の反射 こんな感じでしょうか。 ただ単純にSpatializeだけを使うなら一番最初のDirect Binauralのみをアクティブにしておけば 問題ありません。 一応 せっかくなので他のSteam Audioコンポーネントをざっくり紹介します。 Ambisonic Audio系 ・Steam Audio Ambisonics Source:Ambisonicオーディオを使う場合に使用 Enable Binauralを有効にするとHRTFベースで、 無効にするとパンニングでAmbisonicオーディオを再生します。 ・Steam Audio Geometry ・Steam Audio Material MeshRendererを持つオブジェクトに対してGeometryコンポーネントを追加することで、 そのMeshが音を反射する壁であると宣言することができます。 (MeshRenderer持ちを子に持つ親オブジェクトでも大丈夫、その方が管理しやすそう) Materialコンポーネントは上記とセットで使い、音を反射する壁の材質を設定することができます。 プリセットで木、岩、ガラス、レンガ、コンクリート、カーペット、金属といった主流な材質を Customを選択すれば周波数ごとに減衰を設定できる任意材質まで選択できます。 実装選択肢が多すぎる。 ・Steam Audio Dynamic Object Geometry系はStaticオブジェクトに対して設定するものですが、 こちらは動的オブジェクトに対して設定するコンポーネントです。 Dynamic Objectコンポーネントと同じオブジェクトにGeometryコンポーネントも 貼り付けるのが正しい実装のようです。 またこちらはセットでRigidBodyコンポーネントも必要です。 使い方としては、部屋の壁にGeometryを、ドアにDynamicとGeometryを設定 といった感じになると思います。 更にこれらは貼り付けるだけでは発動せず、Steam Audio Windowから Pre-Export SceneやExport All Dynamic Objectsをして 予めどのオブジェクトがAmbisonic処理対象なのかを出力しなければなりません。 後ほど問題点で書きますが、使える場面が結構絞られる気がします。 Audio Bake系 ・Steam Audio Listener ・Steam Audio Probe Box ・Steam Audio Baked Static Listener Node 半径やProbe Boxを指定して、範囲内の環境音に掛かるReverbエフェクトをベイクします。 未検証ですが、範囲内で鳴ったベイク対象音源に対して 一律にReverbエフェクトを掛けるものだと思います。 使い方としては洞窟の反響音などでしょうか。 懸念ポイント 1.iOSに対応していない もうお馴染みですが、このプラグインもiOSには公式対応しておりません。 なんでこんなに徹底的に誰も対応しないんだ、iOSが何をした。 (浮かび上がる悪行の数々)(正直仕方ない気はする) 更におもしろポイントとして、 iOS対応を願うissueがGitHubに上がっていますが、 半年近く黙殺されています。 There is quite a clear lack of spatial audio tools for iOS unfortunately. ここまで徹底的に対応プラグインがないとなると、 iOSでの音出力扱いがかなり特殊なんじゃないかと思ったりします。 iOSプラグイン開発者の方がいれば助けてくださいお願いします。 2.動的生成メッシュには使いにくそう こちらはさほど問題ではないというか、高レイヤーの悩みですね。 使い方のパートでAmbisonic Audio系を紹介しましたが、 StaticでもDynamicでも、一旦Sceneに存在するMeshやオブジェクトを Steam Audio WindowからExportしなければなりません。 つまり、Scene内に既にMeshなりオブジェクトとして存在している必要があります。 何が問題かというと、近年流行りのAR/MR系の動的空間生成などには適用しにくいという点です。 iPadやiPhoneのLiDARから生成されるメッシュや、Hololensの空間検知でのメッシュは Scene内に存在するオブジェクトではなく、アプリ実行中に動的生成されるものです。 部屋を動的取得して、その部屋に対する適切なAmbisonic Audioを設定するという ロマンコンボが一切通用しないのは悲しいポイントです。 もちろんHRTFベースのポストエフェクトは掛けられるので、 プラグインまるごと使えなくなるような問題ではありません。 元々がStandaloneゲームやVRゲーム対象なので、高望みし過ぎといった所でしょうか。 懸念ポイントとしてはこの程度で、総じて使いやすく高機能でまとまったプラグインと言えます。 まとめ 近年は「グラが良いゲームはとりあえず良い」みたいな風潮があります。 PVや動画などではゲームプレイ感は伝わりにくく、ぱっと見で分かるグラの良さで 人々の興味を引いてしまうのはよく分かります。 しかし、同様に音もダイレクトに人間の耳につながるもので こちらもこだわっていく価値は十分にあると言えるでしょう。 (ただ曲が良いとかはよく聞くけど、音声処理が良いとかあんま話題に上がらない気がする) 今回はUnity向けのSpatializer Pluginを3本紹介しました。 プラットフォームは限られますが、StandaloneやAndroid向けは 少し手間を掛けるだけで音の聞こえ方を大きく改善させることができます。 (尤も、それを聞き分けられる人間はどれほどいるのかという問題はある。自分はよく分からん) 特にSteam Audioはとにかく機能が多く、VRゲーム開発にはうってつけですね。 この記事はふつーのUnityエンジニアが書いたもので、 本職のオーディオエンジニアからすると「何言ってんのか」みたいな 間違いや勘違いを堂々書いている可能性があります。 その際はぜひコメントして頂ければ、勉強した上で該当箇所を修正させていただきます。 (意訳:アプローチを教えて下さい) ここまで読んでくださってありがとうございました。 ご意見・ご感想・修正要望・Twitterでの共有などなど お待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む