Created
November 2, 2024 02:35
-
-
Save kankikuchi/a2726fdf7bfb132bc413d426983fc115 to your computer and use it in GitHub Desktop.
エディタ上でグラフを表示するクラス【Unity】【エディタ拡張】
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// http://kan-kikuchi.hatenablog.com/entry/EditorGraphViewer | |
// Created by kan.kikuchi | |
#if UNITY_EDITOR | |
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEditor; | |
/// <summary> | |
/// エディタ上でグラフを表示するクラス | |
/// 参考 https://qiita.com/nitto/items/8ad2027e1af2e0d7316c | |
/// </summary> | |
public static class EditorGraphViewer { | |
/// <summary> | |
/// グラフ表示 | |
/// </summary> | |
public static void ShowGraph( | |
List<Vector2>values, | |
float width = 300, float height = 200, float padding = 10, | |
float xMin = 0, float xMax = 100,float yMin = 0, float yMax = 100, | |
float xStep = 1, float yStep = 1 | |
) { | |
var settings = new AxisSettings(xRange: (xMin, xMax), yRange: (yMin, yMax), xStep:xStep, yStep:yStep); | |
var rect = GUILayoutUtility.GetRect(width, height).Padding(padding); | |
DrawBackDrop(rect, Colors.backdropColor, Colors.gridLineColor); | |
DrawGrid(rect, settings, Colors.gridLineColor); | |
DrawData(rect, settings, values.ToArray(), Colors.dataColor); | |
} | |
public static Rect Padding(this Rect rect, float padding) { | |
rect.position += new Vector2(padding, padding); | |
rect.size -= new Vector2(padding, padding) * 2f; | |
return rect; | |
} | |
/// <summary> | |
/// 点列を描画する | |
/// </summary> | |
private static void DrawData(Rect rect, AxisSettings settings, Vector2[] values, Color color) { | |
using (new GUI.ClipScope(rect)) // ※スコープ内のGUIメソッドは指定Rectを基準として処理される | |
using (new Handles.DrawingScope(color)) { | |
for (int i = 0; i < values.Length - 1; i++) { | |
Vector2 p1 = Convertor.ValueToGraphPoint(values[i], rect.size, settings); | |
Vector2 p2 = Convertor.ValueToGraphPoint(values[i + 1], rect.size, settings); | |
Handles.DrawLine(p1, p2); | |
} | |
foreach (var value in values) { | |
Vector2 p = Convertor.ValueToGraphPoint(value, rect.size, settings); | |
Handles.DrawSolidDisc(p, Vector3.forward, 2f); | |
} | |
} | |
} | |
private static void DrawGrid(Rect rect, AxisSettings settings, Color gridColor) { | |
using (new Handles.DrawingScope(gridColor)) { | |
var graphOrigin = rect.position; | |
// X軸 | |
var xStart = Mathf.Ceil(settings.xRange.min / settings.xStep) * settings.xStep; | |
var xTicks = EnumerableUtil.LinspaceWithStep(xStart, settings.xRange.max, settings.xStep); | |
foreach (var x in xTicks) { | |
var value1 = new Vector2(x, settings.yRange.min); | |
var value2 = new Vector2(x, settings.yRange.max); | |
// GUI座標に変換して描画 | |
var p1 = Convertor.ValueToGraphPoint(value1, rect.size, settings) + graphOrigin; | |
var p2 = Convertor.ValueToGraphPoint(value2, rect.size, settings) + graphOrigin; | |
Handles.DrawLine(p1, p2); | |
} | |
// Y軸 | |
var yStart = Mathf.Ceil(settings.yRange.min / settings.yStep) * settings.yStep; | |
var yTicks = EnumerableUtil.LinspaceWithStep(yStart, settings.yRange.max, settings.yStep); | |
foreach (var y in yTicks) { | |
var value1 = new Vector2(settings.xRange.min, y); | |
var value2 = new Vector2(settings.xRange.max, y); | |
// GUI座標に変換して描画 | |
var p1 = Convertor.ValueToGraphPoint(value1, rect.size, settings) + graphOrigin; | |
var p2 = Convertor.ValueToGraphPoint(value2, rect.size, settings) + graphOrigin; | |
Handles.DrawLine(p1, p2); | |
} | |
} | |
} | |
/// <summary> | |
/// 背景と枠線を描画する | |
/// </summary> | |
private static void DrawBackDrop(Rect rect, Color backdropColor, Color outlineColor) { | |
Handles.DrawSolidRectangleWithOutline(rect, backdropColor, outlineColor); | |
} | |
private static class Colors { | |
public static readonly Color backdropColor = new Color(0.8f, 0.8f, 0.8f, 0.1f); | |
public static readonly Color gridLineColor = new Color(0.9f, 0.9f, 0.9f, 0.5f); | |
public static readonly Color dataColor = Color.green; | |
} | |
private struct AxisSettings { | |
// グラフの値域 | |
public (float min, float max) xRange; | |
public (float min, float max) yRange; | |
// グリッド幅 | |
public float xStep; | |
public float yStep; | |
public AxisSettings((float min, float max) xRange, (float min, float max) yRange, float xStep, float yStep) { | |
this.xRange = xRange; | |
this.yRange = yRange; | |
this.xStep = xStep; | |
this.yStep = yStep; | |
} | |
} | |
private static class Convertor { | |
/// <summary> | |
/// グラフ範囲の座標に変換する | |
/// </summary> | |
public static Vector2 ValueToGraphPoint(Vector2 value, Vector2 size, AxisSettings settings) { | |
var xRange = settings.xRange; | |
var yRange = settings.yRange; | |
float x = Mathf.Lerp(0, size.x, (value.x - xRange.min) / (xRange.max - xRange.min)); | |
float y = Mathf.Lerp(size.y, 0, (value.y - yRange.min) / (yRange.max - yRange.min)); | |
return new Vector2(x, y); | |
} | |
} | |
private static class EnumerableUtil { | |
/// <summary> | |
/// ステップサイズで線形に配置されたデータを作成する | |
/// </summary> | |
public static IEnumerable<float> LinspaceWithStep(float start, float end, float step) { | |
if (step <= 0) throw new InvalidOperationException("Step must be positive and non-zero."); | |
int count = (int)((end - start) / step) + 1; | |
return Enumerable | |
.Range(0, count) | |
.Select(i => start + i * step) | |
.TakeWhile(value => value <= end); | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment