📗
【Unity】キャンバスの描画順を表示する拡張機能
はじめに
初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!
今回は
キャンバスの描画順を表示する拡張機能
について紹介します
使い方
エディター上部のメニューからCustomEditorのCanvasOrderViewでウィンドウを表示
ウィンドウ内のShow Canvas Treeボタンを押す
スクリプト
こちらの処理をEditorフォルダに配置してください。
CanvasOrderView.cs
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using System.Collections.Generic;
using System.Linq;
namespace Editor
{
#if UNITY_EDITOR
public class CanvasOrderView : EditorWindow
{
private bool _isView = false;
private TreeViewState _treeViewState;
private CanvasOrderTreeView _treeView;
/// <summary>
/// ウィンドウを表示
/// </summary>
[MenuItem("CustomEditor/CanvasOrderView")]
public static void ShowWindow()
{
GetWindow<CanvasOrderView>("Canvas Order View");
}
/// <summary>
/// ウィンドウに描画
/// </summary>
private void OnGUI()
{
if (GUILayout.Button("Show Canvas Tree", GUILayout.Width(150)))
{
if (_isView)
{
_treeView.Reload();
}
else
{
_treeViewState = new TreeViewState();
_treeView = new CanvasOrderTreeView(_treeViewState);
_isView = true;
}
}
if (_isView)
{
// ヘッダー行を表示
EditorGUILayout.BeginHorizontal();
GUILayout.Space(20);
EditorGUILayout.LabelField("Order", GUILayout.Width(250));
EditorGUILayout.LabelField("Reference", GUILayout.Width(250));
EditorGUILayout.EndHorizontal();
GUILayout.Box("", GUILayout.Width(position.width), GUILayout.Height(1));
// ツリービューの描画領域を設定
Rect rect = new Rect(0, 50, position.width, position.height - 50);
_treeView.OnGUI(rect);
}
}
}
public class CanvasOrderTreeView : TreeView
{
private List<Canvas> _rootCanvases;
private HashSet<int> _displayedCanvasIds;
/// <summary>
/// コンストラクタ。
/// </summary>
public CanvasOrderTreeView(TreeViewState state) : base(state)
{
_displayedCanvasIds = new HashSet<int>();
Reload();
}
/// <summary>
/// ツリービューのルートを構築
/// </summary>
protected override TreeViewItem BuildRoot()
{
_displayedCanvasIds.Clear();
// シーン内のすべてのキャンバスを取得
var allCanvases = Object.FindObjectsOfType<Canvas>().ToList();
// ルートキャンバス(親がない、または親がCanvasでないオブジェクト)をフィルタリング
_rootCanvases = allCanvases.Where(c => c.transform.parent == null || c.transform.parent.GetComponentInParent<Canvas>() == null).ToList();
var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
// カメラとオーバーレイのキャンバスを分ける
var cameraCanvases = _rootCanvases.Where(c => c.renderMode == RenderMode.ScreenSpaceCamera).ToList();
var overlayCanvases = _rootCanvases.Where(c => c.renderMode == RenderMode.ScreenSpaceOverlay).ToList();
var cameraItem = new TreeViewItem { id = 1, depth = 0, displayName = "Camera" };
root.AddChild(cameraItem);
// Depthごとにカメラキャンバスをグループ化
var groupedCameraCanvases = cameraCanvases.GroupBy(c => c.worldCamera.depth)
.Select(g => new { Depth = g.Key, Canvases = g.OrderBy(c => c.sortingLayerName).ThenBy(c => c.sortingOrder).ToList() });
foreach (var group in groupedCameraCanvases)
{
var depthItem = new TreeViewItem { id = group.Depth.GetHashCode(), depth = 1, displayName = $"Depth = {group.Depth}" };
cameraItem.AddChild(depthItem);
// Sorting Layerごとにさらにグループ化
var groupedByLayer = group.Canvases.GroupBy(c => c.sortingLayerName)
.Select(g => new { Layer = g.Key, Canvases = g.OrderBy(c => c.sortingOrder).ToList() });
foreach (var layerGroup in groupedByLayer)
{
var layerItem = new TreeViewItem { id = layerGroup.Layer.GetHashCode(), depth = 2, displayName = $"Sorting Layer = {layerGroup.Layer}" };
depthItem.AddChild(layerItem);
foreach (var canvas in layerGroup.Canvases)
{
var canvasItem = new TreeViewItem { id = canvas.GetInstanceID(), depth = 3, displayName = $"Order in Layer = {canvas.sortingOrder}" };
layerItem.AddChild(canvasItem);
// 子キャンバスを追加
AddChildCanvases(canvasItem, canvas, "Order in Layer");
}
}
}
var overlayItem = new TreeViewItem { id = 2, depth = 0, displayName = "Overlay" };
root.AddChild(overlayItem);
// Sort Orderでソート
var sortedOverlayCanvases = overlayCanvases.OrderBy(c => c.sortingOrder).ToList();
foreach (var canvas in sortedOverlayCanvases)
{
var canvasItem = new TreeViewItem { id = canvas.GetInstanceID(), depth = 1, displayName = $"Sort Order = {canvas.sortingOrder}" };
overlayItem.AddChild(canvasItem);
// 子キャンバスを追加
AddChildCanvases(canvasItem, canvas, "Sort Order");
}
// ルートの深さを設定
SetupDepthsFromParentsAndChildren(root);
return root;
}
/// <summary>
/// キャンバスの子キャンバスを追加
/// </summary>
private void AddChildCanvases(TreeViewItem parentItem, Canvas parentCanvas, string orderType)
{
var children = parentCanvas.GetComponentsInChildren<Canvas>(true).Where(c => c != parentCanvas).ToList();
foreach (var childCanvas in children)
{
if (!_displayedCanvasIds.Contains(childCanvas.GetInstanceID()))
{
var childItem = new TreeViewItem { id = childCanvas.GetInstanceID(), depth = parentItem.depth + 1, displayName = $"{orderType} = {childCanvas.sortingOrder}" };
parentItem.AddChild(childItem);
_displayedCanvasIds.Add(childCanvas.GetInstanceID());
AddChildCanvases(childItem, childCanvas, orderType);
}
}
}
/// <summary>
/// ツリーの行を描画
/// </summary>
protected override void RowGUI(RowGUIArgs args)
{
var canvas = EditorUtility.InstanceIDToObject(args.item.id) as Canvas;
Rect rect = args.rowRect;
rect.x += GetContentIndent(args.item);
rect.width -= GetContentIndent(args.item);
EditorGUI.DrawRect(new Rect(rect.x, rect.y, rect.width, rect.height), new Color(0.1f, 0.1f, 0.1f, 0.2f));
// キャンバスの情報を表示
EditorGUI.LabelField(new Rect(rect.x, rect.y, 150, rect.height), args.item.displayName);
// キャンバスの参照を表示
if (canvas != null)
{
EditorGUI.ObjectField(new Rect(rect.x + 150, rect.y, rect.width - 150, rect.height), canvas.gameObject, typeof(GameObject), true);
}
}
}
#endif
}
Discussion