20200702のC#に関する記事は11件です。

小数部に残された末尾のゼロを除去する、カウントする

private void button_Click(object sender, EventArgs e)
{
    // 除去する
    Debug.WriteLine(truncate_trailing_zeros(1040000.00M)); // -> 1040000
    Debug.WriteLine(truncate_trailing_zeros(1050000.00M)); // -> 1050000
    Debug.WriteLine(truncate_trailing_zeros(10.0000400M)); // -> 10.00004
    Debug.WriteLine(truncate_trailing_zeros(10.0000500M)); // -> 10.00005

    // カウントする
    Debug.WriteLine(count_trailing_zeros(0.4400M)); // -> 2
    Debug.WriteLine(count_trailing_zeros(1.0000M)); // -> 4
    Debug.WriteLine(count_trailing_zeros(1.0001M)); // -> 0
    Debug.WriteLine(count_trailing_zeros(100.00M)); // -> 2
}

public static decimal truncate_trailing_zeros(decimal value)
{
    return value / 1.0000000000000000000000000000M;
}

public static int count_trailing_zeros(decimal value)
{
    return (decimal.GetBits(Math.Abs(value))[3] >> 16 & 0x0FF) - (decimal.GetBits(truncate_trailing_zeros(value))[3] >> 16 & 0x0FF);

    /*var decimal_part = decimal.GetBits(Math.Abs(value))[3] >> 16 & 0x0FF;

    var last = -1;
    for (int i = 0; i <= decimal_part; i++)
    {
        var sample = (decimal)Math.Pow(10, -i) * (decimal)Math.Pow(10, i);
        var current = decimal.GetBits(value / sample)[3] >> 16 & 0x0FF;

        if(last == current)
        {
            return i - 1;
        }
        last = current;
    }

    return decimal_part;*/
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.cs
namespace ZenjectSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace ZenjectSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace ZenjectSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}
SayHelloInstaller.cs
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            Container.Bind<IPerson>().To<Parent>().AsSingle();
            Container.Bind<SayHello>().AsSingle();
        }
    }
}
SayHelloMonoBehaviour.cs
using UnityEngine;
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject] private SayHello _sayHello = default;
    }
}

 SayHelloMonoBehaviourは適当なGameObjectにアタッチします。SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
スクリーンショット 2020-07-02 20.26.20.png
と出ます。こいつをVContainerで書いてみます。

VContainerで書く

インストール

 まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。

インストーラーを作る

 最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。

LifetimeScopeを作る

 Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
スクリーンショット 2020-07-02 21.04.50.png

コード書く

コードを書いていきます。

IPerson.cs
namespace VContainerSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace VContainerSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace VContainerSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}

ここまではさっきと同じです。

SayHelloMonoBehaviour.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }
    }
}

 MonoBehaviourです。エディター上で適当なGameObjectを作って、それにアタッチします。サンプルではメソッドインジェクションがありましたので、そちらにしました。

SayHelloInstaller.cs
using 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でのやり方は下の方に残しておきます。

動作

スクリーンショット 2020-07-03 22.15.25.png
動きました。

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.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour, IInitializable
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }

        public void Initialize()
        {
        }
    }
}
SayHelloInstaller.cs
using 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.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }
    }
}
SayHelloMonoBehaviourInjector.cs
using VContainer.Unity;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviourInjector : IInitializable
    {
        public SayHelloMonoBehaviourInjector(SayHelloMonoBehaviour sayHelloMonoBehaviour)
        {
        }

        public void Initialize()
        {
        }
    }
}
SayHelloInstaller.cs
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.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的に扱えます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.cs
namespace ZenjectSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace ZenjectSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace ZenjectSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}
SayHelloInstaller.cs
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            Container.Bind<IPerson>().To<Parent>().AsSingle();
            Container.Bind<SayHello>().AsSingle();
        }
    }
}
SayHelloMonoBehaviour.cs
using UnityEngine;
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject] private SayHello _sayHello = default;
    }
}

SayHelloMonoBehaviourは適当なGameObjectにアタッチします。
SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
スクリーンショット 2020-07-02 20.26.20.png
と出ます。こいつをVContainerで書いてみます。

VContainerで書く

インストール

まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。

インストーラーを作る

最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。

LifetimeScopeを作る

Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
スクリーンショット 2020-07-02 21.04.50.png

コード書く

あとはガリガリ書くだけです。

IPerson.cs
namespace VContainerSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace VContainerSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace VContainerSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}

ここまではさっきと同じです。

SayHelloInstaller.cs
using 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.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }
    }
}

やはり適当なGameObjectにアタッチします。メソッドインジェクションがサンプルに載ってたので、とりあえずこっちにしました。

動作

スクリーンショット 2020-07-02 20.42.35.png
動きました。

所感

 使い心地はZenject/Extenjectとあまり変わらない感じですね。一つ興味深いのはZenject/ExtenjectにあるFactory関係の実装予定はないそうで。まぁ確かにあれはPlaceholderFactory作ってそれをSpawnerで包んで、なんか手間だなぁ、と感じたり。いまいち使いこなせてない感がありました。ですんで、多機能よりも軽量化に注力するのは大歓迎です。今後に期待です。

追記(7/2)

 すみません。同じProjectにあったZenjectと競合してました。あの子、名前空間も貫通してくるんやな。どうもメソッドインジェクションがうまく動いていない様子。どこか読み忘れてたかな。

SayHelloMonoBehaviour.cs
using 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.cs
using UnityEngine;
using VContainer.Unity;

namespace VContainerSayHello
{
    public class SayHello : IInitializable
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }

        public void Initialize()
        {

        }
    }
}
SayHelloInstaller.cs
using 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を実装するときはこう書く。
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.cs
namespace ZenjectSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace ZenjectSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace ZenjectSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}
SayHelloInstaller.cs
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            Container.Bind<IPerson>().To<Parent>().AsSingle();
            Container.Bind<SayHello>().AsSingle();
        }
    }
}
SayHelloMonoBehaviour.cs
using UnityEngine;
using Zenject;
namespace ZenjectSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject] private SayHello _sayHello = default;
    }
}

SayHelloMonoBehaviourは適当なGameObjectにアタッチします。
SayHelloがResolveされるときに、コンストラクタのIPersonにParentが放り込まれ、
スクリーンショット 2020-07-02 20.26.20.png
と出ます。こいつをVContainerで書いてみます。

VContainerで書く

インストール

まずインストール方法ですが、上記Githubリンクのリリースページにunitypackageがありますので、それをダウンロード、インストールするだけでOKです。

インストーラーを作る

最初から手で書いてもいいのですが、テンプレートを自動生成してくれます。Projectの右クリックからCreate > C# Script。で、ファイル名をXXXInstallerとすれば、Installerのテンプレートを作ってくれます。

LifetimeScopeを作る

Zenject/ExtenjectのSceneContext等にあたるものがLifetimeScopeになります。作り方はHierarchyの右クリックでVContainer > LifetimeScope。で、InspectorのMonoInstallerの+ボタンをクリックすると、さっき作ったSayHellowInstallerが出てきます。指定したInstallerはLifetimeScopeGameObjectにアタッチされます。
スクリーンショット 2020-07-02 21.04.50.png

コード書く

コードを書いていきます。

IPerson.cs
namespace VContainerSayHello
{
    public interface IPerson
    {
        string Name { get; }
    }
}
Parent.cs
namespace VContainerSayHello
{
    public class Parent : IPerson
    {
        public string Name => "親御";
    }
}
SayHello.cs
using UnityEngine;
namespace VContainerSayHello
{
    public class SayHello
    {
        public SayHello(IPerson person)
        {
            Debug.Log($"{person.Name}さんによろしく!");
        }
    }
}

ここまではさっきと同じです。

Injectの扱い

SayHelloInstaller.cs
using 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.cs
using 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.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour, IInitializable
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }

        public void Initialize()
        {
        }
    }
}
SayHelloInstaller.cs
using 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.cs
using UnityEngine;
using VContainer;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviour : MonoBehaviour
    {
        [Inject]
        public void Construct(SayHello sayHello)
        {
        }
    }
}
SayHelloMonoBehaviourInjector.cs
using VContainer.Unity;

namespace VContainerSayHello
{
    public class SayHelloMonoBehaviourInjector : IInitializable
    {
        public SayHelloMonoBehaviourInjector(SayHelloMonoBehaviour sayHelloMonoBehaviour)
        {
        }

        public void Initialize()
        {
        }
    }
}
SayHelloInstaller.cs
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.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を作って、必要な依存をそこで解決しましょう。

動作

スクリーンショット 2020-07-03 22.15.25.png
動きました。(ちゃんと撮り直した)

所感

 [Inject]の使い方にちょっと混乱しましたが、使えそうです。この辺りのZenject/Extenjectとの違いは軽量化のためでしょうか。
 一つ興味深いのはZenject/ExtenjectにあるFactory関係の実装予定はないそうで。まぁ確かにあれはPlaceholderFactory作ってそれをSpawnerで包んで、なんか手間だなぁ、と感じたり。いまいち使いこなせてない感がありました。ですんで、多機能よりも軽量化に注力するのは大歓迎です。今後に期待です。

追記(7/2)

 すみません。同じProjectにあったZenjectと競合してました。名前空間を貫通してくるとは思いませんでした。IInitialize関係がこちらの追記にあったのですが、上の方にスクリプト修正の上で転記しました。

追記(7/3)

 ハダシAさんに質問した所、Zenject/Extenjectのような一方的なInjectは想定しておらず、Resolveが必要とのご回答をいただきました。Zenject/Extenjectのように使っていたInjectを上述のとおり修正いたしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity Technologies製推論エンジン Barracudaがスゴイという話

概要

2020年5月12日のML-Agents正式版公開に伴い、
ML-Agentsで利用されている推論エンジンのBarracudaが正式公開されたのでテストした所
導入および実施がありえない程簡単だったので記事にしました

Unity Technologies 公式ブログ

実施内容

以前実施した画像分類モデルに加え、画風変換モデルをテストしました。

1.画像分類

空間内に配置した写真をUnityのカメラで撮影し、画像分類モデルで判定を行いました
sc.PNG

結果は次のようになりました

malchese.PNG
Maltese dog

convertible.PNG
convertible(オープンカー)
見た限り、正しく分類できていそうです。

使用したモデルは次の通りです

画像分類
モデル名 VGG-19
入力 224×224×3
出力 1000
データセット ImageNet (ILSVRCA2012)

2.画風変換

画像分類同様、空間内に配置した写真をUnityのカメラで撮影し、画風変換モデルを適用したものを表示しました

candy.PNG
イラスト風

mosaic.PNG
ステンドグラス風
シェーダーみたいに使えたら面白そうですね

画風変換
モデル名 Fast Neural Style Transfer
入力 224×224×3
出力 224×224×3
データセット COCO 2014 Training images dataset

学習済みモデルの配布元

上で示した通り、Unity上で学習済みモデルを推論できることが分かりました。
次から実施方法について解説します。

導入方法

1. PackageMangerを開き、Barracudaをインストールします。

マネージャー.PNG
barracuda.PNG

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ファイル(学習済みモデル)をアタッチします。

barracuda.PNG

5. モデルをロードし、推論を実行するワーカーを作成します
    void Start()
    {
        m_RuntimeModel = ModelLoader.Load(modelAsset);
        m_worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Compute, m_RuntimeModel);
    }

CPU・GPUの使用はここで決定します。
Workerのタイプ

6. ProjectタブからRenderTextureを作成します

barracuda.PNG

7. Hierarchyからカメラを作成し、出力先を先ほど作成したRenderTextureにします

barracuda.PNG
barracuda.PNG

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アプリの作成等にも使っていけそうです。

リンク

公式ドキュメント
今回のコード

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EntityFrameworkを使ってDBモデルの作成(超簡易)

はじめに

EntityFrameworkを使えば、GUI操作で簡単にDBモデルが作れる。(簡単に言うとデータベースにアクセスするやつ)「CnnectionStringBuilder」っていうDBに接続するやつを使うより楽だし遥かに汎用性があって使いやすい気がする。

この記事では開発環境の構築を行っていること前提で説明を進めていくので、DB作ったりテーブル作ったりとか事前準備の手順なんて記述してない。

ちなみにEntityFrameworkとかいまだなんなのか理解してないしネットに転がってる記事難しすぎるから、簡単な操作だけ抜粋して資料っぽく説明付けていつでも思い出せるようにこの記事を作る。

※これは【超簡易】なのでスクショだらけです

環境

・VS2019
・.NetFramework 4.7.2
・SQLServer 15.0.2000.5
・EF6(Nugetからインストールするだけ)

DBモデル作成

環境構築の説明はめんどくさいので割愛して、さっそく操作説明に入ります。

①プロジェクト作成

適当にプロジェクトを作る。(画像ではコンソールアプリケーションのプロジェクト)
ソリューションエクスプローラから以下の操作をする。
・「追加」→「新しい項目」
image.png

②ADO.NET Entity Data Model選択

環境整えてるならあるはず。選択して名前決めたら「追加」を押下。
image.png

③モデルのコンテンツ選択

どうやってDBのモデルを作るか選ぶ。
「データベースからEF Designer」ってやつを選択して「次へ」を押下。
image.png

④データ接続の選択

接続するDBを選択する。(モデルを作りたいDBを選ぶ)
image.png
「新しい接続」を選択すると接続プロパティが表示される。以下を入力して「テスト接続」をクリック。
・データソース(画像の状態がデフォルト)
・サーバ名
・認証
・データベース名の選択または入力
※Windows認証の場合はこのまま進めるが、SQLServer認証の場合はユーザ名とパスワードを事前に確認しておいたほうがいい(セキュリティの設定とかも)
image.png
テスト接続が成功すると以下の画面が表示される。
image.png
テスト接続成功後、データ接続の選択画面に戻って「次へ」を押下。

⑤データベースオブジェクトと設定の選択

DBモデルとして作成したいテーブル名の横にあるチェックボックスにチェックを付ける。

「生成されたオブジェクトの名前を複数化または単数化する」と「モデルに外部キー列を含める」にもチェック。

最後に「モデル名前空間」を命名して「完了」を押下。
image.png
これでDBモデルが完成する。

⑥DBモデル完成

こんな画面が表示される。
image.png

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

n桁の有効数字に丸める

private void button_Click(object sender, EventArgs e)
{
    Debug.WriteLine(calc_significant_figures(1040000.00M, 2, 0)); // -> 1000000
    Debug.WriteLine(calc_significant_figures(1050000.00M, 2, 0)); // -> 1100000
    Debug.WriteLine(calc_significant_figures(10.0000400M, 6, 0)); // -> 10.0000
    Debug.WriteLine(calc_significant_figures(10.0000500M, 6, 0)); // -> 10.0001

    Debug.WriteLine(calc_significant_figures(1050000.00M, 2, 1)); // -> 1000000
    Debug.WriteLine(calc_significant_figures(10.0000500M, 6, 1)); // -> 10.0000

    Debug.WriteLine(calc_significant_figures(1040000.00M, 2, 2)); // -> 1100000
    Debug.WriteLine(calc_significant_figures(10.0000400M, 6, 2)); // -> 10.0001
}

static decimal calc_significant_figures(decimal value, int digits, int type)
{
    if (value == 0M)
    {
        return 0M;
    }

    var integer_part = (int)Math.Log10(Math.Abs((int)value)) + 1;

    //var decimal_part = decimal.GetBits(value)[3] >> 16 & 0x0FF;

    switch (type)
    {
        case 0:
            {
                var scale = (decimal)Math.Pow(10, integer_part);
                var result = scale * Math.Round(value / scale, digits, MidpointRounding.AwayFromZero);

                //return Math.Round(result, decimal_part);

                var decimals = digits - integer_part;
                return Math.Round(result, (decimals < 0) ? 0 : decimals);
            }
        case 1:
            {
                var scale = (decimal)Math.Pow(10, integer_part - digits);
                return scale * Math.Floor(value / scale);
            }
        case 2:
            {
                var scale = (decimal)Math.Pow(10, integer_part - digits);
                return scale * Math.Ceiling(value / scale);
            }
    }

    throw new Exception("error: calc_significant_figures");
}

有効数字 @wikipedia

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ASP.NET Coreのローカリゼーション周りのメモ

ASP.NET Coreのローカリゼーション周りのメモ

ASP.NET Core のローカリゼーション回りのソースを読んだのでメモ

調べたバージョンはASP.NET Core 3.1

IStringLocalizer, IStringLocalizerFactory あたりは今回は触れません。
ここではミドルウェア周りについて触れます

使い方

詳細はこちら に書いているので
ここではさらっと触れるのみとします。

startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //省略...

        //ローカリゼーション周りのクラスをDIコンテナに登録する。
        //.resxファイルはResourcesフォルダに配置するのでResourcesPathを変更
        services.AddLocalization(options => options.ResourcesPath = "Resources");
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        //省略...

        //ミドルウェアの登録及び、オプションの設定
        app.UseRequestLocalization(options =>
        {
            //サポートするカルチャの設定
            string[] supportedCultures = new string[] {"ja", "en"};

            options
                .AddSupportedCultures(supportedCultures)
                .AddSupportedUICultures(supportedCultures)
                .SetDefaultCulture(supportedCultures[0])
                ;
        });
        //省略...
    }
}

あとは利用側でIStringLocalizer<T> をインジェクションし、T に相当するクラス及びResources/T.ja.resx, Resources/T.en.resx を用意すればよいです。

RequestLocalizationMiddleware クラス

ApplicationBuilderExtensionsクラスのUseRequestLocalization メソッドを実行する事で登録されます。

RequestLocalizationMiddleware クラスのソースはこちら

HttpContextから カルチャーに関する情報を取得し SetCurrentThreadCultureメソッドで CultureInfo.CurrentCulture, CultureInfo.CurrentUICultureにセットしています。

カルチャーに関する情報はデフォルトでは

  1. クエリストリング
  2. Cookie
  3. Accept-Language リクエストヘッダー

の順序で解決が行われます。
これらの内容は RequestLocalizationOptions で設定を行います。

RequestLocalizationOptions クラス

ソースはこちら

上記の RequestLocalizationMiddleware でカルチャの解決に使用するオプションを定義しています。

これらのプロパティについて紹介します。

RequestCultureProviders プロパティ

HttpContext からカルチャの解決を行っているものの正体です。

デフォルトでは

  1. QueryStringRequestCultureProvider
  2. CookieRequestCultureProvider
  3. AcceptLanguageHeaderRequestCultureProvider

の3つがListの要素に設定されています。

解決に用いるキーの名前はインスタンスプロパティとして定義されているので変更することができます。
(例 CookieRequestCultureProviderのCookie名が .AspNetCore.Culture という値であり ASP.NET Core を使用している事を外部に公開していることになるので、この名前を変更したい という要望に対応することができます。 )

このRequestCultureProvidersプロパティを変更することで、解決の順序を変更することや、新しいカルチャ解決の方法を追加することも可能です
(例. HttpContext が渡されているので Claim から解決することもおそらく可能ではないかと思います。)

新しいカルチャ解決の方法を実装する際はRequestCultureProviderを継承
もしくはCustomRequestCultureProvider
に直接ラムダ式を記述すれば可能です。

AddInitialRequestCultureProvider拡張メソッドを呼ぶことで、先頭に追加することも可能です。

SupportedCultures, SupportedUICultures, DefaultRequestCulture プロパティ

サポートしているカルチャの一覧です。

DefaultRequestCulture はカルチャが解決できなかった時に使用されるデフォルトのカルチャです。

プロパティに直接セットすることも可能ですが
AddSupportedCultures, AddSupportedUICultures, SetDefaultCultureメソッドを呼ぶことでもセット可能です。
(Addという名前で始まっていますがソースを見た感じ総入れ替えが行われているように見える..?)

FallBackToParentCultures, FallBackToParentUICultures プロパティ

カルチャが見つからなかったときにフォールバックするか?の設定のようです。

例えば
- SupportedCulturesja
- リクエストから解決したカルチャがja-JP

のときにja を使うといったことが可能なようです。

RequestLocalizationMiddlewareクラスのGetCultureInfo
メソッドあたりで使われています。

終わりに

今回はカルチャの解決方法について注目してソースを確認しました。

次は利用する側(IStringLocalizer, IStringLocalizerFactory) について見てみようと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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項目紹介していますので、エラーが出てしまっている人はぜひ確認してみてください!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】簡単にSceneをまたいだ値の受け渡し

初めに

Scene間での値の受け渡しに関してはまだ様々なところでこっちの方がいい、あっちの方がいいなど議論されています、ですのでどれが正解なのかは各個人によって決まります。
今回はいくつかの方法をお伝えできればと思います。

目次

  • DontDestroyOnLoad
  • PlayerPrefs
  • 静的クラスまたは静的変数

DontDestroyOnLoad

UnityにはDontDestroyOnLoadという関数があります、この関数はObjectをSceneをまたいでも引き継がれるObjectにするための関数です。
このDontDestroyOnLoadなObjectに値を渡すことで次のSceneでも値を引き継ぐことができます。

サンプルコード

    //値を保存用
    public int Score { set; get;}

    void Start()
    {
    //↓これを呼び出せばDontDestroyObjectにできます
    DontDestroyOnLoad(gameObject);
    }

実行結果

FadeOut.gif

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();
    }

実行結果

PlayerPrefs.gif

静的クラスまたは静的変数

静的クラスなどはゲーム開始時に最初にメモリ領域を確保してゲーム終了までそのメモリ領域を解放しませんので値が常に保持されているということができます。

サンプルコード

値保存用クラス

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();
    }

実行結果

static.gif

まとめ

実行結果が少しわかりずらいのでなくてもよかったかな?って感じる…
ぶっちゃけScene間の値のやり取りって難しいですよね、今回記事にした内容以外にもいくつかやり方ありますし、そもそもScene遷移しないという選択肢もあるわけで…
とりあえず色々試して自分に一番合うものをお探しください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Sceneをまたいだ値の受け渡し方

初めに

Scene間での値の受け渡しに関してはまだ様々なところでこっちの方がいい、あっちの方がいいなど議論されています、ですのでどれが正解なのかは各個人によって決まります。
今回はいくつかの方法をお伝えできればと思います。

目次

  • DontDestroyOnLoad
  • PlayerPrefs
  • 静的クラスまたは静的変数

DontDestroyOnLoad

UnityにはDontDestroyOnLoadという関数があります、この関数はObjectをSceneをまたいでも引き継がれるObjectにするための関数です。
このDontDestroyOnLoadなObjectに値を渡すことで次のSceneでも値を引き継ぐことができます。

サンプルコード

    //値を保存用
    public int Score { set; get;}

    void Start()
    {
    //↓これを呼び出せばDontDestroyObjectにできます
    DontDestroyOnLoad(gameObject);
    }

実行結果

FadeOut.gif

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();
    }

実行結果

PlayerPrefs.gif

静的クラスまたは静的変数

静的クラスなどはゲーム開始時に最初にメモリ領域を確保してゲーム終了までそのメモリ領域を解放しませんので値が常に保持されているということができます。

サンプルコード

値保存用クラス

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();
    }

実行結果

static.gif

まとめ

実行結果が少しわかりずらいのでなくてもよかったかな?って感じる…
ぶっちゃけScene間の値のやり取りって難しいですよね、今回記事にした内容以外にもいくつかやり方ありますし、そもそもScene遷移しないという選択肢もあるわけで…
とりあえず色々試して自分に一番合うものをお探しください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む