- 投稿日:2020-07-02T21:25:48+09:00
Unityの新しいDIライブラリ:VContainer (0.0.2対応修正 7/4)
VContainer is 何?
タイトルの通りです。現在Unityで使えるDIライブラリにはZenject/Extenjectがありますが、それとは別の選択肢としてVContainerが作られています。なんでもZenject/Extenjectは多機能で便利なのですが、もっとシンプルな薄いDIライブラリを目指されているのだとか。作者はハダシA氏です。
https://github.com/hadashiA/VContainer
現在、開発中なのですが、0.0.1がリリースされたので早速触って見ました。
0.0.2が出ました(7/4)。InjectとIInitialize関係が更新されました。まずはZenject/Extenjectで作る
IPerson.csnamespace ZenjectSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace ZenjectSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace ZenjectSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }SayHelloInstaller.csusing Zenject; namespace ZenjectSayHello { public class SayHelloInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<IPerson>().To<Parent>().AsSingle(); Container.Bind<SayHello>().AsSingle(); } } }SayHelloMonoBehaviour.csusing UnityEngine; using Zenject; namespace ZenjectSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] private SayHello _sayHello = default; } }SayHelloMonoBehaviourは適当なGameObjectにアタッチします。SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
と出ます。こいつをVContainerで書いてみます。VContainerで書く
インストール
まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。
インストーラーを作る
最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。
LifetimeScopeを作る
Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
コード書く
コードを書いていきます。
IPerson.csnamespace VContainerSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace VContainerSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace VContainerSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }ここまではさっきと同じです。
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] public void Construct(SayHello sayHello) { } } }MonoBehaviourです。エディター上で適当なGameObjectを作って、それにアタッチします。サンプルではメソッドインジェクションがありましたので、そちらにしました。
SayHelloInstaller.csusing UnityEngine; using VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { [SerializeField] private SayHelloMonoBehaviour sayHello = default; public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); builder.RegisterComponent(sayHello); } } }キモとなる部分です。書き加えたのはInstallメソッド内の3行とsayHelloフィールド。
builder.Register<IPerson, Parent>(Lifetime.Singleton);
builder.Register<SayHello>(Lifetime.Singleton);この2行は、ParentとSayHelloをZenject/ExtenjectでいうところのBindします。
[SerializeField] private SayHelloMonoBehaviour sayHello = default;
builder.RegisterComponent(sayHello);
その次の行とsayHelloのインスタンスです。この解説の前に、まずInjectの挙動について解説します。
[Inject]の挙動
Zenject/ExtenjectだとMonoBehaviourにInjectを書いておけば、どこでもInjectしてくれました。VContainerはエディター上でGameObjectを作ってHierarchyに存在してるだけではInjectを発動してくれません。
VContainerでは、Injectは原則的にResolveするときに発火します。例えばなんらかのクラスのコンストラクタ引数になっててそこに放り込まれる時とか、IInitializableが実装されててそれが走る時とか。
DIはコンストラクタでのインジェクションが原則だと言われます。コンストラクタインジェクションはResolveされる時以外はなんらInjectされないので、それとタイミングを合わせていると考えれば納得です。[Inject]を発火させる方法
ですんで、IInitializableをSayHelloMonoBehaviourに実装してAsを使って発火するか、適当なコンストラクタにぶち込むか、なんですが(7/4追記に転記)、もっと簡単なやり方があります。それが上述の
builder.RegisterComponent(sayHello);
です。これでsayHelloインスタンスをInjectしてくれます。
※0.0.2で実装されました。0.0.1でのやり方は下の方に残しておきます。
動作
Instantiateとかはどうなんの?
試しにPrefabをInstantiateしてみましたが、Injectは発火しません。PlaceholderFactoryもありません(少なくとも現在は)。DiContainer.Injectのようなものもないようです。つまり自前でFactoryを作って、必要な依存をそこで解決しましょう。
所感
[Inject]の使い方にちょっと混乱しましたが、使えそうです。この辺りのZenject/Extenjectとの違いは軽量化のためでしょうか。
一つ興味深いのはZenject/ExtenjectにあるFactory関係の実装予定はないそうで。まぁ確かにあれはPlaceholderFactory作ってそれをSpawnerで包んで、なんか手間だなぁ、と感じたり。いまいち使いこなせてない感がありました。ですんで、多機能よりも軽量化に注力するのは大歓迎です。今後に期待です。追記(7/2)
すみません。同じProjectにあったZenjectと競合してました。名前空間を貫通してくるとは思いませんでした。IInitialize関係がこちらの追記にあったのですが、上の方にスクリプト修正の上で転記しました。
追記(7/3)
ハダシAさんに質問した所、Zenject/Extenjectのような一方的なInjectは想定しておらず、Resolveが必要とのご回答をいただきました。Zenject/Extenjectのように使っていたInjectを上述のとおり修正いたしました。
追記(7/4)
0.0.2でRegisterComponentが実装されました。この記事をみて速攻で実装してくれました。熱い&感謝です。
IInitializableを使ってResolveする方法をこちらに残しておきます。以下のようなやり方は現在不要です。ただ、基本的には以下のようにしてInjectを発火させるんだ、と思っておいた方がVContainerを扱いやすくなると思います。
ちなみにAsとやっていますが、0.0.2でbuilder.RegisterEntryPoint<Foo>(..);
が実装され、IInitializableとかITickableをまとめてやってくれるようになってます。
SayHelloMonoBehaviourにIInitializable等を実装
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour, IInitializable { [Inject] public void Construct(SayHello sayHello) { } public void Initialize() { } } }SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); builder.RegisterComponentInHierarchy<SayHelloMonoBehaviour>().As<IInitializable>(); //HierarchyにあるSayHelloMonoBehaviourのInitializeを発火。Resolveされる。 } } }IInitializableをMonoBehaviourに実装してAsで発火します。
何かのコンストラクタでResolveさせる
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] public void Construct(SayHello sayHello) { } } }SayHelloMonoBehaviourInjector.csusing VContainer.Unity; namespace VContainerSayHello { public class SayHelloMonoBehaviourInjector : IInitializable { public SayHelloMonoBehaviourInjector(SayHelloMonoBehaviour sayHelloMonoBehaviour) { } public void Initialize() { } } }SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { [SerializeField] private SayHelloMonoBehaviour sayHello = default; public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); builder.RegisterInstance(sayHello); builder.Register<SayHelloMonoBehaviourInjector>(Lifetime.Singleton).As<IInitializable>(); //SayHelloMonoBehaviourInjectorがInitializeの発火でResolveされる。 //その際、コンストラクタ引数にSayHelloMonoBehaviourがあるのでsayHelloインスタンスがResolveされる。 //builder.RegisterEntryPoint<SayHelloMonoBehaviourInjector>(Lifetime.Singleton); //で代用可能。 } } }RegisterInstanceで該当のMonoBehaviourをRegisterして、適当なコンストラクタにぶち込みます。そのときResolveされます。
IInitializableをRegisterEntryPointやAsすることで、Zenject/ExtenjectのNonLazy的に扱えます。
- 投稿日:2020-07-02T21:25:48+09:00
Unityの新しいDIライブラリ:VContainer
VContainer is 何?
タイトルの通りです。現在Unityで使えるDIライブラリにはZenject/Extenjectがありますが、それとは別の選択肢としてVContainerが作られています。なんでもZenject/Extenjectは多機能で便利なのですが、もっとシンプルな薄いDIライブラリを目指されているのだとか。作者はハダシA氏です。
https://github.com/hadashiA/VContainer
現在、開発中なのですが、0.0.1がリリースされたので早速触って見ました。まずはZenject/Extenjectで作る
IPerson.csnamespace ZenjectSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace ZenjectSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace ZenjectSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }SayHelloInstaller.csusing Zenject; namespace ZenjectSayHello { public class SayHelloInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<IPerson>().To<Parent>().AsSingle(); Container.Bind<SayHello>().AsSingle(); } } }SayHelloMonoBehaviour.csusing UnityEngine; using Zenject; namespace ZenjectSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] private SayHello _sayHello = default; } }SayHelloMonoBehaviourは適当なGameObjectにアタッチします。
SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
と出ます。こいつをVContainerで書いてみます。VContainerで書く
インストール
まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。
インストーラーを作る
最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。
LifetimeScopeを作る
Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
コード書く
あとはガリガリ書くだけです。
IPerson.csnamespace VContainerSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace VContainerSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace VContainerSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }ここまではさっきと同じです。
SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); } } }キモとなる部分です。実際に書いたのはInstallメソッド内の2行です。Interfaceの紐付けがこんな感じなんですねぇ。
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] public void Construct(SayHello sayHello) { } } }やはり適当なGameObjectにアタッチします。メソッドインジェクションがサンプルに載ってたので、とりあえずこっちにしました。
動作
所感
使い心地はZenject/Extenjectとあまり変わらない感じですね。一つ興味深いのはZenject/ExtenjectにあるFactory関係の実装予定はないそうで。まぁ確かにあれはPlaceholderFactory作ってそれをSpawnerで包んで、なんか手間だなぁ、と感じたり。いまいち使いこなせてない感がありました。ですんで、多機能よりも軽量化に注力するのは大歓迎です。今後に期待です。
追記(7/2)
すみません。同じProjectにあったZenjectと競合してました。あの子、名前空間も貫通してくるんやな。どうもメソッドインジェクションがうまく動いていない様子。どこか読み忘れてたかな。
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { private SayHello _sayHello = default; [Inject] public void Construct(SayHello sayHello) { _sayHello = sayHello; } private void Start() { Debug.Log(_sayHello == null); //Trueを出す } } }SayHelloにIInitialize(とかITickableとか)を実装すれば、とりあえずコンストラクタは走ります。
SayHello.csusing UnityEngine; using VContainer.Unity; namespace VContainerSayHello { public class SayHello : IInitializable { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } public void Initialize() { } } }SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton).As<IInitializable>(); //Initializeを実装するときはこう書く。 } } }
- 投稿日:2020-07-02T21:25:48+09:00
Unityの新しいDIライブラリ:VContainer (7/3修正)
VContainer is 何?
タイトルの通りです。現在Unityで使えるDIライブラリにはZenject/Extenjectがありますが、それとは別の選択肢としてVContainerが作られています。なんでもZenject/Extenjectは多機能で便利なのですが、もっとシンプルな薄いDIライブラリを目指されているのだとか。作者はハダシA氏です。
https://github.com/hadashiA/VContainer
現在、開発中なのですが、0.0.1がリリースされたので早速触って見ました。まずはZenject/Extenjectで作る
IPerson.csnamespace ZenjectSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace ZenjectSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace ZenjectSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }SayHelloInstaller.csusing Zenject; namespace ZenjectSayHello { public class SayHelloInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<IPerson>().To<Parent>().AsSingle(); Container.Bind<SayHello>().AsSingle(); } } }SayHelloMonoBehaviour.csusing UnityEngine; using Zenject; namespace ZenjectSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] private SayHello _sayHello = default; } }SayHelloMonoBehaviourは適当なGameObjectにアタッチします。
SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
と出ます。こいつをVContainerで書いてみます。VContainerで書く
インストール
まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。
インストーラーを作る
最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。
LifetimeScopeを作る
Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
コード書く
コードを書いていきます。
IPerson.csnamespace VContainerSayHello { public interface IPerson { string Name { get; } } }Parent.csnamespace VContainerSayHello { public class Parent : IPerson { public string Name => "親御"; } }SayHello.csusing UnityEngine; namespace VContainerSayHello { public class SayHello { public SayHello(IPerson person) { Debug.Log($"{person.Name}さんによろしく!"); } } }ここまではさっきと同じです。
Injectの扱い
SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); } } }とりあえずInstallerをこう書きます。実際に書いたのはInstallメソッド内の2行です。
次は、MonoBehaviourです。Zenject/ExtenjectだとMonoBehaviourにInjectを書いておけば、どこでもInjectしてくれました。VContainerはただHierarchyに存在してるだけではInjectを発動してくれません。
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] private SayHello _sayHello; //Injectされない [Inject] public void Construct(SayHello sayHello) { //呼ばれない } } }例えば、エディター上で適当なGameObjectを作って、上述のSayHelloMonoBehaviour.csをアタッチ。再生ボタンを押したとき、Extenject/ZenjectはInjectしてくれますが、VContainerはやってくれません。
Injectの発動条件ですが、Resolveする必要があります。例えば
SayHelloMonoBehaviourにIInitializable等を実装
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour, IInitializable { [Inject] public void Construct(SayHello sayHello) { } public void Initialize() { } } }SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); builder.RegisterComponentInHierarchy<SayHelloMonoBehaviour>().As<IInitializable>(); //HierarchyにあるSayHelloMonoBehaviourのInitializeを発火。Resolveされる。 } } }IInitializableをMonoBehaviourに実装してAsで発火します。
何かのコンストラクタでResolveさせる
SayHelloMonoBehaviour.csusing UnityEngine; using VContainer; namespace VContainerSayHello { public class SayHelloMonoBehaviour : MonoBehaviour { [Inject] public void Construct(SayHello sayHello) { } } }SayHelloMonoBehaviourInjector.csusing VContainer.Unity; namespace VContainerSayHello { public class SayHelloMonoBehaviourInjector : IInitializable { public SayHelloMonoBehaviourInjector(SayHelloMonoBehaviour sayHelloMonoBehaviour) { } public void Initialize() { } } }SayHelloInstaller.csusing VContainer; using VContainer.Unity; namespace VContainerSayHello { public sealed class SayHelloInstaller : MonoInstaller { [SerializeField] private SayHelloMonoBehaviour sayHello = default; public override void Install(IContainerBuilder builder) { builder.Register<IPerson, Parent>(Lifetime.Singleton); builder.Register<SayHello>(Lifetime.Singleton); builder.RegisterInstance(sayHello); builder.Register<SayHelloMonoBehaviourInjector>(Lifetime.Singleton).As<IInitializable>(); //SayHelloMonoBehaviourInjectorがInitializeの発火でResolveされる。 //その際、コンストラクタ引数にSayHelloMonoBehaviourがあるのでsayHelloインスタンスがResolveされる。 } } }RegisterInstanceで該当のMonoBehaviourをRegisterして、適当なコンストラクタにぶち込みます。そのときResolveされます。
IInitializableをAsすることで、Zenject/ExtenjectのNonLazy的に扱えます。Instantiateとかはどうなんの?
試しにPrefabをInstantiateしてみましたが、Injectは発火しません。PlaceholderFactoryもありません(少なくとも現在は)。DiContainer.Injectのようなものもないようです。つまり自前でFactoryを作って、必要な依存をそこで解決しましょう。
動作
所感
[Inject]の使い方にちょっと混乱しましたが、使えそうです。この辺りのZenject/Extenjectとの違いは軽量化のためでしょうか。
一つ興味深いのはZenject/ExtenjectにあるFactory関係の実装予定はないそうで。まぁ確かにあれはPlaceholderFactory作ってそれをSpawnerで包んで、なんか手間だなぁ、と感じたり。いまいち使いこなせてない感がありました。ですんで、多機能よりも軽量化に注力するのは大歓迎です。今後に期待です。追記(7/2)
すみません。同じProjectにあったZenjectと競合してました。名前空間を貫通してくるとは思いませんでした。IInitialize関係がこちらの追記にあったのですが、上の方にスクリプト修正の上で転記しました。
追記(7/3)
ハダシAさんに質問した所、Zenject/Extenjectのような一方的なInjectは想定しておらず、Resolveが必要とのご回答をいただきました。Zenject/Extenjectのように使っていたInjectを上述のとおり修正いたしました。
- 投稿日:2020-07-02T21:23:55+09:00
Unity Technologies製推論エンジン Barracudaがスゴイという話
概要
2020年5月12日のML-Agents正式版公開に伴い、
ML-Agentsで利用されている推論エンジンのBarracudaが正式公開されたのでテストした所
導入および実施がありえない程簡単だったので記事にしました実施内容
以前実施した画像分類モデルに加え、画風変換モデルをテストしました。
1.画像分類
空間内に配置した写真をUnityのカメラで撮影し、画像分類モデルで判定を行いました
結果は次のようになりました
convertible(オープンカー)
見た限り、正しく分類できていそうです。使用したモデルは次の通りです
画像分類 モデル名 VGG-19 入力 224×224×3 出力 1000 データセット ImageNet (ILSVRCA2012) 2.画風変換
画像分類同様、空間内に配置した写真をUnityのカメラで撮影し、画風変換モデルを適用したものを表示しました
画風変換 モデル名 Fast Neural Style Transfer 入力 224×224×3 出力 224×224×3 データセット COCO 2014 Training images dataset 上で示した通り、Unity上で学習済みモデルを推論できることが分かりました。
次から実施方法について解説します。導入方法
1. PackageMangerを開き、Barracudaをインストールします。
2. スクリプトを作成し、Unity.Barracudaをインポートします。
3. NNModelをPublic変数で定義します
using UnityEngine; using UnityEngine.UI; using Unity.Barracuda; // <- 2. public class Classification : MonoBehaviour { // Barracuda 推論用 public NNModel modelAsset; // <- 3. private Model m_RuntimeModel; private IWorker m_worker;4. 適当なオブジェクトにスクリプトを追加し、InspectorからONNXファイル(学習済みモデル)をアタッチします。
5. モデルをロードし、推論を実行するワーカーを作成します
void Start() { m_RuntimeModel = ModelLoader.Load(modelAsset); m_worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Compute, m_RuntimeModel); }CPU・GPUの使用はここで決定します。
Workerのタイプ6. ProjectタブからRenderTextureを作成します
7. Hierarchyからカメラを作成し、出力先を先ほど作成したRenderTextureにします
8. 作成したRenderTextureをスクリプトにアタッチし、次のようなコードで推論を実行します
Tensor input = new Tensor(targetTexture); m_worker.Execute(input); Tensor output = m_worker.PeekOutput();入力はRenderTextureから直接作成できます!!
以前実施した際には前処理が色々面倒だったのでここだけでも使う価値があると思います。9. 出力は次のような形で取り出せます
output.ToReadOnlyArray(); //floatの配列で取得する output.ToRenderTexture(outputTexture, 0, 0, 1/255f, 0, null); //RenderTextureに直接出力※Tensor及びworkerは処理が終了したタイミングで破棄する必要があります
private void Update() { Tensor input = new Tensor(inputTexture); Inference(input); input.Dispose(); //処理が終わったタイミングで破棄 } private void OnDestroy() { m_worker.Dispose(); //終了時に破棄する }まとめ
今回のポイント
- 入力が簡単
- モデルをUnity上で管理できる
- RenderTextureに直接出力できる
さすが公式の実装だけあって以前と比べてかなり簡単に実施できるようになっていました。
Unityはスマートフォンアプリへの出力にも対応しているので、ARアプリの作成等にも使っていけそうです。リンク
- 投稿日:2020-07-02T18:06:35+09:00
net::ERR_CLEARTEXT_NOT_PERMITTED
- 投稿日:2020-07-02T12:50:07+09:00
【Unity】「1 exception was raised by workers」 の直し方
どうもマカロンです。
ここではUntiyで「1 exception was raised by workers」というエラーが出てしまった人たちを助けるために私がnoteの方で書かせていただいた記事を紹介させていただきます。
Unity エラー「1 exception was raised by workers」との死闘
【完全版】続・Unity エラー「1 exception was raised by workers」との死闘
全5項目紹介していますので、エラーが出てしまっている人はぜひ確認してみてください!!
- 投稿日:2020-07-02T11:39:43+09:00
VIVE CosmosでSRWorksを使う
VIVE ProでARコンテンツを作成するために導入されていたSRWorksというパッケージがあります
VIVE Cosmosでもversion 0.9.0.3より利用可能になっているということで導入の方法を紹介します機器概要
- Unity 2019.3.2.f1
- SteamVR Plugin 2.5.0
- windows Laptop PC
- VIVE Cosmos <導入はこちら>
準備
1. 必要ファイルのDL
先に以下のサイトからファイルをDLしておく
[DOWNLOAD] VIVE SRWorks (XR) SDK 0.9.0.3
追伸 -6/3-
SRWorksのversionが更新されました(v0.9.3.0)
Vive SRWorks SDK Release 0.9.3.0必要なファイルは以下の3つ
- SRWorks (XR) SDK Unity Plugin (zip形式)
- SRWorks (XR) SDK Unity Samples (zip形式)
- SRWorks Runtime (msi形式)他のファイルは導入に必要ないがサンプルなどがあるので各自必要な人はDLしてください
またUE4用のPluginとSampleもあるが今回はUnityで行う
2. SRWorks Runtimeをインストール
DLしたVIVE_SRWorksInstaller_0.9.0.3.msiを開く
Nextを押す
インストールする場所を決める
基本デフォルトのままでよい
EveryoneとJust meはどちらでも構わないと思うが一応適切に答えておく
Nextを押す
デバイスへの変更を許可するとインストールが始まる
完了したらCloseを押す3. zipファイルの解凍
DLしたzip形式のファイルを解凍しておく
導入手順
最初にVIVE Cosmosは接続しておく
1. projectを新規作成する
2. VIVE Cosmosの環境設定
VIVE Cosmosが利用できる環境を設定する
・SteamVR Pluginの導入 <導入の記事>3. SRWorks-Pluginを導入する
先ほどDL,解凍したファイルの中にあるVive-SRWorks-Unity-Plugin.unitypackageをUnityへimportする
importはProjectウィンドウのAssetsの中に直接ファイルをDrag&Drop
もしくはタブからAssets > Import Package > Custom Package...でファイルを選択
Import Unity Packageウィンドウが出てくるのでそのままImportする
同様のやり方でVive-SRWorks-Unity-Experience.unitypackageもImportする
※Importすると何やらwarningがたくさん出てくるが気にしなくてよい
4.XR機能の設定
File/Build Settingsを開く
Player Settingsを開く
XR Plug-in ManagementからInitialing XR on Startupの項目にチェックを入れる
AssetにXRというファイルが追加されるので確認する
5. サンプルシーンを開く
Assets/ViveSR/Scenes/ViveSR_Sampleをopenする
するとSettingsのウィンドウが出てくるのでAcceptを押す
気にせずUpdate layersをクリック
完了したら実行してみる
前面カメラを通して前方の映像が見える
またAssets/Vive_SRExperience/Scenes/Demoを実行するとより様々なデモを体験することができる
Demo1#Vive #Cosmos #SRWorks #Unity pic.twitter.com/tvDa6a0CLA
— ゆーま (@sagirin262) July 2, 2020
Demo2#Vive #Cosmos #SRWorks #Unity pic.twitter.com/JTp44HcSYq
— ゆーま (@sagirin262) July 2, 2020
Demo3#Vive #Cosmos #SRWorks #Unity pic.twitter.com/29ZnMVJ3sC
— ゆーま (@sagirin262) July 2, 2020参考記事
公式ページ
- Intro To VIVE SRWorks SDK
- VIVE Pro SRWorks SDK and VIVE Audio SDKs - downloads
- VIVE SRWorksVIVE Developers
- Downloadバグ
- VIVE SRWorks 0.8.0.2 demos crash. How to fix?
- ViveSR_Sample Unity Black Screen with Vive Cosmos Elite
- v0.9.0.3_Prebuilt_Samples-Unity Visual C++ ランタイム エラー
- Runtime Error Crash in Unity
- 投稿日:2020-07-02T10:44:48+09:00
【Unity】簡単にSceneをまたいだ値の受け渡し
初めに
Scene間での値の受け渡しに関してはまだ様々なところでこっちの方がいい、あっちの方がいいなど議論されています、ですのでどれが正解なのかは各個人によって決まります。
今回はいくつかの方法をお伝えできればと思います。目次
- DontDestroyOnLoad
- PlayerPrefs
- 静的クラスまたは静的変数
DontDestroyOnLoad
UnityにはDontDestroyOnLoadという関数があります、この関数はObjectをSceneをまたいでも引き継がれるObjectにするための関数です。
このDontDestroyOnLoadなObjectに値を渡すことで次のSceneでも値を引き継ぐことができます。サンプルコード
//値を保存用 public int Score { set; get;} void Start() { //↓これを呼び出せばDontDestroyObjectにできます DontDestroyOnLoad(gameObject); }実行結果
PlayerPrefs
PlayerPrefsに関しては過去にまとめた記事がありますのでこちらを参照してください。
サンプルコード
値を保存
void Update() { if (Input.GetKeyDown(KeyCode.A)) { PlayerPrefs.SetInt("Score",int.Parse(GetComponent<InputField>().text)); SceneManager.LoadScene("2"); } }値を呼び出す
void Start() { GetComponent<Text>().text = PlayerPrefs.GetInt("Score").ToString(); }実行結果
静的クラスまたは静的変数
静的クラスなどはゲーム開始時に最初にメモリ領域を確保してゲーム終了までそのメモリ領域を解放しませんので値が常に保持されているということができます。
サンプルコード
値保存用クラス
public static class Test { public static int Score { set; get;} }値を入れる
void Update() { if (Input.GetKeyDown(KeyCode.A)) { Test.Score=int.Parse(GetComponent<InputField>().text); SceneManager.LoadScene("2"); } }値を呼び出す
void Start() { GetComponent<Text>().text = Test.Score.ToString(); }実行結果
まとめ
実行結果が少しわかりずらいのでなくてもよかったかな?って感じる…
ぶっちゃけScene間の値のやり取りって難しいですよね、今回記事にした内容以外にもいくつかやり方ありますし、そもそもScene遷移しないという選択肢もあるわけで…
とりあえず色々試して自分に一番合うものをお探しください!
- 投稿日:2020-07-02T10:44:48+09:00
【Unity】Sceneをまたいだ値の受け渡し方
初めに
Scene間での値の受け渡しに関してはまだ様々なところでこっちの方がいい、あっちの方がいいなど議論されています、ですのでどれが正解なのかは各個人によって決まります。
今回はいくつかの方法をお伝えできればと思います。目次
- DontDestroyOnLoad
- PlayerPrefs
- 静的クラスまたは静的変数
DontDestroyOnLoad
UnityにはDontDestroyOnLoadという関数があります、この関数はObjectをSceneをまたいでも引き継がれるObjectにするための関数です。
このDontDestroyOnLoadなObjectに値を渡すことで次のSceneでも値を引き継ぐことができます。サンプルコード
//値を保存用 public int Score { set; get;} void Start() { //↓これを呼び出せばDontDestroyObjectにできます DontDestroyOnLoad(gameObject); }実行結果
PlayerPrefs
PlayerPrefsに関しては過去にまとめた記事がありますのでこちらを参照してください。
サンプルコード
値を保存
void Update() { if (Input.GetKeyDown(KeyCode.A)) { PlayerPrefs.SetInt("Score",int.Parse(GetComponent<InputField>().text)); SceneManager.LoadScene("2"); } }値を呼び出す
void Start() { GetComponent<Text>().text = PlayerPrefs.GetInt("Score").ToString(); }実行結果
静的クラスまたは静的変数
静的クラスなどはゲーム開始時に最初にメモリ領域を確保してゲーム終了までそのメモリ領域を解放しませんので値が常に保持されているということができます。
サンプルコード
値保存用クラス
public static class Test { public static int Score { set; get;} }値を入れる
void Update() { if (Input.GetKeyDown(KeyCode.A)) { Test.Score=int.Parse(GetComponent<InputField>().text); SceneManager.LoadScene("2"); } }値を呼び出す
void Start() { GetComponent<Text>().text = Test.Score.ToString(); }実行結果
まとめ
実行結果が少しわかりずらいのでなくてもよかったかな?って感じる…
ぶっちゃけScene間の値のやり取りって難しいですよね、今回記事にした内容以外にもいくつかやり方ありますし、そもそもScene遷移しないという選択肢もあるわけで…
とりあえず色々試して自分に一番合うものをお探しください!
- 投稿日:2020-07-02T02:13:35+09:00
MagicLeapコントローラのイベント取得方法
公式のチュートリアルの動画だと若干コードが古かったりしたのでメモ
詳細な動作については後日追記予定
以下のボタン操作のバインディングのメモ
- バンパーボタン
- ホームボタン
- タッチパッド
- トリガー
ホームボタン押下したらアプリを閉じるようにした
最初にサンプルアプリ作ったときにボタンイベント取得方法わからんくて適当なオブジェクトにアタッチしたコンポーネントで一定時間経過したらアプリ閉じるようにしてたのは内緒using UnityEngine; using UnityEngine.XR.MagicLeap; /// <summary> /// MagicLeapコントローラ. /// </summary> public class MlController : MonoBehaviour, IController { [SerializeField] MLInput.Controller controller; void Start() { // コントローラの入力を有効にし,対応するイベントハンドラを登録する. MLInput.Start(); MLInput.OnControllerButtonDown += OnButtonDown; MLInput.OnControllerButtonUp += OnButtonUp; MLInput.OnTriggerDown += OnTriggerDown; MLInput.OnTriggerUp += OnTriggerUp; MLInput.OnControllerTouchpadGestureStart += OnTouchPadGestureStart; MLInput.OnControllerTouchpadGestureContinue += OnTouchPadGestureContinue; MLInput.OnControllerTouchpadGestureEnd += OnTouchPadGestureEnd; } void OnDestroy() { // コントローラの入力を無効にし,登録していたイベントハンドラを削除する. MLInput.Stop(); MLInput.OnControllerButtonDown -= OnButtonDown; MLInput.OnControllerButtonUp -= OnButtonUp; MLInput.OnTriggerDown -= OnTriggerDown; MLInput.OnTriggerUp -= OnTriggerUp; MLInput.OnControllerTouchpadGestureStart -= OnTouchPadGestureStart; MLInput.OnControllerTouchpadGestureContinue -= OnTouchPadGestureContinue; MLInput.OnControllerTouchpadGestureEnd -= OnTouchPadGestureEnd; } /// <summary> /// ボタン押下時の処理. /// </summary> /// <param name="controllerId"></param> /// <param name="button"></param> void OnButtonDown( byte controllerId, MLInput.Controller.Button button) { switch (button) { case MLInput.Controller.Button.Bumper: break; case MLInput.Controller.Button.HomeTap: Application.Quit(); break; } } /// <summary> /// ボタン押上時の処理. /// </summary> /// <param name="controllerId"></param> /// <param name="button"></param> void OnButtonUp( byte controllerId, MLInput.Controller.Button button) { switch (button) { case MLInput.Controller.Button.Bumper: break; case MLInput.Controller.Button.HomeTap: break; } } /// <summary> /// トリガーの押下処理. /// </summary> /// <param name="controllerId"></param> /// <param name="value"></param> void OnTriggerDown( byte controllerId, float value) { } /// <summary> /// トリガーの押上処理. /// </summary> /// <param name="controllerId"></param> /// <param name="value"></param> void OnTriggerUp( byte controllerId, float value) { } /// <summary> /// タッチパッドのジェスチャー始点. /// </summary> /// <param name="controllerId"></param> /// <param name="gesture"></param> void OnTouchPadGestureStart( byte controllerId, MLInput.Controller.TouchpadGesture gesture) { } /// <summary> /// タッチパッドのジェスチャー操作中. /// </summary> /// <param name="controllerId"></param> /// <param name="gesture"></param> void OnTouchPadGestureContinue( byte controllerId, MLInput.Controller.TouchpadGesture gesture) { } /// <summary> /// タッチパッドのジェスチャ終点. /// </summary> /// <param name="controllerId"></param> /// <param name="gesture"></param> void OnTouchPadGestureEnd( byte controllerId, MLInput.Controller.TouchpadGesture gesture) { } }
- 投稿日:2020-07-02T01:04:13+09:00
DOTween入門してみた ~1日目~
はじめに
いい感じにアニメーションできるという噂を耳にしDOTweenに入門してみました。
尚2日目以降の記事の予定はありません。
DOTweenとは何か、入手方法、基本的な機能などは、わかりやすく解説してくれている記事がたくさんあるのでこの記事では触れません。
よさげな参考文献は一番下に記載しておきます。動作環境
・Windows10 Home
・Unity2019.4.0f1
・DOTween 1.2.335Sequenceの無限ループさせても、gameObjectが消えるときにSequenceをKillさせたい
SequenceのSetloop(-1)により無限ループにさせると、gameObjectがdestroyされてもSequenceは実行され続けます。
そして実行を止めると「Tweenさせるオブジェクトがないよ」と警告が...
この警告はDOTweenがいい感じに処理してくれているもので、設定からsafeModeのチェックを外すと、警告だったものが大量のエラーになるはずです。
↓safeModeをONにしたまま、警告を消すこともできます。
https://kan-kikuchi.hatenablog.com/entry/DOTween_Safe_Modeしかし、できれば警告すら起きないような実装にしたい...と思いまして。
SetLink(gameObject)を利用する ※2020/07/03追記
もうこれでいいです。
SetLink(gameObject)で、gameObjectがDestroyされるときに自動でKillしてくれます。以下蛇足の3つの方法です。
Sequenceのフィールドを用意し、OnDestroyでKill
この実装の欠点。フィールドとしてSequenceを記憶しなければならないこと。
private Sequence currentSequence; private void OnDestroy() { this.currentSequence.Kill(); }Sequence.OnUpdateでdestroyされていたらKill
Sequence sequence = DOTween.Sequence(); sequence. OnUpdate(() => { if (this == null) sequence.Kill(); }) .SetLoops(-1) /*以下sequenceの設定をしていく*/ .Play();よし。これで無駄なフィールドを用意しなくて済みます。
動かしてみると...ちゃんとSequenceがKillされていることが確認できます。
しかし、先ほど同様警告が(今回はひとつだけ)出てしまいます。
safeModeの力を借りれば、この実装でもいいかもしれませんね。OnDestroyで呼ぶUnityEventに代入
Sequenceというフィールドを用意するから邪魔なのであって、OnDestroyの際に呼ぶ処理を用意したら、邪魔にならないはず。
結局フィールドを用意することに変わりないのですが...。
private UnityEvent onDestroy = new UnityEvent(); //どこかの関数内------------------- Sequence sequence = DOTween.Sequence(); sequence. .SetLoops(-1) /*以下sequenceの設定をしていく*/ .Play(); this.onDestroy.AddListener(()=>sequence.Kill()); //-------------------------------- private void OnDestroy() { this.onDestroy.Invoke(); }余談(?) Sequenceではなく、直接呼んでも警告でた
DOTweenの記事をいろいろと漁っていたのですが、「SequenceでSetLoops(-1)の無限ループをすると、手動でKillしないといけない」みたいな話が多く、「問題解決のためには、Sequenceを使わないで、SetLoops(-1)にしよう」のような話も多くありました。
しかしですね、例えば以下のような処理をしたとしても、safeMode外したらバリバリエラー出ます...。
状態としてはOnUpdateでKillした3つ目の実装と同じですかね。this.transform.DOBlendableRotateBy(this.transform.right * 200f, 3f, RotateMode.WorldAxisAdd) .SetLoops(-1)というわけで3つ目のUnityEventを使った方法で、無事解決(サンプルとして無限に回転させてみました)
Tweener tweener = this.transform.DOBlendableRotateBy(this.transform.right * 200f, 3f, RotateMode.WorldAxisAdd) .SetLoops(-1) this.onDestroy.AddListener(() => tweener.Kill());Sequenceが1周するごとに処理をしたい
OnStepComplete()でできます。
Appendなどで複数まとめたときは、ひとまとまり全て終わった段階で呼ばれます。前回のSequenceから初期化したくない
通常はひとつのSequenceが終了しループするたびに、開始時の状態に戻ります。
戻したくないときは、SetLoops(n, LoopType.Incremental)とする。ここが不便だよDOTween
触っていくと、いろいろと不便に感じる点があったので書いていきます。
私もまだDOTweenを触ったばかりなので、「こんな実装方法があるよ」等あればぜひぜひ教えてほしいです泣LoopType.Incrementalにしても、rotationは初期化される
前回のSequenceの終わったときの状態を考慮する、つまりSequenceループのたびに初期化をおこなわないというものですが、この効果が出るのはどうもpositionであり、rotationは初期化されてしまうんですよね...。
どぉ~してぇ~ループ終わりのOnStepCompleteはあっても、ループ始まりのOnStepStartはない ※2020/07/03修正
AppendCallBackを使いましょう。
アニメーションの任意のタイミングにコールバックを加えることができます。便利!OnStepCompleteはループ終了後の初期化処理がおこなわれたあとに呼ばれる
例えば以下のような処理があったとき、Debug.Log()で何が出力されるでしょうか。
これはループ終了時のpositionではなく、ループ開始前のpositionの値になります。
Debug.Log(Time.time)にしてみると、ちゃんと時間経過しているのがわかるため、OnStepCompleteはループ終了後の初期化処理が終わってから呼ばれていると推測できます。
これとrotationが初期化される点で、複雑な回転ループは難しいと感じます...Sequence sequence = DOTween.Sequence() .Append(this.transform.DOBlendableLocalMoveBy(Vector3.right * 10f, 1f)) .OnStepComplete(() => Debug.Log(this.transform.position)) .SetLoops(-1) .Play();Pause中はOnUpdateが呼ばれない
逆に呼ばれたら困ることのほうが多いので、これは助かりますが。
呼ばれないので、Update内で処理しましょう実装例
といってもこの手の記事は、もっと優秀なものが沢山あるので少しだけです。
特定の垂直平面に沿った長方形周回移動
/// <summary> /// 特定の垂直平面において(反)時計回りに長方形移動し続ける /// </summary> /// <param name="horizontalDirection">水平方向移動量</param> /// <param name="verticalLength">垂直方向移動量(正のとき初期位置から上がって下がる)</param> /// <param name="oneRoutineTime">長方形を一周する時間</param> private void MoveSquareSample(Vector3 horizontalDirection, float verticalLength, float oneRoutineTime) { if (horizontalDirection.y != 0) throw new System.Exception(); float horizontalLength = horizontalDirection.magnitude; float horizontalTime = oneRoutineTime * (horizontalLength / (2 * horizontalLength + 2 * verticalLength)); float verticalTime = oneRoutineTime * (verticalLength / (2 * horizontalLength + 2 * verticalLength)); Sequence sequence = DOTween.Sequence(); sequence .OnStepComplete(() => { Debug.Log("1ループ終了"); }) .Append(this.transform.DOBlendableLocalMoveBy(Vector3.up * verticalLength, verticalTime)) .Append(this.transform.DOBlendableLocalMoveBy(horizontalDirection, horizontalTime)) .Append(this.transform.DOBlendableLocalMoveBy(Vector3.down * verticalLength, verticalTime)) .Append(this.transform.DOBlendableLocalMoveBy(horizontalDirection * -1, horizontalTime)) .SetRelative() .SetLoops(-1) .SetLink(this.gameObject) .Play(); }きりもみ回転
ここまでくると、もはやDOTweenを使う意味が...はい
あくまでなんちゃって。実用性はないですね/// <summary> /// きりもみ回転 /// </summary> /// <param name="durationOfRotationUpAxis">transform.upを軸とした1回転の時間</param> /// <param name="durationOfRotationHorizontalAxis">horizontalAxisを軸とした1回転の時間</param> /// <param name="horizontalAxis">水平方向の回転軸。yは0</param> private void RotationSample(float durationOfRotationUpAxis, float durationOfRotationHorizontalAxis, Vector3 horizontalAxis) { if (horizontalAxis.y != 0f) throw new System.Exception(); horizontalAxis = horizontalAxis.normalized; Sequence sequence = DOTween.Sequence(); sequence .SetLoops(-1) .OnUpdate(() => { this.transform.RotateAround(this.transform.position, horizontalAxis, 360f * Time.deltaTime / durationOfRotationHorizontalAxis); this.transform.RotateAround(this.transform.position, this.transform.up, 360f * Time.deltaTime / durationOfRotationUpAxis); }) .SetLink(this.gameObject) .Play(); }特定の軌道を往復し続ける
this.sequence = DOTween.Sequence() .SetRelative() .SetLink(this.gameObject) .SetLoops(-1); foreach(Vector3 p in this.path.Concat(path.Reverse().Select(v => v * -1f))) { this.sequence.Append(this.transform.DOMove(p, p.magnitude / this.moveSpeed)); } this.sequence.Play();よさげな文献
最初に一通り目を通すのに良いと思います。
https://gist.github.com/anzfactory/da73149ba91626ba796d598578b163cc
https://amagamina.jp/how-to-dotween/
https://qiita.com/kagigi/items/bdf4d42835add07b0077様々なEasingのグラフが並んでいます。
https://easings.net/MoveとRotateに着目して、各種操作
https://qiita.com/BEATnonanka/items/378de2bca3c972a95399
https://qiita.com/BEATnonanka/items/b4cca6471e77466cec74Sequenceによる統合
https://qiita.com/lycoris102/items/6a9e1e39bfa69880eaba
- 投稿日:2020-07-02T00:03:38+09:00
【Unity】Assets/Plugins はもう不要?→必要(な場合もある)
はじめに
移動できないUnityアセットはアセットの審査でリジェクトされてほしい。あと勝手に
— su10@ハイパーカジュアルゲーム開発 (@su10_dev) July 1, 2020
・Assets/Scripts
・Assets/Plugins
とかに入ってくるのも同様。>・Assets/Scripts
— su10@ハイパーカジュアルゲーム開発 (@su10_dev) July 1, 2020
これは論外で、Assets/{アセット名とか}/ にすべき。
>・Assets/Plugins
これは Assets/{アセット名とか}/Plugins にすべきで、これ守ってないやつを毎回直すの面倒。
これらを守ってくれないと後から消したくなったときに苦痛(git revertはあるけど改造する場合もあるし)これをつぶやいてから「いや、
Assets/Plugins
はUnityの制限があるからしょうがなくない?」みたいなご指摘をいくつかいただいたので調べました(調べる前は「Plugins
はResources
と同じでどこにあっても・複数あっても同じ」という認識)。結論から言うとUnityの仕様で
Assets/Plugins
に配置しないといけない場合がまだあったので諦めるしかない場合もある(Unityなんとかしてくれ?)という話でした。マニュアル記載の
Assets/Plugins
の挙動マニュアルのいろんなページに散らばってるのですが、以下のそれぞれで
Assets/Plugins
に関する挙動が説明されています。Plug-ins
プラグイン をプロジェクトに追加して Unity の機能を拡張することができます。プラグインは、通常は C/C++ で作成されたネイティブの DLL です。それらは、サードパーティのコードライブラリ、システムコール、その他の Unity ビルトイン機能にアクセスできます。Unity が検出できるように、プラグインは常に Plugins というフォルダーに配置してください。
1 つの Plugins フォルダーのみを持つことができ、プロジェクトのルートに配置する必要があります。Assets フォルダー内に直接配置します。
このフォルダーがスクリプトのコンパイルに与える影響の詳細については、特殊フォルダーとスクリプトのコンパイル順 を、異なるターゲットプラットフォームのプラグインの管理については Plugin インスペクター を参照してください。
定義済みのアセンブリ
Unityは、プロジェクトフォルダー構造内のスクリプトファイルの場所に基づいて、4 つの異なるフェーズでスクリプトをコンパイルします。各フェーズのために、別々の CSharp プロジェクトファイル (.csproj) と事前定義されたアセンブリが作成されます。任意のフェーズに適当なスクリプトがない場合、Unity は対応するプロジェクトファイルやアセンブリを作成しません。
スクリプトが、異なるフェーズでコンパイルされた (つまり、別のアセンブリにある) クラスを参照する場合、コンパイル順はとても重要です。基本的なルールは、現在のフェーズ の後に コンパイルされたものは参照できないということです。現在のフェーズ、またはそれ以前のフェーズでコンパイルされたものは、すべて参照可能です。
コンパイルの各フェーズは下記のとおりです。
フェーズ アセンブリ名 スクリプトファイル 1 Assembly-CSharp-firstpass Standard Assets、Pro Standard Assets、Plugins という名のフォルダー内のランタイムスクリプト 2 Assembly-CSharp-Editor-firstpass Standard Assets、Pro Standard Assets、Plugins という名の最上位フォルダー内のあらゆる場所にある Editor という名のフォルダー内のエディタースクリプト 3 Assembly-CSharp Editor という名のフォルダーに入っていないすべてのスクリプト 4 Assembly-CSharp-Editor Editor という名のフォルダー内にある残りのすべてのスクリプト
(↑PathはAssets/MyPlugin/MyPlugin.dll
になってる)デフォルト設定
Unity は、プラグインが置かれたフォルダーに応じて、プラグインファイルのインポート設定のデフォルトを設定します。
フォルダー デフォルト設定 (省略) (省略) Assets/Plugins/iOS プラグインは iOS でのみ互換性があると判断されます。 (省略) (省略)
Unityのマニュアルではないですが、こんなブログ記事もありました。
- Unity5からは"Assets/Plugins"フォルダは不要(けいごのなんとか)
Pluginsフォルダがありますけど名残というかわかりやすいようにしているだけです。Pluginsフォルダは必要ありません。
というわけで、
Assets/Plugins
の仕様としては
- ネイティブプラグインはここに入れる必要がある
- コンパイルの順番に関係する(そもそも含まれるDLLに違いが出る)
の2点っぽいことがわかったので検証していきます。
若干古い気もしますがUnity2018.4.0f1にて行いました。
ネイティブプラグイン置き場として必須?
これについては先述の
プラグインは、通常は C/C++ で作成されたネイティブの DLL です。それらは、サードパーティのコードライブラリ、システムコール、その他の Unity ビルトイン機能にアクセスできます。Unity が検出できるように、プラグインは常に Plugins というフォルダーに配置してください。
を疑う形になりますが一応確認しておきます。インポート設定とファイルの配置を変えてiOS用にビルドしてみました。
Assets/Foo/
以下でAny Platform
の場合含まれました(!)
Assets/Plugins/iOS
以下でいずれのプラットフォームにも含めない設定にした場合含まれてません。
* * *
というわけで、マニュアルのスクショを見てもそうであるように、ネイティブプラグインファイルが
Assets/Plugins
以下である必要はなさそうです(インスペクタからの設定に依存する)。コンパイルの順番に関係する?
配置場所によって含まれるDLLが違うということで、スクリプトを
Assets/Foo/Plugins/
とAssets/Plugins/
にそれぞれ配置して、含まれるDLLを調べました。一応それぞれの
*.csproj
を覗いてみましたがインスペクタに表示されたDLL側にちゃんと含まれてました。あと当然Assets/Foo/Plugins
からAssets/Foo/
にスクリプトを移動しても結果は変わりませんでした。* * *
というわけで、
Assets/Plugins/
とそれ以外にスクリプトを配置した場合とでは違いがあることがわかりました。まとめ
結論としては
- ネイティブプラグインの置き場所として
Assets/Plugins
以下でなければならない制限はもうない- スクリプトが
Assets/Plugins
以下だとコンパイル順に影響する
Assets/Plugins/
以下ならAssembly-CSharp-firstpass.dll
に含まれる- つまり最初にコンパイルされる
ということになりました。
正直後者のメリットというかそうしなければならない理由が思い浮かばず、アセット作者の方々は前者のことを知らずに今でも惰性で
Assets/Plugins
以下に入れてる場合が多いんじゃないかなと思いました(今だとasmdef
でなんとかなるのでは??)。それと、【Unity】アプリに含めるアセットを抑えて、アプリサイズを小さくする(テラシュールブログ)によると
StreamingAssetsやPlugins/iOSやPlugins/Androidに含めたファイルは、そのまま含まれます。
とのことなので、もし
Assets/Plugins
に展開されるアセットがデモとかサンプルに画像を含みまくってたらビルド後のサイズが膨れそうなのでやめてほしいです(未検証)。* * *
いろいろ書いてきましたがアセットを実際に作ってる方のほうが詳しいと思うので、間違い等あればご指摘ください。
あとコンパイル順に関して「
Assembly-CSharp-firstpass.dll
じゃないとダメなんだぜ」というのがあればぜひ教えて下さい。