げぇむぷろぐらみんぐ

日々の生活で得た知識、経験を書きます

【Unity】任意のコンポーネントがアタッチされたPrefab一覧を探す

はじめに

今回は、Unityで任意のコンポーネントがアタッチされたPrefab一覧を取得する方法についてまとめます。

Unity標準の機能だと、シーン上で任意のコンポーネントがアタッチされたオブジェクト一覧を探すことはできますが、Project全体から探すことはできないようでした。

そのため今回は、シェルスクリプトを用いて探す方法と、Unity上でエディタ拡張をすることによって探す方法を説明します。

今回の環境は、

です。

シェルスクリプトによる方法

探し方としては、以下のようになります。

  1. アタッチされていることを調べたいスクリプトのGUIDを調べる
  2. プロジェクト内のPrefab一覧を調べる
  3. Prefab一覧のmetaファイルで、1で調べたGUIDを持っているものがないか調べる

Unityで管理されているものにはmetaファイルが存在し、その中でGUIDというユニークなIDを持っているため、それを利用して探したいものを特定することができます。

具体的なスクリプトは以下です。

#!/bin/sh

GUID=`find ./Assets -type f -name $1.meta | xargs grep guid | sed -e 's/guid: //g'`
find ./Assets -type f -name "*.prefab" -print0 | xargs -0 grep -l $GUID

引数に渡されたファイル名のmetaファイルを探して、その中からguidが書かれている行を選択し、余計な文字列を除去してGUIDのみを取得して変数に格納します。 その後、格納したGUIDを持つPrefabを検索し、その名前を表示しています。 findとxargsにオプションがついているのは、以下の記事を参考に区切り文字をnull文字にしているからです。

qiita.com

先程のシェルスクリプトをreference.shという名前で保存して、実際に実行してみた結果が以下です。

% sh reference.sh Test.cs
./Assets/AttachedPrefab.prefab
./Assets/SubAttachedPrefab.prefab

Test.csがアタッチされているPrefabを探して、その結果としてAttachedPrefab.prefabとSubAttachedPrefab.prefabが返されました。

エディタ拡張による方法

次に、Unityエディタ上で任意のコンポーネントがアタッチされたPrefabを探す方法について説明します。

以下が指定されたコンポーネントがアタッチされているPrefab一覧を探すためのエディタ拡張のコードです。

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Sample.Editor
{
    public class ReferenceFinder : EditorWindow
    {
        private MonoScript _targetScript;
        private List<GameObject> _findAssets = new List<GameObject>();
        private Vector2 _currentScrollPosition;

        [MenuItem("Sample/ReferenceFinder")]
        private static void Open()
        {
            GetWindow<ReferenceFinder>("Reference Finder");
        }

        private void OnGUI()
        {
            EditorGUILayout.BeginHorizontal();
            _targetScript =
                EditorGUILayout.ObjectField("TargetScript", _targetScript, typeof(MonoScript), false) as MonoScript;
            EditorGUILayout.EndHorizontal();

            if (_targetScript == null || !_targetScript.GetClass().IsSubclassOf(typeof(MonoBehaviour)))
            {
                EditorGUILayout.LabelField("MonoBehaviourを継承したクラスを設定してください");
                _targetScript = null;
                return;
            }

            if (_findAssets.Count > 0)
            {
                _currentScrollPosition = EditorGUILayout.BeginScrollView(_currentScrollPosition);
                foreach (var asset in _findAssets)
                {
                    EditorGUILayout.ObjectField(asset.name, asset, typeof(GameObject), false);
                }

                EditorGUILayout.EndScrollView();
            }

            if (GUILayout.Button("Search"))
            {
                _findAssets.Clear();

                var guids = AssetDatabase.FindAssets("t:GameObject", null);


                foreach (var guid in guids)
                {
                    string path = AssetDatabase.GUIDToAssetPath(guid);
                    var loadAsset = AssetDatabase.LoadAssetAtPath<GameObject>(path);

                    if (loadAsset.GetComponent(_targetScript.GetClass()) != null)
                    {
                        _findAssets.Add(loadAsset);
                    }
                }
            }
        }
    }
}

やっていることは、

  1. 探したいコンポーネントをMonoScriptとして受け取る
  2. Searchボタンが押されたらプロジェクト内のGameObjectのGUID一覧を取得
  3. GUIDからオブジェクトをロード
  4. オブジェクトに対して任意のScriptをGetComponentして取得できるかをチェック
  5. 取得できたらリストに追加

です。

今回は、Prefabにアタッチできるスクリプトのみを指定できるようにしたかったため、MonoBehaviourを継承していないスクリプトは選択しても検索できないようにしました。

上記の拡張の結果以下のようなウィンドウで検索することができるようになりました。

f:id:siguma_sig:20181014155641p:plain

まとめ

今回は、プロジェクト内で任意のスクリプトがアタッチされたPrefabの一覧を取得する方法について説明しました。

今回の例では、Prefabの一覧のみを取得しましたが、任意のスクリプトがアタッチされたオブジェクトをシーン上に含むシーンの一覧なども今回のスクリプトを少し改造すれば作れるはずです。

なにか間違いや、もっと良い書き方があればぜひ教えてください!

Unityによる開発のときに使えるシェルスクリプト入門

はじめに

Unityで、あるスクリプトがアタッチされているPrefabをプロジェクト内からすべて見つけ出したいということがあったときに、Unityエディタの機能だと無さそうだったので先輩に相談しました。 すると、サクッとシェルスクリプトを書いて探し方を教えてくれて、かっけーとなったので僕も勉強してまとめてみました。

長くなってしまったので、今回は便利そうなコマンドのまとめだけになってしまいました。

シェルスクリプト初心者なので、もっとこう書くと簡潔だよとかあればどしどしご指摘お願いします。

使えそうなコマンド一覧

使えそうなコマンド一覧に関してどんなコマンドか紹介と、具体例を記述していきます。 コマンドの引数やオプションに取りうる詳細な情報は割愛します。

find

指定したディレクトリ以下に存在するファイルやディレクトリを再帰的に検索してくれるコマンドです。

例えば、Unityでプロジェクトのルートディレクトリにいるとして、Resourcesの中身一覧を見たくなった場合には以下のようなコマンドでできます。

% find Assets/Resources
  • ファイルだけ表示したくなった場合
% find Assets/Resources -type f
  • .prefabのみ表示したくなった場合
% find Assets/Resources -name "*.prefab"

xargs

標準入力から扱いたいものを受け取って、それを指定されたコマンドの引数に渡すためのコマンドです。 かなりわかりづらいですが、コマンドとコマンド間の仲介役のような感じです。

例えば、先程のfindと組み合わせて、Resources以下のどのディレクトリがサイズが大きいかを調べたくなったとすると以下のようなコマンドでできます。

% find  Assets/Resources -type d | xargs du

上記に関して、-typeの引数にdを指定するとディレクトリの一覧を得ることができます。それによって、得たディレクトリの一覧をディレクトリのサイズを調べるためのコマンドであるduの引数に渡しています。

この例をもとにもっと詳しく説明すると、まずはじめのfindコマンドの結果が以下のようになったとします。

% find  Assets/Resources -type d
Assets/Resources/Sprites
Assets/Resources/Animations
Assets/Resources/Font

これらのfindした結果に対して、ディレクトリのサイズを調べるためには愚直にやると以下のように一つづつ調べることになります。

% du Assets/Resources/Sprites
% du Assets/Resources/Animations
% du Assets/Resources/Font

これと同じように、xargsは標準入力から受け取った結果を直接duに渡してくれているため、一つのコマンドで完結します。

sed

与えられた文字列を指定した処理を加えて標準出力できるコマンドです。 主に読み込んだテキストであったり、入力したコマンドの結果の一部に手を加えたい場合に用います。

例えば、スクリプト内のTestという文字列をTest2に置き換えたくなった場合には以下のようなコマンドでできます。

% sed -i -e s/Test/Test2/g Hoge.cs

上記のコマンドでは、-iオプションによって直接Hoge.csの中身を書き換えています。 Macだと、上のコマンドを実行した場合は、-iのあとをバックアップの拡張子としてバックアップを保存してしまうので、Hoge.cs-eというファイルができてしまいます。 それを防ぐには、バックアップの拡張子を以下のように空にすれば良いです。

% sed -i '' -e s/Test/Test2/g Hoge.cs

また、-iでなく以下のようにすれば、書き換えた結果を他のファイルに書き込むこともできます。

% sed -e s/Test/Test2/g Test.cs

grep

与えられた文字列から正規表現に一致する行を出力できるコマンドです。 あるコマンドの結果を条件によって絞り込んで確認したい場合や、ファイル内に任意の文字列が含まれているかどうかの確認に使ったりできます。

例えば、a.txt内でTestという文字列が含まれる行を調べたい場合は以下のようなコマンドでできます。

% grep Test a.txt

また、今までのコマンド群を組み合わせて、Resourcesフォルダ内にあるPrefabのGUIDを調べるコマンドは以下のようになります。

find Assets/Resources -name "*.prefab" -type f | xargs grep guid

まとめ

よく使いそうなシェルコマンドについて調べてまとめてみました。 普段はgrepくらいしか使っていなかったのですが、これらが使いこなせるようになると簡単なファイルの一括操作などが非常に楽にできるようになりそうなので、ちょっとずつ使うコマンドを増やしていきたいです。

【Unity】FirebaseのRealTimeDatabaseを使ってみた

はじめに

直近でサーバーからのPushの実装が必要な案件があったのですが、サーバーサイドの知見はあまり持っておらずWebSocketを用いてある程度の同時接続に耐えうるように作るのは厳しそうだなと思ったので、 どうにかそこらへんから逃げつつ作れないかなと思い先輩に相談したところFirebaseのRealTimeDatabaseを勧められました。

実際にRealTimeDatabaseを使って実装してみたところ、想像以上に簡単にサーバーサイドの実装を終えることができたので書き残します。 クライアントサイドはUnityなので、今回はC#で記述しますがドキュメントを読んだ感じ他の言語でもほぼ同様なようでした。

RealTimeDatabaseとは

FirebaseのRealTimeDatabaseは公式サイトによると、

NoSQL クラウド データベースでデータの保管と同期を行うことができます。データはすべてのクライアントにわたってリアルタイムで同期され、アプリがオフラインになっても、利用可能な状態を保ちます。

とあるように、リアルタイムに同期可能なデータベースです。

クライアントから直接データベースへの書き込みや読み取りを行うような方法でやり取りを行うため、サーバー側は全く意識することなく実装を行うことができます。

また、お値段も非常に安く、同時接続100までは無料で使用することができます。

firebase.google.com

使い方

Unityプロジェクトに対するFirebaseの導入方法などは公式のサイトをご覧ください。

Unity の Firebase Realtime Database を使ってみる  |  Firebase

今回は、データの取得、更新と、リアルタイムに更新を受け取る方法について説明します。

データの取得

データの取得は、以下のようにGetValueAsyncというメソッドを用いて行うことができます。 今回は、コールバックによって結果を受け取っていて、taskが完了したタイミングで結果をLogとして表示しています。

FirebaseDatabase.DefaultInstance.GetReference("user")
    .GetValueAsync().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            Debug.LogError("not found users");
        }
        else if (task.IsCompleted)
        {
            DataSnapshot snapShot = task.Result;
            Debug.Log(snapShot.Value);
        }
    });

上記のコードは、以下のようなデータ構成だった場合、user以下すべてのデータを読み取ります。

  • user
    • books
    • animals
    • age

すべてを読み取るのではなく、user以下のある特定のデータを読み取る場合は以下のようにします。

FirebaseDatabase.DefaultInstance.GetReference("user")
    .Child("age")
    .GetValueAsync().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            Debug.LogError("not found users");
        }
        else if (task.IsCompleted)
        {
            DataSnapshot snapShot = task.Result;
            Debug.Log(Convert.ToInt32(snapShot.Value)); // user's age
        }
    });

このように、読み取りたいところまでChildで指定して行ってGetValueAsyncを呼べば、読み取りたいデータのみを読み取ることができます。 また、上記のコードを見るとわかるように、結果はすべてobject型で返されるため適宜変換が必要となるので注意してください。

データの書き込み

データの書き込みに関しても、読み取りとほぼ同様に書き込みたいところまでChildで指定していき、SetValueAsyncによって書き込みを行います。 以下に例を示します。

FirebaseDatabase.DefaultInstance.GetReference("users")
    .Child("age")
    .SetValueAsync(21);

こちらは、基本的には特に型変換もすることなく値を設定することができます。 すでにデータが存在していた場合は上書きされ、存在していなかった場合は新しく子要素として作られます。

データの更新通知

今回一番欲しかった機能である更新検知を行う方法について説明します。 仕組みとしては、ある値が変わったときに呼ばれるメソッドを登録しておくことができます。 以下のように記述することで、メソッドを登録できます。

FirebaseDatabase.DefaultInstance.GetReference("users")
    .Child("age").ValueChanged += HandleAgeValueChanged;

private void HandleAgeValueChanged(object sender, ValueChangedEventArgs args)
{
    if (args.DatabaseError != null)
    {
        Debug.LogError(args.DatabaseError.Message);
        return;
    }


    Debug.Log(args.Snapshot.Value); // current age
}

登録されたメソッドの内部では、データ取得のときとほぼ同様に、SnapShotから現在の値を取得しています。 どこかでageに対して更新処理があった際には、自動的に上記のメソッドが呼ばれることになります。

まとめ

今回は、FirebaseのRealTimeDatabaseを用いてデータの取得、更新、更新通知を行う方法についてまとめました。 その他にもトランザクションとしてデータの書き込みを行うこともできたり、以下の記事のようにリレーションを実現することもできるようです。

qiita.com

まだ実運用していないので、同時接続などの性能面での不安が残っているのですが、ここらへんに関しても学びがあったらまたまとめたいと思います。

【Unity】ScreenSpace OverlayなCanvas上に3Dモデルを表示する

はじめに

通常、ScreenSpaceをOverlayに指定した場合は最前面にCanvas上のUIの描画が来るため、3Dモデルをさらにその上に表示することはできません。 今回は、Canvasの設定はOverlayにしておきたいが、3DモデルをUIの上に表示したい!という場合の対応方法の一つをやってみたのでまとめます。

完成形

以下のようにOverlayなCanvasのUI上に3D表示を行い、それを切り替えることができるようになります。

f:id:siguma_sig:20180908231417g:plain

やり方

やり方は単純で、3Dモデルの描画をRenderTextureに行いCanvas上ではRawImageでそのRenderTextureの表示を行えば完了です。

順を追って具体的に説明します。

RenderTextureの作成

Projectウィンドウで Create > RenderTextureで作成できます。 細かい設定が気になる方はこちらを見てください。

f:id:siguma_sig:20180908232123p:plain

3Dモデル描画用のカメラを作成

Hierarchy上でメニューから選択してCameraを新しく作ります。その際に、ClearFlagsをSolidColorにしてアルファ値を0にしておいてください。 また、先ほど作成したRenderTexutreをTargetTextureに設定してください。

f:id:siguma_sig:20180908232232p:plain

RawImageの配置

OverlayなCanvas上にRawImageを配置して、Textureに先程作成したRenderTextureを設定します。

f:id:siguma_sig:20180908232442p:plain

カメラの微調整

これで新しく作成したカメラに描画されているものがUIとして表示されるようになります。 写したい3Dモデルが映るような位置に微調整をしましょう。

まとめ

同様な方法で、パーティクルに関してもOverlayなCanvasの上に表示を行うことができます。 ただし、RenderTextureへの描画を行うためにCameraを一つ作成する必要があるため、パフォーマンスのことを考えるとUI上に3Dモデルをばんばん出したいという場合には向いていないかもしれません。

OverlayのままUI上に3Dモデルを表示できるいい方法が他にあれば是非教えてください。

C#のIEquatableについて学び直したまとめ

はじめに

開発中にあるクラスにIEquatableが実装されていないと指摘を受けたのですが、それについて理解が曖昧なまま実装して修正してしまったので改めて学び直したメモを残します。

IEquatableインターフェイスとは

2つのオブジェクトが等しいかどうかを調べるEqualsメソッドを実装することを保証するためのインターフェイスです。

ここでいう2つのオブジェクトが等しいという場合の等しいがどんな状態を表すかが重要で、IEquatableを実装しなくてもクラス同士の比較自体はできます。 では、実装する場合としない場合で何が違うのかを比べてみます。

IEquatableを実装しない場合とする場合の比較

実装しない場合、参照型であるクラスと値型になる構造体で挙動が変わります。 今回は参照型であるクラスについてのみ説明していきます。

以下のようなクラスが存在するとします。

namespace Sample.Equals
{
    public class EqualClass 
    {
        private int _rank;
        private string _name;

        public EqualClass(int rank, string name)
        {
            _rank = rank;
            _name = name;
        }

        public string GetRank()
        {
            return $"{_name} : Rank {_rank}";
        }
    }
}

このとき、以下のような比較をするとコメントで示されたように、falseになります。

EqualClass sample1 = new EqualClass(0, "hoge");
EqualClass sample2 = new EqualClass(0, "hoge");
            
Debug.Log(sample1.Equals(sample2)); // False

ここで、IEquatableを以下のように実装します。

public bool Equals(EqualClass equalClass)
 {
     if (equalClass == null)
     {
         return false;
     }
 
     return (this._rank == equalClass._rank && this._name == equalClass._name);
 }

そうすると、さっきの比較を行うと結果はtrueを返すようになります。

このような挙動になる理由は、Equalsを実装する前にはオブジェクトの参照先を比較しています。 そのため、先程の例ではsample1とsample2で別のインスタンスが作られているため、中の値が等しくてもfalseを返すようになっているのです。 しかし、Equalsを実装した場合はその中身でそれぞれのメンバの値を比較して結果を返しているため、値が等しければtrueを返します。

このように、直接メンバの値によって比較したい場合は、IEquatableインターフェイスを実装する必要があります。

IEquatableを実装すべき場合

先程説明したように、参照型のオブジェクトで値の比較をしたい場合はEqualsの実装が必要です。 では、このように値の比較をしたくなるのはどんな場合にあるのでしょうか?

大きなものの一つに、ListやArray、Dictionaryといったジェネリックなコレクションクラスで利用したい場合が挙げられます。 例えば、先程のEqualClassを下記のようにListでまとめたかったとします。

List<EqualClass> sampleList = new List<EqualClass>()
{
    new EqualClass(0, "hoge"),
    new EqualClass(1, "fuga"),
};

ここで、rankが0で名前がhogeだった人がリストの何番目に入っているか調べたくなったときに、以下のように記述した際に、IEquatableが実装されていないと見つからなかったことになり-1を返します。

EqualClass hoge = new EqualClass(0, "hoge");
            
Debug.Log(sampleList.IndexOf(hoge));    // -1

このように、意図せず比較を使っている場合もあるため常にIEquatableは実装したほうが良いと思います。

余談ですが、Dictionaryのキーに使いたい場合は、IEquatableインターフェイスの他にGetHashCode()もオーバーライドする必要があります。

IEquatableとIEquatable(Object)の違い

このように比較をする可能性のあるクラスには必ず実装しておいたほうが良さそうなIEquatableですが、二種類あります。 この2つの違いは何なのでしょうか?

2つのEqualsは以下のようなメソッドになっています。

bool IEquatable<T>(T obj)
bool IEquatable(object obj)

前者は先程まで説明していたジェネリックなIEquatableで後者は引数にobject型を取るEqualsです。 これらの違いとしては、objectの方は比較の際にボクシングが発生するためパフォーマンス的にあまり良くないですが、ジェネリックな方はボクシングが発生しない分パフォーマンスが良くなります。

一見objectの方はパフォーマンスも悪く、使うタイミングがないように思えますが、クラスによってはobjectの方のみを呼び出すようになっているものもあるため、常にジェネリックの方を実装していたとしてもobjectの方も実装したほうが良いそうです。

まとめ

理解が曖昧だったEqualについて整理しました。 こうやってまとめて振り返ってみると、今回指摘されたのはListで利用する可能性のあるクラスだったからでした。 こんな感じで日々の開発の中でふと疑問に思ったことはしっかりと時間の取れるときに調べてまとめることでより理解を深めていきたいですね。 間違っているところがあれば、是非コメントください。

参考

IEquatable(T) インターフェイス (System)

自作クラスのEqualsメソッドをオーバーライドして、等価の定義を変更する - .NET Tips (VB.NET,C#...)

クラッシュロワイヤルのAPIが公開された話

はじめに

今週はじめにクラッシュロワイヤルのAPIが一部公開されたことがクラッシュロワイヤルの海外版アプリ内の告知で発表されていました。 それについてどれくらい使えるものだったのかと思ったことについて書きます。

公開されたもの

APIはこちらでユーザー登録をすることで使えるようになります。

developer.clashroyale.com

現時点で取得できる情報は以下の一覧のようです。

クラン情報

  • クランの検索
  • 任意のクランの詳細情報
  • クランメンバーのリスト
  • クラン対戦の戦歴
  • 現在行われているクラン対戦の情報

プレイヤー情報

  • プレイヤーの詳細情報
  • 今後手に入る宝箱のリスト
  • 最近のバトルの戦歴

トーナメント情報

  • 全トーナメントの名前
  • トーナメントの詳細情報

カード情報

  • 全カードの情報(コスト、アイコン、Maxレベル)

地域情報

  • 全地域のID
  • 特定の地域の詳細情報
  • 特定の地域のクランランキング
  • 特定の地域のユーザーランキング
  • 特定の地域のクラン対戦ランキング

できそうなこと

基本的な情報はすべて取得できるようです。 取得できない情報を探すのが大変なぐらい公開されていそうでした。

中でもびっくりしたのが、他のプレイヤーの戦歴を見れることです。 試合自体はオープンに行われており、誰でも観戦は自由にできるゲームなので公開しても問題ないのかもしれませんが、クラッシュロワイヤルはプロプレイヤーも多くいるゲームなので、そのプレイヤーたちのデッキであったり戦歴を見れるのは非常に嬉しいです。 これを使えば、有名プレイヤーたちのデッキの傾向や戦歴の分析ができそうです。

思ったこと

おそらくゲームのAPIを公開するというのは非常に珍しいのではないかと思います(他ゲームで公式で公開されているものがあれば知りたいです)。 公式サイトにも書かれていますがAPIを公開することで熱心なユーザーが自発的に便利なアプリやサイトを開発してくれれば、そのゲームをやっている人はよりゲームを楽しめるようになり、それ自体が有名になればそれらのサイトやアプリをきっかけでゲームを始めるような人も出てくるようになると思います。

ニコニコ動画のようなユーザーによる盛り上げによって自然と人気になるスタイルは非常に良いと思いますし、今回のクラッシュロワイヤルのAPI公開はそれを促す施策として非常に効果的だろうなと思いました。 ゲームは広告やSNS運用で集客をするのが当たり前になっていると思いますが、このような熱心なユーザーを利用しての集客方法もあるのだなと新しく気づかされました。

まとめ

少し試してみたところ、有名プレイヤーの戦歴やどんなデッキを使っているのかも見れるようだったので有名プレイヤーたちのデッキの傾向や戦歴を見れるようなアプリを作ってみたくなりました。 こういったものが公開されるゲームが増えるとユーザーの熱によってゲームコミュニティ自体が広がって行くと思うので、どんどんやってほしいなあと思いました。

【Unity】uGUIの当たり判定を広げる

はじめに

かなり小さいサイズのボタンを作成する場合にImageのサイズは変えたくないけど、判定だけ少し広めに取りたいということがあります。 今回は、そのような場合に判定を広げる方法とその広げた判定がどんな感じになっているかをGizmoで見えるようにする方法を紹介します。

完成したもの

以下のような白いボタンに対して、判定が緑の範囲となるような感じで調整ができます。

f:id:siguma_sig:20180812162709p:plain

f:id:siguma_sig:20180812162717p:plain

ソースコード

using UnityEngine;
using UnityEngine.UI;

namespace UIColliderSample
{
    [RequireComponent(typeof(RectTransform))]
    [RequireComponent(typeof(CanvasRenderer))]
    public class InvisibleGraphic : Graphic
    {
        private RectTransform _rectTransform;
        private Rect _rect = new Rect();

        private Rect GetRect()
        {
            if (_rectTransform == null)
            {
                _rectTransform = GetComponent<RectTransform>();
            }

            _rect.center = (Vector2) _rectTransform.position + _rectTransform.rect.center;
            Vector2 size = _rectTransform.rect.size;
            size.x *= _rectTransform.lossyScale.x;
            size.y *= _rectTransform.lossyScale.y;
            _rect.size = size;
            return _rect;
        }


        protected override void OnPopulateMesh(VertexHelper vh)
        {
            base.OnPopulateMesh(vh);
            vh.Clear();
        }

        private void OnDrawGizmos()
        {
            var rect = GetRect();
            Gizmos.color = new Color(0.0f, 1.0f, 0.0f, 0.5f);
            Gizmos.DrawCube(rect.center, rect.size);
        }
    }
}

ソースコードの説明

判定を広げる部分に関してはkanさんのブログを参考にしました。 やっていることとしてはGraphicを継承することで判定を取れるようにして、そのままだと白い四角が描画されてしまうので以下の部分で頂点を空にすることで描画されないようにしています。

protected override void OnPopulateMesh(VertexHelper vh)
{
    base.OnPopulateMesh(vh);
    vh.Clear();
}

Gizmoの部分では、このスクリプトがアタッチされているオブジェクトのRectTransformを見て、そのサイズになるようにRectを表示しています。 RectTransformに対して正しいRectになるように、以下の部分でRectを作成しています。

private Rect GetRect()
{
    if (_rectTransform == null)
    {
        _rectTransform = GetComponent<RectTransform>();
    }

    _rect.center = (Vector2) _rectTransform.position + _rectTransform.rect.center;
    Vector2 size = _rectTransform.rect.size;
    size.x *= _rectTransform.lossyScale.x;
    size.y *= _rectTransform.lossyScale.y;
    _rect.size = size;
    return _rect;
}

Rectのサイズはscaleも考慮する必要があるので、lossyScaleを掛けています。

使い方

作成した上記のスクリプトを当たり判定を広げたいuGUIに子オブジェクトを作成してそれにアタッチする。

f:id:siguma_sig:20180812170505p:plain

まとめ

判定を広げるのをもっと簡単にできたらいいなあ…と思いました。 もしもっと良いやり方があれば教えてください!

あとなにげにGizmoを初めていじりました。

参考

kan-kikuchi.hatenablog.com