はじめに
動画のように1クリックでShadowCaster2Dの影の範囲を作ってくれるEditor拡張を作りました。
ソースコード
作ったわけ
2Dゲーム作ってる友達が『毎回チマチマ設定するのめんどくさいな~』と嘆いていたので作りました。
機能は
-
SpriteRenderer
のMeshに対応した形でShapeを作る -
CompositeCollider2D
からTilemapの形でShapeを作る - 作った形をまとめて消す
があります。
解説
既存のShadowCaster2Dを拡張する
今回は既存のインスペクターの見た目の下に自分の追加項目を付け足すのが目標です。
ShadowCaster2D
は最初からUnityにあるComponentで、これをEditor拡張するには
[CustomEditor(typeof(ShadowCaster2D))]
public class ShadowCaster2DAutoShapeEditor : Editor
{
var loadAssembly = Assembly.Load("Unity.RenderPipelines.Universal.Editor");
var type = loadAssembly.GetType("UnityEditor.Rendering.Universal.ShadowCaster2DEditor");
if (type == null)
{
return;
}
var editor = CreateEditor(target, type);
editor?.OnInspectorGUI();
// この下に自分で付け足したいEditor拡張内容を書く
}
とします。
var loadAssembly = Assembly.Load("Unity.RenderPipelines.Universal.Editor");
をアセンブリが違うのでこうしないとtype
はnull
になります。
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
}
にすると標準のインスペクターの見た目ではなく、Debugモードでの見た目になるので使いません。
SpriteRendererのMeshに沿ってShapeを作る
// 重心を使って頂点を時計回りにソートする
Vector2 center = Vector2.zero;
foreach (Vector2 vertex in vertices)
{
center += vertex;
}
center /= vertices.Length;
Array.Sort(vertices, (v1, v2) =>
{
float angle1 = Mathf.Atan2(v1.y - center.y, v1.x - center.x);
float angle2 = Mathf.Atan2(v2.y - center.y, v2.x - center.x);
return angle2.CompareTo(angle1);
});
ここが一番重要です。SpriteRenderer
のクラスから簡単に頂点情報は手に入れれますが、これをそのままShadowCaster2D
に代入するとShapeの形が変になります。
これは頂点情報の格納されてる順番がぐちゃぐちゃなので起きます。
ぐちゃぐちゃな並び順を一周回るようにソートします。
重心を求めて、重心から頂点の角度を計算し、それでソートします。
凸型だったり、特殊形状だとこの方法はおそらくうまくいきません。
Vector3[] hogePath = new Vector3[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
hogePath[i] = vertices[i];
}
shapePathField.SetValue(shadowCaster2d, hogePath);
shapePathHashField.SetValue(shadowCaster2d, UnityEngine.Random.Range(int.MinValue, int.MaxValue));
meshField.SetValue(shadowCaster2d, new Mesh());
あとはShadowCaster2D
にSetして完了です。Vecter3
じゃないとうまくSetできなかったのでVecter3
にしてます。
CompositeCollider2DからShapeを作る
SpriteRenderer
の時ほど難しくないです。
for (int i = 0; i < tilemapCollider.pathCount; i++)
{
}
最初のFor
はPathの分だけ繰り返します。これはタイルマップは宙に浮いているマスなどがあるので、浮いているマスの分だけ別々にGameObjectを作って処理するからです。
CompositeCollider2D
から頂点情報をGetすると並び順に問題はないのでさっきのようにソートする必要性は無いです。
GetしてShadowCaster2D
にSetして完了です。
保存できるようにする
折角Shapeを作ったのに保存できなかったら意味が無いです。
EditorUtility.SetDirty((ShadowCaster2D)target);
これを書いて保存できるようにします。