dasukoのブログ

現役エンジニアのブログです。

【Unity】MenuItemでショーカットキーを設定する

はじめに

UnityにはMenuItemというメインメニューとインスペクターのコンテキストメニューにメニューアイテムを追加することができるAttributeがあります。

MenuItemの公式のドキュメントはこちら

UnityEditor.MenuItem - Unity スクリプトリファレンス

MenuItemの基本的な使い方

以下のようにMenuItemというAttributeを特定のメソッドに追加することにより、メインメニューやコンテキストメニューにメニューアイテムを追加できます。

    [MenuItem("CustomMenu/Do Something")]
    static void DoSomething()
    {
        Debug.Log("Do Something.");
    }

こんな感じでメニューに追加されます。

f:id:dasuko:20201030012901p:plain

これを実行すると、意図した通りにコンソールにログが出力されます。

f:id:dasuko:20201030012823p:plain

ホットキーを定義する

MenuItemは、ホットキー(ショートカットキー)を作成することもできます。

以下のような特殊文字を使用するかどうかで表記方法が異なります。

コマンド 特殊文字
cmd %
shift #
alt(option) &

特殊文字を使用する場合

上記の特殊文字を使用する場合は、以下のようにコマンド名の最後にスペースを入れ、その後にショートカットキーを指定します。

(Do SomethingなのでCommand + dにしてみました)

    [MenuItem("CustomMenu/Do Something %d")]
    static void DoSomething()
    {
        Debug.Log("Do Something.");
    }

メニューにもショートカットーキーが表示されます。

f:id:dasuko:20201030171154p:plain

Command + shift + option + dの場合はこんな感じ

f:id:dasuko:20201030171303p:plain

これらのコマンドを打つことで、特定の処理を実行することができます!

特殊文字を使用しない場合

特殊文字を使用しない場合は、コマンド名の最後にスペースを入れ、その後に_ + ショートカットキーを指定します。

    [MenuItem("CustomMenu/Do Something _d")]
    static void DoSomething()
    {
        Debug.Log("Do Something.");
    }

f:id:dasuko:20201030171701p:plain

最後に

MenuItemでメニューを拡張できる上にショートカットを指定できるのは便利ですね。

今回、特殊文字を使用しない例として、dを指定しましたが、

誤って処理を実行してしまう可能性もあるので、cmd + dとかにするのがいいかなと思います。

参考

UnityEditor.MenuItem - Unity スクリプトリファレンス

エンジニアは大きく2つのタイプに分けられるという話

はじめに

これまで10年以上プログラミングをしてきて、エンジニアには大きく2つのタイプがいるなーと感じます。

※基本的にこれらは、個人の意見であり、偏見であり、必ずしもそうという訳ではありません。

エンジニアのタイプ

それはクリエイタータイプと技術者タイプです。

※クリエイターかつ技術者というのも存在します。

※2つじゃなくて3つやないかい!という質問は受け付けておりません。

クリエイタータイプ

このタイプの人は主にBtoC事業の会社に興味を持ちがちです。

なぜなら「誰もが知っていて、誰もが使っていて楽しい使いやすいと思えるサービスを作りたい!」と思っているからです。

最高のものを作って、とにかくみんなに触ってもらいたい!と思います。

ゲーム開発者やアプリ開発者になる人が多いです。

(個人開発者とかは特にこのタイプが多いと思います。)

特徴としては、

  • とにかく時間があれば、いい物を作るために使いたい。改良したいと思う
  • リファクタリングは後からでいいよね!と割り切る
  • プログラミングの勉強の他に企画を考えたり、世の中の面白いものを触るのに時間を使う
  • 技術者タイプの人がちょっと苦手
  • 設計には拘らないが、コードフォーマットは気にしがち
  • プルリクエストのレビューをするときは、コードを見るより動作を見る(動けばオッケー)
  • ゲームをするときでも、そのゲームのどこが面白いのかを分析しがち
  • 個人開発をするときもゲームや面白いアプリを作りがち
  • チームで開発するのが好き
  • ミーティングが好き

技術者タイプ

このタイプの人はBtoC、BtoBどちらにもいます。

企業選びの際は、何を作っているかよりも重要としているポイントがあったりします。

システム開発とかツール開発が好きだったりもします。

特徴としては、

  • 新しい技術を取り入れがち
  • 企画よりもまず、設計や言語や開発環境、ライブラリ選定などに拘る
  • インデントがタブだと鳥肌がたつ
  • 時間があったら新しい技術を試したり、何かを作る
  • ゲームをするときは、この表現をするのにどのような処理をしているのかを考えがち
  • 個人開発をするときはツールやライブラリを作りがち
  • 物理・数学が得意
  • 開発をするときには、できるだけ一人の時間がほしい

技術者かつクリエイタータイプ

技術者タイプとクリエイタータイプのどちらでもある人もいます。

ゲーム開発者に多い印象です。

最後に

あくまで個人の意見であり、偏見なのであまり鵜呑みにしないでください。

クリエイタータイプの人は割と文系思考で、技術者タイプの人は理系思考って感じですかね。

【Unity】TryGetComponentが便利

はじめに

C#ではTry◯◯みたいなメソッドがあり、取得できたのみ処理したい場合に

簡潔に書くことができます。

詳しくはこちらを見てみるといいと思います。

文字列が数値を表しているかどうかを確認する方法 - C# プログラミング ガイド | Microsoft Docs

intとかlongのTryParseが代表的かなと思います。

パースに成功すればtrueが返り、失敗した場合にはfalseが返ります。

DictionaryのTryGetValueなんかも使う人が多いのではないでしょうか

docs.microsoft.com

TryGetComponentとは

公式ドキュメントはこちらです。

Component-TryGetComponent - Unity スクリプトリファレンス

公式ドキュメントの説明としてはこんな感じですね

Gets the component of the specified type, if it exists.

 

他のTry◯◯メソッドのように、GetComponentをして取得できればtrue、取得できなければfalseを返します。

使い方としては、こんな感じです。

using UnityEngine;

public class TryGetComponentTest : MonoBehaviour
{
    void Start()
    {
        if (TryGetComponent(out Rigidbody rigidbody))
        {
            // Rigidbodyが取得できた場合
            rigidbody.velocity = new Vector3(0f, 0f, 1f);
        }
    }
}

outで指定した引数に結果が帰ってきます。

以下のように、取得できなかった場合にAddComponentするような実装も考えられると思います。

using UnityEngine;

public class TryGetComponentTest : MonoBehaviour
{
    void Start()
    {
        Rigidbody rigidbody;
        if (!TryGetComponent(out rigidbody))
        {
            rigidbody = gameObject.AddComponent<Rigidbody>();
        }

        rigidbody.velocity = new Vector3(0f, 0f, 1f);

    }
}

あとは、以下のように衝突したオブジェクトがEnemyクラスの場合はダメージを与える!みたいな処理が簡潔に書けるようになりますね!

using UnityEngine;

public class Player : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.TryGetComponent(out Enemy enemy))
        {
            // 敵の場合はダメージを与える!
            enemy.Damage();
        }
    }
}

public class Enemy
{
    public void Damage()
    {
        // Damage
    }
}

まとめ

TryGetComponentは便利!という話でした。

個人的には、多くの場合そのコンポーネントが取得できることを意図しているので、

取得できなかった場合はAddComponentしてくれるような拡張メソッドを作った方が使い勝手はいいかなと思います。

参考

Component-TryGetComponent - Unity スクリプトリファレンス

文字列が数値を表しているかどうかを確認する方法 - C# プログラミング ガイド | Microsoft Docs

docs.microsoft.com

【C#】DI(Dependency Injection)を使ってみた!(Unityでの実装例あり)

はじめに

今回はDI(Dependency Injection)についてまとめてみたいと思います。

Dependency Injectionとは

Dependency Injectionの意味は依存性の注入です。

Wikipediaにはこのようにあります。

依存性の注入(いそんせいのちゅうにゅう、英: Dependency injection)とは、コンポーネント間の依存関係をプログラムのソースコードから排除するために、外部の設定ファイルなどでオブジェクトを注入できるようにするソフトウェアパターンである。英語の頭文字からDIと略される。

依存性の注入 - Wikipediaより引用

 

Dependency Injectionを使うメリットも含めて簡単に説明すると、AというクラスがBというクラスに依存している場合(AというクラスがB型のフィールドを持っている場合)、Bに変更を加えると、

Aも修正する必要があったり、Aのテストも再度する必要が出てきたりします。

また、Bというクラスのモックを使用してユニットテストをしたい!となっても置き換えることができません。

そのため、AというクラスはBというインターフェースに依存するようにします。

そして、Aを初期化するときにB'というBのインターフェースを実装したクラス型のオブジェクトを渡します。

こうすることで、AはB'に依存していないので、Bインターフェースの振る舞いを後から変更することが容易になります。

また、ユニットテストをする場合でも、モックB''を用意すれば、簡単にAのユニットテストを実行することができます。

C#Dependency Injectionを使ってみる

以下に簡単な実装例を示してみます。

LoggerのインターフェースをModelの初期化時に渡すようにしてました。

// Logger interface
public interface ILogger
{
    // 簡易的なのでログレベルはなし
    void Log(string msg);
}

// Model class
public class Model
{
    private readonly ILogger logger;

    public Model(ILogger logger)
    {
        this.logger = logger;
    }

    public void DoSomething()
    {
        logger.Log("DoSomething");
    }
}

この例だと、Modelが一つしかないので問題ないですが、

クラスが増えてくると、初期化処理を書くのが面倒になりそうです。

そのため、ライブラリを使うといいと思います。

Zenjectを使ってみる

今回はUnity環境(C#)でDI(Dependency Injection)を試して見たいのでExtenjectというライブラリを使うことにしました。

これは元々Zenjectというライブラリがあって、そのリポジトリからフォークされたものみたいです。

GitHub - svermeulen/Extenject: Dependency Injection Framework for Unity3D

MonoInstallerを追加してみる

  1. ヒエラルキータブで右クリックして、Zenject->Scene Contextを選択します

  2. 次にプロジェクトタブでフォルダを右クリックして、Create->Zenject->MonoInstallerを選択してMonoInstallerを作成します。

  3. 作成したMonoInstallerのスクリプトをシーン上のGameObjectにアタッチします。

  4. 次にSceneContextを選択し、インスペクター上でMonoInstallerプロパティのところに+ボタンで新しく行を追加し、

作成したMonoInstallerを追加します。

f:id:dasuko:20201023234109p:plain

  1. Edit->Zenject ->Validate Current Sceneを選択、もしくはControl+Alt+Vを押すことで検証を行うこともできます。

まずはValidate Current Sceneの動作も見たいので、ExtenjectのREADMEにのっていた以下のサンプルを追加してみました。

(文言は"DI Test"に変えてみました)

using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<string>().FromInstance("DI Test");
        Container.Bind<Greeter>().AsSingle().NonLazy();
    }
}

public class Greeter
{
    public Greeter(string message)
    {
        Debug.Log(message);
    }
}

Validate Current Sceneを実行しても、以下のようなログが出ていて問題なさそうですね。

f:id:dasuko:20201023235630p:plain

これで実行すると、しっかり"DI Test"というログがコンソールに出力されました!!!

f:id:dasuko:20201023235734p:plain

次にインターフェースとそれを実装したクラスを定義して、バイドしてみたいと思います。

using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<IService>()
            .To<ServiceImpl>()
            .AsSingle();
    }
}

public interface IService
{
    void DoSomething();
}

public class ServiceImpl : IService
{
    public void DoSomething()
    {
        Debug.Log("DoSomething");
    }
}

public class Model
{
    [Inject]
    private IService service;

    public void DoSomething()
    {
        service.DoSomething();
    }
}

次にMonoBehaviourを継承したクラスを作ります。

StartメソッドでDoSomethingメソッドを実行してみました。

using UnityEngine;
using Zenject;

public class DependencyInjectionTest : MonoBehaviour
{
    [Inject]
    private DiContainer container;

    void Start()
    {
        var model = container.Instantiate<Model>();
        model.DoSomething();
    }
}

結果以下のように"DoSomething"がコンソールに出力されました!!!

f:id:dasuko:20201024014457p:plain

参考

依存性の注入 - Wikipedia

ASP.NET Core での依存関係の挿入 | Microsoft Docs

GitHub - svermeulen/Extenject: Dependency Injection Framework for Unity3D

Zenject入門その1 疎結合とDI Container - Qiita

【Unity】SerializeReferenceを使ってみた

はじめに

今回はSerializeReferenceを使ってみたいと思います。

ドキュメントはこちら

Unity - Scripting API: SerializeReference

SerializeReferenceとは

フィールドをシリアル化する方法として、SerializeFieldAttributeを追加するという方法があります。

しかし、この方法でシリアル化できるのは、値型とクラスが抽象クラスやインターフェースではない参照型オブジェクトのみでした。

When Unity serializes an object, it serializes all fields as value types, unless the field type derives from [UnityEngine.Object]. By default, polymorphic fields are not supported and reference based topologies, like graphs, cannot be expressed natively.

通常、UnityEngine.Objectを継承していない限りは値としてシリアル化されるようです。

SerializeReferenceAttributeを指定することにより、本来値としてシリアル化されるフィールドを参照としてシリアル化できるというわけです。

SerializeReferenceAttributeはインターフェースや抽象クラスもシリアル化することができます。

ドキュメントにある注意事項についても引用しておきます。

  • The field type must not be of a type that specializes UnityEngine.Object.
  • The field type can be abstract.
  • The field type can be an interface.
  • Generic Lists and array fields decorated with [SerializeReference] apply the attribute to the elements of the list/array not the list/array instance itself.
  • Referenced values cannot be shared between UnityEngine.Object instances. For example, two MonoBehaviours cannot share an object that is serialized by reference. Use ScriptableObjects instead to share data.
  • Field value can be null.
  • Field value cannot be a specific specialization of a generic type(inflated type).
  • The type of the dynamic instance/object assigned to the field must be of a [Serializable] type.
  • The types 'System.Object', 'List<System.Object>' or 'System.Object[]' are also supported for the field type.

注意点として重要なのは以下だと思います。 - UnityEngine.Objectを継承したクラスであってはならない - 参照型の値はインスタンス間で共有できない -フィールドタイプは抽象クラスやインターフェースも指定可能 - System.Objectを指定することも可能 - Serializable Attribute必須

インターフェースのシリアライズ

まずはインターフェースをシリアライズしてみます。

以下のようなクラスを追加して、シリアライズされるのか確認してみました。

using UnityEngine;

public class SerializeReferenceTest : MonoBehaviour
{
    [SerializeReference]
    private ICharacter character;
}

public interface ICharacter
{
}

public class Slime : ICharacter
{
    public int HP;
    public int Attack;
    public int Defense;
}

インスペクター上でシリアライズはされてるけど、継承したクラスを実装してもクラスを選択できないみたいです。

f:id:dasuko:20201020014438p:plain

この辺はエディタ拡張で具体的なクラスを指定してあげる必要があるみたいです。

以下でエディタ拡張を実装した例が紹介されています。

【Unity】インターフェイスをSerialize出来るようにするSerializeReferenceのための表示attributeを作ってみた - Qiita

抽象クラスのシリアライズ

次は同じ実装を抽象クラスを使って実装してみました。

フィールドは抽象クラスで定義しました。

using UnityEngine;

public class SerializeReferenceTest : MonoBehaviour
{
    [SerializeReference]
    private CharacterBase character;
}

[System.Serializable]
public abstract class CharacterBase
{
    public int HP;
    public int Attack;
    public int Defense;
}

[System.Serializable]
public class Slime : CharacterBase
{
  
}

こちらもやはり同様にフィールドはシリアライズされませんでした。

f:id:dasuko:20201020014438p:plain

まとめ

参照で持てるようになったとは言え、追加実装が必要となると、まだまだ使いづらい印象です。

追加実装なしにインスペクターからクラスを選択できるような機能をUnityが提供してくれたらいいですね。

参考

Unity - Scripting API: SerializeReference

【Unity】インターフェイスをSerialize出来るようにするSerializeReferenceのための表示attributeを作ってみた - Qiita

【英語】オンライン英会話のレアジョブとネイティブキャンプを比較してみた

はじめに

筆者はこれまでオンライン英会話を継続して行っているのですが、

今までにネイティブキャンプとレアジョブ、二つのオンライン英会話を試しました。

なので、この二つについて比較して感想や特徴を簡単にまとめてみたいと思います。

アフィリエイトは一切なしです

レアジョブ

レアジョブの公式サイトはこちら

www.rarejob.com

料金プラン

※2020/10/15現在

主なプラン |プラン|一日のレッスン数|料金(月額)| |---|---|---| |日常英会話コース|1回|5800円| |ビジネス英会話コース|1回|9800円| |中学・高校生コース|1回|9800円|

一回のレッスンは25分です。

特徴や感想

ビジネス英会話や日常英会話では教材を使って学習します。

その他にもディスカッションをする教材もあり、ディスカッションでは教材はあるものの、自分で文章を組み立てて話すため、

瞬間的に話す力が鍛えられます。

時間は朝6時〜深夜の1時。

企業導入実績は2900社。

講師はフィリピンの講師です。

無料体験期間にカウンセリングを受けることができ、その時におすすめの教材や学習プランを相談できます。

レッスンは、ブラウザ上で受けるかスカイプを使うか選択できます。

フィリピン人の講師しかいませんが、講師のレベルが高い印象です。

ネイティブキャンプ

ネイティブキャンプの公式サイトはこちら

nativecamp.net

料金プラン

※2020/10/15 現在 |プラン|一日のレッスン回数|料金(月額)| |---|---|---| |プレミアムプラン|無制限|6480円|

一回のレッスンは25分です。

特徴や感想

24時間何回でもレッスンを受けられます。

講師は国籍問わず、様々な国の講師がいます。

(しかし、インターネット回線が悪かったり、雑音がひどい講師が多い印象があります。)

教材も充実しており、リーディング、リスニングの教材は受講中でなくても使えてかなり便利です。

また、ネイティブキャンプの一番の売りはカランメソッドだと思います。

カランメソッドは、ベルリッツで働いていたロビン・カランがより良いメソッドを夢見て確立したもので、母国語での助けを借りずに英語のみを用いて英語を学んでいくカランメソッドはダイレクトメソッドの一つとされています。

日本語を話せない外国人講師が英語を教える際に最も有効な教授法であるダイレクトメソッド=カランメソッドはオンライン英会話に最も適したメソッドと言うことができます。

公式サイトより引用

 

ただ、カランメソッドを受講するには、専用の教材を購入する必要があります。

それが少し難点かなと思います。

カランメソッドについてはこちらをご参考ください。

nativecamp.net

まとめ

ネイティブキャンプとレアジョブについての感想を簡単に書いてみました。

個人的な印象としては、レアジョブは一日1レッスンですが、講師の質が高く、

ネイティブキャンプは1日何回でもレッスンが受けられますが、講師の質が少し劣るので、

毎日コツコツやりたい人はレアジョブ、

毎日できないので、一日複数回レッスンを受けたいという人はネイティブキャンプがいいのかなと思います。

【Unity】EditorでCoroutineを使う方法!Editor Coroutines

はじめに

今回はEditor Coroutinesをご紹介します。

Editor拡張でCoroutine(コルーチン)を使いたい!ってことがたまにあるかと思います。(HTTPリクエストを実装するときとか)

このパッケージを使えば、Editor上で簡単にCoroutineを使うことができます!

これはUnityのPackage Managerから追加することができます。

公式ドキュメントはこちらです。

Namespace Unity.EditorCoroutines.Editor | Editor Coroutines | 1.0.0

インストール

EditorCoroutinesはPackageManagerで配布されているので、

PackageManagerを開きます。

Window>PackageManagerから開きます。

f:id:dasuko:20201014234518p:plain

次に検索窓にEditorCoroutinesと入力します。

f:id:dasuko:20201014234611p:plain

すると、Editor Coroutinesが出てくるので、選択した状態でinstallをクリックします。

インストール作業はこれだけです。

実装

今回はEditorWindowを実装してみました。

Test/Testというメニューを実行すると、ウインドウが立ち上がり、Executeボタンをクリックすると、実行されます。

まずは、簡単にyield return nullのみを実行した例を掲載します。

using System.Collections;
using UnityEngine;
using UnityEditor;
using Unity.EditorCoroutines.Editor;

public class CustomEditorWindow : EditorWindow
{
    [MenuItem("Test/Test")]
    static void OpenWindow()
    {
        GetWindow<CustomEditorWindow>().Show();
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Execute"))
        {
            EditorCoroutineUtility.StartCoroutine(DoSomething(), this);
        }   
    }

    private IEnumerator DoSomething()
    {
        yield return null;
    }
}

EditorCoroutineUtilityのStartCoroutineにコルーチンとオーナーを渡します。

StartCoroutineの定義はこんな感じ!

public static EditorCoroutine StartCoroutine (IEnumerator routine, object owner);

※EditorCoroutineExtensionsでEditorWindowの拡張メソッドとしてStartCoroutineが定義されているので、EditorCoroutineUtilityを経由する必要はありません。

詳細はこちら Class EditorWindowCoroutineExtension | Editor Coroutines | 1.0.0

  

次に、数秒待ってみる処理を書きたいと思います。

数秒待つときは、WaitForSecondsを使うと思います。

ドキュメントはこちら UnityEngine.WaitForSeconds - Unity スクリプトリファレンス

しかし、これをEdiorCoroutineで使っても1秒も待ってくれません!!!!

以下のEditorWaitForSecondsを使う必要があります。

Class EditorWaitForSeconds | Editor Coroutines | 1.0.0

では、実装サンプルです。

using System.Collections;
using UnityEngine;
using UnityEditor;
using Unity.EditorCoroutines.Editor;

public class CustomEditorWindow : EditorWindow
{
    [MenuItem("Test/Test")]
    static void OpenWindow()
    {
        GetWindow<CustomEditorWindow>().Show();
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Execute"))
        {
            this.StartCoroutine(DoSomething());
        }   
    }

    private IEnumerator DoSomething()
    {
        Debug.Log("Start");

        yield return new EditorWaitForSeconds(5f);

        Debug.Log("Done");
    }
}

Startというログを出力してから5秒後にDoneというログを出力しています。

今回は拡張メソッドの方のStartCoroutineを使いました。

※拡張メソッドなのでthisが必要です

 

これを実行してみます。

まずはTest/Testメニューを選択します。

f:id:dasuko:20201014235837p:plain

ウインドウが表示されるので、Executeボタンをクリックします。

f:id:dasuko:20201014235857p:plain

すると、以下のようにStartが出力されてから5秒後にDoneが出力されました!!! f:id:dasuko:20201015000012p:plain

参考

UnityEngine.WaitForSeconds - Unity スクリプトリファレンス

Namespace Unity.EditorCoroutines.Editor | Editor Coroutines | 1.0.0