Add final evaluation report for Minimap system after all fixes and improvements
- Summarized the evolution of scores across five review rounds - Detailed the status of each evaluation dimension post-fixes - Highlighted remaining issues and recommended future work for further enhancements - Compared current system against industry benchmarks
This commit is contained in:
148
Assets/_Game/Scripts/Editor/World/Map/MapDatabaseEditor.cs
Normal file
148
Assets/_Game/Scripts/Editor/World/Map/MapDatabaseEditor.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using BaseGames.World.Map;
|
||||
|
||||
namespace BaseGames.Editor.Map
|
||||
{
|
||||
/// <summary>
|
||||
/// MapDatabaseSO 的自定义 Inspector:
|
||||
/// <list type="bullet">
|
||||
/// <item>显示房间总数、已配置出口数</item>
|
||||
/// <item>一键 "重新验证",将错误以 HelpBox 列出</item>
|
||||
/// <item>一键 "打开布局编辑器" 跳转到 MapLayoutEditorWindow</item>
|
||||
/// <item>可展开的房间列表,每行点击可 Ping 对应 SO</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(MapDatabaseSO))]
|
||||
public class MapDatabaseEditor : UnityEditor.Editor
|
||||
{
|
||||
private MapDatabaseSO _database;
|
||||
private List<string> _lastErrors;
|
||||
private bool _roomListExpanded;
|
||||
private Vector2 _roomListScroll;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存的错误房间 ID 集合,仅在"重新验证"按钮点击时重建。
|
||||
/// 避免 OnInspectorGUI(高频重绘)每次都重建 O(N²) 扫描。
|
||||
/// </summary>
|
||||
private readonly HashSet<string> _cachedErrorRoomIds = new();
|
||||
|
||||
private static readonly GUIContent LabelValidate = new GUIContent("重新验证", "检查重复 RoomId、出口目标缺失、房间网格重叠等问题");
|
||||
private static readonly GUIContent LabelOpenEditor = new GUIContent("打开布局编辑器", "在独立窗口中预览全局地图布局");
|
||||
|
||||
private void OnEnable() => _database = (MapDatabaseSO)target;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// ── 统计摘要 ──────────────────────────────────────────────────────
|
||||
int roomCount = _database.AllRooms?.Length ?? 0;
|
||||
int exitCount = 0;
|
||||
if (_database.AllRooms != null)
|
||||
foreach (var r in _database.AllRooms)
|
||||
if (r?.Exits != null) exitCount += r.Exits.Length;
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("📊 数据库统计", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.LabelField("房间总数", roomCount.ToString());
|
||||
EditorGUILayout.LabelField("出口总数", exitCount.ToString());
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 操作按钮 ──────────────────────────────────────────────────────
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button(LabelValidate, GUILayout.Height(28)))
|
||||
{
|
||||
_lastErrors = _database.ValidateAll();
|
||||
|
||||
// 构建错误 RoomId 集合(使用引号匹配,防止 "Room_A1" 误匹配 "Room_A10")
|
||||
_cachedErrorRoomIds.Clear();
|
||||
if (_lastErrors.Count > 0 && _database.AllRooms != null)
|
||||
foreach (var err in _lastErrors)
|
||||
foreach (var r in _database.AllRooms)
|
||||
if (r != null && err.Contains($"'{r.RoomId}'"))
|
||||
_cachedErrorRoomIds.Add(r.RoomId);
|
||||
|
||||
if (_lastErrors.Count == 0)
|
||||
Debug.Log("[MapDatabase] 验证通过,未发现问题 ✓");
|
||||
else
|
||||
Debug.LogWarning($"[MapDatabase] 发现 {_lastErrors.Count} 项问题,详见 Inspector。");
|
||||
}
|
||||
|
||||
if (GUILayout.Button(LabelOpenEditor, GUILayout.Height(28)))
|
||||
{
|
||||
var win = EditorWindow.GetWindow<MapLayoutEditorWindow>("地图布局编辑器");
|
||||
win.SetDatabase(_database);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 验证结果 ──────────────────────────────────────────────────────
|
||||
if (_lastErrors != null)
|
||||
{
|
||||
if (_lastErrors.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("验证通过,无问题 ✓", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorText = string.Join("\n", _lastErrors);
|
||||
EditorGUILayout.HelpBox(errorText, MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 默认字段(AllRooms 数组等) ───────────────────────────────────
|
||||
DrawDefaultInspector();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 可折叠房间列表(快速总览) ────────────────────────────────────
|
||||
_roomListExpanded = EditorGUILayout.Foldout(_roomListExpanded, $"房间列表({roomCount})", true);
|
||||
if (_roomListExpanded && _database.AllRooms != null)
|
||||
{
|
||||
_roomListScroll = EditorGUILayout.BeginScrollView(_roomListScroll, GUILayout.MaxHeight(200));
|
||||
|
||||
foreach (var room in _database.AllRooms)
|
||||
{
|
||||
if (room == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("空引用(AllRooms 中有 null 元素)", MessageType.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasError = _cachedErrorRoomIds.Contains(room.RoomId);
|
||||
var rowStyle = hasError
|
||||
? new GUIStyle(EditorStyles.label) { normal = { textColor = new Color(1f, 0.35f, 0.35f) } }
|
||||
: EditorStyles.label;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(
|
||||
$"{(hasError ? "⚠ " : "")}{room.RoomId} [{room.GridPosition.x},{room.GridPosition.y}] {room.GridSize.x}×{room.GridSize.y}",
|
||||
rowStyle);
|
||||
if (GUILayout.Button("Ping", EditorStyles.miniButton, GUILayout.Width(40)))
|
||||
{
|
||||
Selection.activeObject = room;
|
||||
EditorGUIUtility.PingObject(room);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
391
Assets/_Game/Scripts/Editor/World/Map/MapLayoutEditorWindow.cs
Normal file
391
Assets/_Game/Scripts/Editor/World/Map/MapLayoutEditorWindow.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using BaseGames.World.Map;
|
||||
|
||||
namespace BaseGames.Editor.Map
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局地图布局预览编辑器窗口(架构 15_MapShopModule §5.2)。
|
||||
/// 菜单:BaseGames/Map/Map Layout Editor
|
||||
/// <list type="bullet">
|
||||
/// <item>以格子坐标显示 MapDatabaseSO 中所有房间</item>
|
||||
/// <item>鼠标滚轮缩放,中键(或 Alt+左键)拖拽平移</item>
|
||||
/// <item>按区域自动着色,红色高亮配置错误(重叠/重复)</item>
|
||||
/// <item>左键点击房间:Selection + Ping 对应 SO,并在 Inspector 中打开</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public class MapLayoutEditorWindow : EditorWindow
|
||||
{
|
||||
[MenuItem("BaseGames/Map/Map Layout Editor", priority = 100)]
|
||||
public static void ShowWindow() => GetWindow<MapLayoutEditorWindow>("地图布局编辑器");
|
||||
|
||||
// ── 状态 ──────────────────────────────────────────────────────────────
|
||||
|
||||
private MapDatabaseSO _database;
|
||||
private Vector2 _panOffset;
|
||||
private float _zoom = 24f; // px / 格
|
||||
private bool _isDragging;
|
||||
private Vector2 _dragStart;
|
||||
private MapRoomDataSO _selectedRoom;
|
||||
private List<string> _validationErrors;
|
||||
private HashSet<string> _errorRoomIds;
|
||||
|
||||
/// <summary>缓存的错误房间集合,由验证按钮点击时重建(防止 OnInspectorGUI 高频重建导致 GC)。</summary>
|
||||
private readonly HashSet<string> _cachedErrorRoomIds = new();
|
||||
|
||||
private readonly Dictionary<string, Color> _regionColors = new();
|
||||
private int _paletteIndex;
|
||||
|
||||
// 缓存 GUIStyle,避免每帧每格重新分配(约 100 房间 × 60fps = 大量 GC 压力)
|
||||
private GUIStyle _roomLabelStyle;
|
||||
private GUIStyle _badgeBossStyle;
|
||||
private GUIStyle _badgeNormalStyle;
|
||||
private float _cachedZoomForStyle = -1f; // 用于检测缩放变化时重建 Style
|
||||
|
||||
// 区域配色方案(与 MapExploration 标注颜色视觉呼应)
|
||||
private static readonly Color[] Palette =
|
||||
{
|
||||
new Color(0.30f, 0.60f, 1.00f),
|
||||
new Color(0.30f, 1.00f, 0.55f),
|
||||
new Color(1.00f, 0.60f, 0.25f),
|
||||
new Color(0.85f, 0.30f, 0.90f),
|
||||
new Color(1.00f, 0.88f, 0.20f),
|
||||
new Color(0.25f, 0.90f, 1.00f),
|
||||
new Color(1.00f, 0.45f, 0.45f),
|
||||
new Color(0.50f, 1.00f, 0.80f),
|
||||
};
|
||||
|
||||
// ── 主 GUI ────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawToolbar();
|
||||
|
||||
if (_database == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("请在上方选择 MapDatabaseSO 资产以预览地图布局。", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证错误摘要
|
||||
if (_validationErrors != null && _validationErrors.Count > 0)
|
||||
{
|
||||
int shown = Mathf.Min(3, _validationErrors.Count);
|
||||
string msg = string.Join("\n", _validationErrors.GetRange(0, shown));
|
||||
if (_validationErrors.Count > shown) msg += $"\n…(共 {_validationErrors.Count} 项)";
|
||||
EditorGUILayout.HelpBox(msg, MessageType.Warning);
|
||||
}
|
||||
|
||||
// 地图绘制区域
|
||||
Rect mapRect = GUILayoutUtility.GetRect(
|
||||
GUIContent.none, GUIStyle.none,
|
||||
GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
||||
|
||||
HandleInput(mapRect);
|
||||
DrawMapArea(mapRect);
|
||||
}
|
||||
|
||||
// ── 工具栏 ────────────────────────────────────────────────────────────
|
||||
|
||||
private void DrawToolbar()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
|
||||
var newDb = (MapDatabaseSO)EditorGUILayout.ObjectField(
|
||||
_database, typeof(MapDatabaseSO), false, GUILayout.Width(240));
|
||||
if (newDb != _database)
|
||||
{
|
||||
_database = newDb;
|
||||
_validationErrors = null;
|
||||
_errorRoomIds = null;
|
||||
_regionColors.Clear();
|
||||
_paletteIndex = 0;
|
||||
_selectedRoom = null;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("验 证", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
||||
RunValidation();
|
||||
|
||||
if (GUILayout.Button("重置视图", EditorStyles.toolbarButton, GUILayout.Width(72)))
|
||||
{
|
||||
_zoom = 24f;
|
||||
_panOffset = Vector2.zero;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"缩放 {_zoom:F0}px/格", GUILayout.Width(80));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
// ── 输入处理 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void HandleInput(Rect mapRect)
|
||||
{
|
||||
var e = Event.current;
|
||||
if (!mapRect.Contains(e.mousePosition)) return;
|
||||
|
||||
switch (e.type)
|
||||
{
|
||||
case EventType.ScrollWheel:
|
||||
_zoom = Mathf.Clamp(_zoom - e.delta.y * 1.8f, 6f, 96f);
|
||||
e.Use();
|
||||
Repaint();
|
||||
break;
|
||||
|
||||
// 中键拖拽 或 Alt+左键拖拽 平移
|
||||
case EventType.MouseDown when e.button == 2 || (e.button == 0 && e.alt):
|
||||
_isDragging = true;
|
||||
_dragStart = e.mousePosition;
|
||||
e.Use();
|
||||
break;
|
||||
|
||||
case EventType.MouseDrag when _isDragging:
|
||||
_panOffset += e.mousePosition - _dragStart;
|
||||
_dragStart = e.mousePosition;
|
||||
e.Use();
|
||||
Repaint();
|
||||
break;
|
||||
|
||||
case EventType.MouseUp when _isDragging:
|
||||
_isDragging = false;
|
||||
e.Use();
|
||||
break;
|
||||
|
||||
// 左键(非 Alt)点击选中房间
|
||||
case EventType.MouseUp when e.button == 0 && !e.alt:
|
||||
TrySelectRoom(e.mousePosition - mapRect.position, mapRect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 地图绘制 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void DrawMapArea(Rect mapRect)
|
||||
{
|
||||
if (_database?.AllRooms == null) return;
|
||||
|
||||
EnsureLabelStyles();
|
||||
GUI.BeginClip(mapRect);
|
||||
|
||||
Vector2 origin = mapRect.size * 0.5f + _panOffset;
|
||||
|
||||
foreach (var room in _database.AllRooms)
|
||||
{
|
||||
if (room == null) continue;
|
||||
|
||||
Rect cell = RoomToClipRect(room, origin);
|
||||
|
||||
// 填充
|
||||
bool hasError = _errorRoomIds != null && _errorRoomIds.Contains(room.RoomId);
|
||||
Color regionColor = GetRegionColor(room.RegionId);
|
||||
Color fillColor = hasError
|
||||
? new Color(1f, 0.15f, 0.15f, 0.55f)
|
||||
: new Color(regionColor.r, regionColor.g, regionColor.b, 0.28f);
|
||||
EditorGUI.DrawRect(cell, fillColor);
|
||||
|
||||
// 边框
|
||||
Color borderColor = room == _selectedRoom
|
||||
? new Color(1f, 0.9f, 0.1f, 1f)
|
||||
: new Color(regionColor.r, regionColor.g, regionColor.b, 0.85f);
|
||||
float border = room == _selectedRoom ? 2f : 1f;
|
||||
DrawBorder(cell, borderColor, border);
|
||||
|
||||
// 标签(缩放足够大时才显示,使用缓存 Style)
|
||||
if (_zoom >= 18f)
|
||||
GUI.Label(cell, string.IsNullOrEmpty(room.RoomId) ? "?" : room.RoomId, _roomLabelStyle);
|
||||
|
||||
// 特殊图标标记(Boss / 存档点 / 商店)
|
||||
DrawRoomBadge(cell, room);
|
||||
}
|
||||
|
||||
// 出口连线(缩放足够大时)
|
||||
if (_zoom >= 12f)
|
||||
DrawExitLines(origin);
|
||||
|
||||
GUI.EndClip();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按需重建 GUIStyle 缓存——仅当 zoom 发生变化(或首次调用)时重建,
|
||||
/// 避免每帧每格 <c>new GUIStyle(...)</c> 造成大量 GC 分配。
|
||||
/// </summary>
|
||||
private void EnsureLabelStyles()
|
||||
{
|
||||
if (Mathf.Approximately(_cachedZoomForStyle, _zoom) && _roomLabelStyle != null) return;
|
||||
|
||||
_cachedZoomForStyle = _zoom;
|
||||
int fontSize = Mathf.Clamp(Mathf.RoundToInt(_zoom * 0.4f), 8, 14);
|
||||
|
||||
_roomLabelStyle = new GUIStyle(EditorStyles.miniLabel)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
wordWrap = false,
|
||||
normal = { textColor = Color.white },
|
||||
fontSize = fontSize,
|
||||
};
|
||||
|
||||
int badgeSize = Mathf.Max(8, Mathf.RoundToInt(_zoom * 0.35f));
|
||||
|
||||
_badgeBossStyle = new GUIStyle(EditorStyles.miniLabel)
|
||||
{
|
||||
alignment = TextAnchor.UpperRight,
|
||||
normal = { textColor = new Color(1f, 0.4f, 0.4f) },
|
||||
fontSize = badgeSize,
|
||||
};
|
||||
|
||||
_badgeNormalStyle = new GUIStyle(EditorStyles.miniLabel)
|
||||
{
|
||||
alignment = TextAnchor.UpperRight,
|
||||
normal = { textColor = Color.cyan },
|
||||
fontSize = badgeSize,
|
||||
};
|
||||
}
|
||||
|
||||
private void DrawRoomBadge(Rect cell, MapRoomDataSO room)
|
||||
{
|
||||
if (!room.IsBossRoom && !room.IsSavePoint && !room.IsShop) return;
|
||||
if (cell.width < 8f) return;
|
||||
|
||||
string badge = room.IsBossRoom ? "★" :
|
||||
room.IsSavePoint ? "♦" : "¥";
|
||||
GUI.Label(cell, badge, room.IsBossRoom ? _badgeBossStyle : _badgeNormalStyle);
|
||||
}
|
||||
|
||||
private void DrawExitLines(Vector2 origin)
|
||||
{
|
||||
if (_database?.AllRooms == null) return;
|
||||
var lineColor = new Color(1f, 1f, 0.5f, 0.35f);
|
||||
|
||||
foreach (var room in _database.AllRooms)
|
||||
{
|
||||
if (room?.Exits == null) continue;
|
||||
foreach (var exit in room.Exits)
|
||||
{
|
||||
var target = _database.GetRoom(exit.TargetRoomId);
|
||||
if (target == null) continue;
|
||||
|
||||
Vector2 from = GridCenterToClip(room.GridPosition + room.GridSize / 2, origin);
|
||||
Vector2 to = GridCenterToClip(target.GridPosition + target.GridSize / 2, origin);
|
||||
|
||||
DrawLine(from, to, lineColor, 1.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 公共 API ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>由外部(如 MapDatabaseEditor)注入 Database,避免反射访问私有字段。</summary>
|
||||
public void SetDatabase(MapDatabaseSO db)
|
||||
{
|
||||
_database = db;
|
||||
_validationErrors = null;
|
||||
_errorRoomIds = null;
|
||||
_regionColors.Clear();
|
||||
_paletteIndex = 0;
|
||||
_selectedRoom = null;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
// ── 房间选择 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void TrySelectRoom(Vector2 clipPos, Rect mapRect)
|
||||
{
|
||||
if (_database?.AllRooms == null) return;
|
||||
|
||||
// 使用与 DrawMapArea 完全相同的 origin 公式,确保命中测试一致
|
||||
Vector2 origin = mapRect.size * 0.5f + _panOffset;
|
||||
|
||||
foreach (var room in _database.AllRooms)
|
||||
{
|
||||
if (room == null) continue;
|
||||
Rect cell = RoomToClipRect(room, origin);
|
||||
if (cell.Contains(clipPos))
|
||||
{
|
||||
_selectedRoom = room;
|
||||
Selection.activeObject = room;
|
||||
EditorGUIUtility.PingObject(room);
|
||||
Repaint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_selectedRoom = null;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
// ── 工具方法 ──────────────────────────────────────────────────────────
|
||||
|
||||
private Rect RoomToClipRect(MapRoomDataSO room, Vector2 origin)
|
||||
{
|
||||
float x = origin.x + room.GridPosition.x * _zoom;
|
||||
// Y 轴:格子坐标 Y 向上,屏幕 Y 向下,需翻转
|
||||
float y = origin.y - (room.GridPosition.y + room.GridSize.y) * _zoom;
|
||||
return new Rect(x, y, room.GridSize.x * _zoom, room.GridSize.y * _zoom);
|
||||
}
|
||||
|
||||
private Vector2 GridCenterToClip(Vector2Int gridPos, Vector2 origin)
|
||||
=> new Vector2(origin.x + gridPos.x * _zoom, origin.y - gridPos.y * _zoom);
|
||||
|
||||
private Color GetRegionColor(string regionId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(regionId)) return new Color(0.55f, 0.55f, 0.55f);
|
||||
if (!_regionColors.TryGetValue(regionId, out var c))
|
||||
{
|
||||
c = Palette[_paletteIndex % Palette.Length];
|
||||
_paletteIndex++;
|
||||
_regionColors[regionId] = c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private void RunValidation()
|
||||
{
|
||||
_validationErrors = _database?.ValidateAll() ?? new List<string>();
|
||||
_errorRoomIds = new HashSet<string>();
|
||||
if (_validationErrors != null && _database?.AllRooms != null)
|
||||
foreach (var err in _validationErrors)
|
||||
foreach (var r in _database.AllRooms)
|
||||
if (r != null && err.Contains($"'{r.RoomId}'"))
|
||||
_errorRoomIds.Add(r.RoomId);
|
||||
Repaint();
|
||||
}
|
||||
|
||||
// ── 绘图辅助 ──────────────────────────────────────────────────────────
|
||||
|
||||
private static void DrawBorder(Rect r, Color color, float t)
|
||||
{
|
||||
var prev = GUI.color;
|
||||
GUI.color = color;
|
||||
GUI.DrawTexture(new Rect(r.x, r.y, r.width, t), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(r.x, r.yMax - t, r.width, t), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(r.x, r.y, t, r.height), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(r.xMax - t, r.y, t, r.height), EditorGUIUtility.whiteTexture);
|
||||
GUI.color = prev;
|
||||
}
|
||||
|
||||
// 用细矩形近似绘制连线(无需 GL/Handles)
|
||||
private static void DrawLine(Vector2 a, Vector2 b, Color color, float thickness)
|
||||
{
|
||||
Vector2 dir = b - a;
|
||||
float len = dir.magnitude;
|
||||
if (len < 1f) return;
|
||||
|
||||
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
|
||||
Vector2 mid = (a + b) * 0.5f;
|
||||
|
||||
var prevMatrix = GUI.matrix;
|
||||
var prevColor = GUI.color;
|
||||
GUI.color = color;
|
||||
GUIUtility.RotateAroundPivot(angle, mid);
|
||||
GUI.DrawTexture(new Rect(mid.x - len * 0.5f, mid.y - thickness * 0.5f, len, thickness),
|
||||
EditorGUIUtility.whiteTexture);
|
||||
GUI.matrix = prevMatrix;
|
||||
GUI.color = prevColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user