- 2023/11/02 【Unity】【エディタ拡張】インスペクタのフィールドに属性で色を付ける
- 2023/10/02 【Unity】【C#】Light の Halo はランタイム操作できない?
- 2023/09/02 【Unity】【エディタ拡張】Game View の解像度を取得する
- 2023/08/02 【Unity】【C#】指定ワールド位置がカメラに映っているか調べる
- 2023/04/01 【Unity】A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details. と出たら…
« prev next »
【Unity】【エディタ拡張】インスペクタのフィールドに属性で色を付ける 
2023/11/02 Thu [edit]
以前インスペクタのフィールドを入力不可にする PropertyAttribute を作ったが、Unity2019.4 以降は Personal(無料)アカウントでもダークテーマを使えるようになったので、無効の色(グレー:たぶんアルファが低くなる)が少し見ずらく感じるようになったんだよね…。


なのでいっそのこと自分の好きな色にできないかとググったが、なぜか込み入ったコードしか見つからなかったので、Disable 属性と同じ要領で色を付けられるかと試してみたら、意外と簡単にできたので、コードを載せておこう。無効化とは併用できなかったが(グレーになってしまう)、自分でフィールドを色で見分けるくらいの用途なら十分に役に立つと思う。
(※) Unity 2020.3.34f1 / Windows10(x64) で確認
●デバッグ用途のフィールドに色を付ける例(属性):DebugFieldAttribute.cs
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Example
{
public class DebugFieldAttribute : PropertyAttribute
{
//※中身は空で良い
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(DebugFieldAttribute))]
public class DebugFieldDrawer : PropertyDrawer
{
static readonly Color _color = Color.green; //色を指定
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var tmpColor = GUI.color; //一時退避
GUI.color = _color;
EditorGUI.PropertyField(position, property, label, true);
GUI.color = tmpColor;
}
}
#endif
}
●クラス内部値用途のフィールドに色を付ける例(属性):InternalAttribute.cs
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Example
{
public class InternalFieldAttribute : PropertyAttribute
{
//※中身は空で良い
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(InternalFieldAttribute))]
public class InternalFieldDrawer : PropertyDrawer
{
static readonly Color _color = Color.cyan; //色を指定
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var tmpColor = GUI.color; //一時退避
GUI.color = _color;
EditorGUI.PropertyField(position, property, label, true);
GUI.color = tmpColor;
}
}
#endif
}
●実際にインスペクタで表示してみる例(メインコード等):FieldColorTest.cs
using UnityEngine;
public class FieldColorTest : MonoBehaviour
{
[SerializeField, DebugField] string debugValue = "hoge";
[SerializeField, InternalField] int internalValue = 123;
[SerializeField, Disable] float disableValue = 3.141592f; //Disable 属性は以前に作ったもの
[SerializeField] string normalValue = "piyo";
}

今回はエディタスクリプト部分をプリプロセッサ(UNITY_EDITOR)で囲んでしまったが、Disable 属性のときのようにスクリプトを分けて「Editor」フォルダに PropertyDrawer を継承したスクリプトを置いても良い。"DebugField", "InternalField" の名前は任意なので、自分で好きに付けると良いだろう。とても簡単なスクリプトだが、名前と色だけ変えて使い回せば、インスペクタも結構見やすくなった。思いつきだったが、何個かあらかじめ定義しておけば、簡単に色分けで分類できて、意外と便利だったので、誰かの役に立つと嬉しい。
(PropertyAttribute, PropertyDrawer をもっと知りたいときは、以下を参照)
・UnityのAttribute(属性)についてまとめてメモる。
・自分だけのPropertyDrawerを作ろう!
(関連記事)
【Unity】【エディタ拡張】インスペクタで入力不可(Disable)な属性を作る
【Unity】インスペクタの値を保持したまま変数をリネームする
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【エディタ拡張】インスペクタの表示項目を動的に変更する
【Unity】【エディタ拡張】独自のギズモ(Gizmo)を表示する
【Unity】【C#】Light の Halo はランタイム操作できない? 
2023/10/02 Mon [edit]
現在 VRMLiveViewer v3.8 に入れるカスタムなライトを実装中…。せっかくなので、Light コンポーネントの細かい機能も操作できるようにしたいと思って、色々調べていたが、どうやらインスペクタにある DrawHalo はスクリプトから操作できないようだ…(プロパティ自体が無い)。
不思議に思ってググっていたら、なぜか Halo は public な機能ではないらしいね。予め設定したものをビルドして使うだけのものなのか…?
また、Light 内の DrawHalo ではなく、別コンポーネントの Halo (インスペクタで個別にアタッチするやつ) でオン/オフするコードが出てきた。しかしそれもやはり少し変わっていて、Behaviour クラスを用いて enabled を true/false してるね。まぁ、一応成功したので、コードだけ載せておこう。
(※) Unity 2020.3.34f1 / Windows11(x64) で確認
●アタッチした Halo コンポーネントをオン/オフするコード
using System;
using UnityEngine;
public static partial class Extensions //※クラス名は任意
{
public static void SetEnableHalo(this Light light, bool isOn)
{
if (light == null)
return;
var halo = (Behaviour)light.gameObject.GetComponent("Halo"); //Behaviour 型
//var halo = light.gameObject.GetComponent("Halo"); //Component 型
if (halo != null)
{
halo.enabled = isOn; //Behaviour 型
//halo.GetType().GetProperty("enabled")?.SetValue(halo, isOn, null); //Component 型
}
}
}
●スクリプトからの使用例
using UnityEngine;
public class SetEnableHaloTest : MonoBehaviour
{
public Light _light; //インスペクタでアタッチしておく
//Button の OnClick 等からコールバックする
public void HaloOnClick()
{
_light.SetEnableHalo(true);
}
//Button の OnClick 等からコールバックする
public void HaloOffClick()
{
_light.SetEnableHalo(false);
}
}
SetEnableHalo 内ではコメントアウトで Component 型のコードも書いてあるが、どちらでもいけるようだ。ただこれはインスペクタで予め Halo コンポーネントを追加しておくか、コードで AddComponent("Halo") しなければならない。型指定にはジェネリック型 <Halo> は使えないようだ。GetComponent も同じ。なぜか名前(文字列)でしか指定できないらしい。以下の参考資料(UnityCsReference)に「internal sealed partial class Halo : Behaviour」となっているので(public ではないので)、そのせいか?
(参考資料)
・UnityCsReference/Runtime/Export/GraphicsComponents.bindings.cs
・Getting the Halo property of a point light in Unity
・How can I change the value of 'Draw Halo' inside Point Light - Unity3D
・Enable Draw Halo in Light Component via Script
結局、オン/オフはできても色や距離の変更はできないので(やはり public なプロパティが無いので)、実装自体をやめた。また、リフレクションでプライベートメンバーを取得するような方法もググってたら出てきたが、そもそも public でない時点でランタイム動作は保証されないので、使わない方が良い気がする。
また同じ様な効果なら、Volumetric Light の方が綺麗だったしね。無理矢理やろうと思えば、予めビルドにオンとオフの2つの状態を別GameObjectにアタッチしておき、切り替える方法もあるが、それなら Light の DrawHalo でやった方が良いか気がする(Color と Range が自動的に連動するので)。ただそれほど有用でもないし、使い勝手も悪いので(コードが無駄に複雑になるので)、Halo 自体のランタイム操作は諦めた方が良いかな…。
(関連記事)
【Unity】Standard Assets の Flare (レンズフレア) は Gamma 用だった?
【Unity】【C#】指定ワールド位置がカメラに映っているか調べる
【Unity】【C#】ガンマ(Gamma, sRGB) - リニア(Linear) 値の相互変換
【Unity】【エディタ拡張】Game View の解像度を取得する 
2023/09/02 Sat [edit]
ランタイム時の画面解像度は (Screen.width, Screen.height) で取得できるのだが、エディタスクリプト上で Screen で画面解像度を取得すると、よくわからない値になる。

調べてみたら、UnityStats.screenRes というプロパティ(Unityマニュアルではなぜか見つからない)で取得できるとあったので、いつものように静的な関数として定義しておくと便利だと思った。簡単なサンプルを載せておこう。
(※) Unity 2020.3.34f1 / Windows11(x64) で確認
●Game View の解像度を取得する (戻値:Vector2Int 型)
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
public static partial class Utils //※クラス名は任意
{
/// <summary>
/// Game View の解像度を取得する (戻値:Vector2Int 型)
/// 2023/09/02 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-431.html
/// </summary>
/// <returns>x = width, y = height</returns>
public static Vector2Int GetGameViewResolution()
{
var res = UnityStats.screenRes.Split('x'); //"1920x1080" 等
return new Vector2Int(int.Parse(res[0]), int.Parse(res[1]));
}
}
#endif
●エディタスクリプトからの使用例 (戻値:Vector2Int 型)
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(GameViweResoTest))] //クラス名は任意(ヒエラルキーにアタッチ)
public class GameViweResoTestEditor : Editor //クラス名は任意(※Editorフォルダに置く)
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Space(15);
if (GUILayout.Button("GameViewResolution"))
{
Debug.Log($"Screen: {Screen.width}x{Screen.height}");
var reso = Utils.GetGameViewResolution();
Debug.Log($"GameViewResolution: {reso.x}x{reso.y}");
}
GUILayout.Space(15);
}
}
戻値に Vector2Int を使ってるが、x が width, y が height となる。
ここでは MonoBehaviour を継承した適当なスクリプト(GameViweResoTest.cs)を作成し、ヒエラルキーにアタッチしている。
1280x720, 1920x1080 等、定番の解像度を使ってるが、「Free Aspect」の状態でも取得できるようだ。そのときに 425x776 など、実際の画面上の解像度(?)になるらしい。

●結果例 (GameView: 4K UHD)
GameViewResolution: 3840x2160
GameViewResolution (Tuple): 3840x2160
現在の Unityバージョン(掲載時点:Unity2020)だと C# も Tuple 型が使えるので(必要なら、.Net 4.x にする)、戻値を Tuple 型にしておくと、少しばかり簡潔になるので良いかもしれない。
●Game View の解像度を取得する (戻値:Tuple 型)
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
public static partial class Utils //※クラス名は任意
{
/// <summary>
/// Game View の解像度を取得する (戻値:Tuple 型)
/// 2023/09/02 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-431.html
/// </summary>
/// <returns>w = width, h = height</returns>
public static (int w, int h) GetGameViewResolutionAsTuple()
{
var res = UnityStats.screenRes.Split('x'); //"1920x1080" 等
return (int.Parse(res[0]), int.Parse(res[1]));
}
}
#endif
●エディタスクリプトからの使用例 (戻値:Tuple 型)
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(GameViweResoTest))] //クラス名は任意(ヒエラルキーにアタッチ)
public class GameViweResoTestEditor : Editor //クラス名は任意(※Editorフォルダに置く)
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Space(15);
if (GUILayout.Button("GameViewResolution"))
{
Debug.Log($"Screen: {Screen.width}x{Screen.height}");
var (w, h) = Utils.GetGameViewResolutionAsTuple();
Debug.Log($"GameViewResolution (Tuple): {w}x{h}");
}
GUILayout.Space(15);
}
}
注意点として、エディタスクリプトは任意の「Editor」フォルダ内に入れて使うか、プリプロセッサ(#if UNITY_EDITOR~#endif)で囲んで使う(通常はヒエラルキーでアタッチするスクリプトはプリプロセッサで囲み、エディタのみ使うものは Editor フォルダに入れるのが簡単)。これらは Unityエディタ上では使えるが、ランタイムでは使えない。あくまで開発支援用スクリプトとなる。
自由に改造して使うと良い。
(関連記事)
【Unity】【C#】画面解像度とアクペクト比(整数)を求める
【Unity】【C#】指定ワールド位置がカメラに映っているか調べる
【Unity】固定背景画像(2D)を表示する
【Unity】【C#】指定ワールド位置がカメラに映っているか調べる 
2023/08/02 Wed [edit]
最近はチュートリアル・ノウハウ系が多かったので、久しぶりにお役たちコード(?)を書いておこう。
まぁ、VRM Live Viewer 開発し始めてからどうしても偏ってしまうが、私も最初から知ってたわけじゃないからね。必要に応じて調べてるうちに Unity にも詳しくなったとも言える。何でも勉強するならテーマがあった方が頭に入るね。極端な話、VRM Live Viewer はオープンソースの塊で、再利用可能コードは数年間に渡って公開してるし、アルゴリズムなどもアプリ開発以前から公開してるし、全部無料で手に入るものばかりなので、誰でも作れることになる。つまり、世界中の人と条件は同じなのだ。積み重ねがどれだけ重要かがわかるね(笑)。
それでは、さっそく今回のテーマのコードを書いてみよう。と言ってもかなり簡単なんだけどね。
●指定のワールド位置がカメラに映っているか調べる
using UnityEngine;
public static partial class Extensions //※クラス名は任意
{
/// <summary>
/// カメラの Viewport 内に表示されているか?
/// 2023/08/01 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-430.html
/// </summary>
/// <param name="cam">判定するカメラ</param>
/// <param name="pos">対象ワールド位置(transform.position)</param>
/// <returns></returns>
public static bool InCamera(this Camera cam, Vector3 pos)
{
if (cam == null)
return false;
var vp = cam.WorldToViewportPoint(pos);
return (0f <= vp.x && vp.x <= 1f && 0f <= vp.y && vp.y <= 1f);
}
}
Camera.WorldToViewportPoint の内容は公式マニュアル見て欲しいが、戻り値の Vector3 は翻訳すると『ビューポート空間は正規化されており、カメラに対して相対的です。カメラの左下は (0,0) です。右上は (1,1) です。Z 位置はカメラからのワールド単位です。』となっているので、要するに値が 0~1 に入ってれば、カメラのビューポートに入ってると考えて良い。
ただ、指定位置は transform.position 等、あくまで「点」でしかないので、メッシュなどが大きい場合、中心(Pivot)は画面から外れてるが、メッシュの一部が画面に少し見えている、なんてことが起こるかもしれない。そういうときはメッシュのバウンディングボックス(Renderer.bounds)等の8隅を調べるなどすれば良いかもしれない。
また、Unity では全ての GameObject は Transform 持ってるので、いっそ transform を引数にしたものを作っておくのも良いかもね。
●指定のワールド位置がカメラに映っているか調べる (Transform 指定)
using UnityEngine;
public static partial class Extensions //※クラス名は任意
{
/// <summary>
/// カメラの Viewport 内に表示されているか?
/// 2023/08/01 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-430.html
/// </summary>
/// <param name="cam">判定するカメラ</param>
/// <param name="tr">対象Transform</param>
/// <returns></returns>
public static bool InCamera(this Camera cam, Transform tr)
{
if (cam == null || tr == null)
return false;
var vp = cam.WorldToViewportPoint(tr.position);
return (0f <= vp.x && vp.x <= 1f && 0f <= vp.y && vp.y <= 1f);
}
}
まぁ、好きに改造して使って欲しい。
今回も VRM Live Viewer で使ってる再利用可能コードの一部となる。タイトルにもある「特定位置がカメラに映っているか調べる」は、ポストエフェクトの被写界深度の自動フォーカスで「ボーン対象」の選択に使っている。ボーンの選択が無い頃はモデルの「Head」(顔の辺り)1つが対象になっていて、要するに顔が常にフォーカスされていた。しかし、カメラモーションによっては下(足)から上(顔)へ接写など、一時的に顔が画面から外れてしまうことがあったんだよね。そういうとき顔以外にもフォーカスを当てる必要があった。つまり「顔が画面から外れていれば、他の近いボーンをフォーカス対象とする」ようにしたかったんだよね。
まぁそんなことを VRM Live Viewer リリース以来、約5年近くやってる。最近はたまに「VRM Live Viewer を github で公開しろ」とか言ってくる人もいるが、冒頭に述べたように内容的にはオープンソースの塊だったりする。それをアプリに最適化、一部都合良く手を加えて改造してるので、ほとんど汎用性は無い。汎用的にするためにアプリ依存コードを除外して、一般化すればオープンソースと同じになる(元に戻る)。なので、全然意味が無い。ブログの冒頭でも言ってるように、最初からどうやって作ったかも、使ってるオープンソースも全部書いてあるので、本当に意味を感じない。
まぁ、言ってくる人は学生さん(?)が多い気がするが、私も学生の頃は何でもできるわけじゃなかったよ。でも四苦八苦して色々考えてたのがいい経験になったのは間違いない。最初から「答えを教えろ」(そしてコピーして作ったものを自分のものにさせろ)というのは、かなり失礼なことだと思う。前述したように「世界中の人と同じ条件で作っている」(恐らく同じVRMアプリ開発者内では最底辺の環境)ので、同じことは誰でもできる。Unity なんてお金を出せば AssetStore でいくらでも優れたアセットを導入できるからね。私は無料で配布してるので、有料アセットなど買えないのだよ。だからオープンソースと自分のこれまでの知識と技術を駆使してるだけで、特別なことはしてない。せいぜい自分なりの工夫を重ねてるだけで、何でもやって貰えるのが当たり前と思われても非常に困る(← 一時期クレクレが年間50件は超えていて頭が痛かった=仮に1件5万~30万取れるとすると250~1500万の仕事をタダ働きすることになる。普通にサラリーマンの給料で換算しても約5年だと1500~2500万を無料で提供してるからね。これ以上仕事してタダでよこせと言われても、自分ならどうかと考えてみて欲しい)。
経験不足は経験でしか補えないからね。私は学生時代書いたコード(C, C++)を未だに持っているが、物凄く苦労して2週間~1ヶ月くらいかけて作ったコード(システム)も、今では1日くらいで作れる。それくらい人は成長できるのだよ。答えはブログでもオープンソースでもググって大量の資料でも、今では充実してるので(昔はそんなに便利じゃなかった)、ヒントはやるから諦めないで精進しろとしか言えないのが本音なんだよね。今後はビジュアルスクリプティングでコンポーネント組み合わせるだけで作れるし、AI がコード自動生成してくれる時代も来るから、今よりもっと「自分で考える」ことができない人増えるだろうね。与えてくれるのが当たり前って考え方を捨てないと、成長はできないと思うよ。
(関連記事)
【Unity】固定背景画像(2D)を表示する
【Unity】【C#】画面解像度とアクペクト比(整数)を求める
【Unity】【C#】HDR Color を計算(変換)する
【Unity】【C#】ガンマ(Gamma, sRGB) - リニア(Linear) 値の相互変換
【Unity】【C#】3DText(TextMesh) を半透明より手前に表示する
【Unity】A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details. と出たら… 
2023/04/01 Sat [edit]
久しぶりに UniVRM を 0.108 にアップデートしてみたら、以下のエラーが出るようになった。
しかし、エラーメッセージはそれだけで、何も情報が出てこない。どうやらメモリーリークしてるようだが、以前は出てなかったので恐らく実装が変わったのだろう。翻訳すると「フルスタックトレースを有効にして詳細を得よ」と書いてあるが、それどこにあるの?ってググってみた。そしたら以下のページが出てきた。
(参考) A Native Collection has not been disposed, Enable Full Stack?
どうやら Jobs をパッケージマネージャでインストールすれば、メニューに出てくるようだ。やってみたら、詳細のエラーログが出るようになったので、Unity 初心者でもわかるように詳しく手順を書いておこう。
(※) Unity 2020.3.34f1 / Windows11(x64) で確認
1.「Edit>Project Settings...」から「Package Manager」を表示し、「Enable Preview Packages」「Show Dependencies」のチェックをオンにする(※現時点では Jobs が Preview のため。Stable になったら、この手順は必要無くなると思う)。
2.「Window>Package Manager...」を開き、「Unity Registry」にして「Jobs」をインストールする。
3. インストールが終わったら(結構時間かかる)、上部のメニューから「Jobs>Leak Detection>Full Stack Traces (Expensive)」をオンにする(※「Expensive」とあるので、負荷が高くなる=動作が重くなる ので注意)。

これでエラー時に詳細なスタックトレースが Console に出るようになる。

私が改修していた UniVRM 0.108 では GltfData がメモリーリークしていたらしい(クラス内部で NativeArray を使ってるっぽいので、Dispose する必要があるのだろう)。using で囲めばエラーは出なくなった。参考ページでは WWW を using で囲ってるね。その手のオブジェクトを疑ってみれば良いだろう。
(参考) A Native Collection has not been disposed, Enable Full Stack?
(関連記事)
【Unity】SymGetSymFromAddr64, GetLastError: '無効なアドレスにアクセスしようとしています。' と出たら…
【Unity】Unity2018 でビルドエラー「CommandInvokationFailure: Gradle build failed.」が出る
【Unity】IncrementalCompiler でのエラー:Unloading broken assembly Packages/com.unity.incrementalcompiler/Editor/Plugins/Unity.PureCSharpTests.dll, this assembly can cause crashes in the runtime
【Unity】【C#】モバイルビルド中の警告:Game scripts or other custom code contains OnMouse_ event handlers.~ を消す
【Unity】InitializeUnityExtensions: Must have a valid path for the plugin [XX] (XX:番号) というエラーの修正方法
【Unity】"~\Temp\Assembly-CSharp.dll.mdb" is denied. と出たら…