mirror of
https://github.com/DCFApixels/DragonECS-Unity.git
synced 2026-04-21 20:15:56 +08:00
add QuerySnapshotWindow
This commit is contained in:
parent
edc1190a40
commit
f8b414d926
@ -84,15 +84,15 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private bool _isShowWrappedMemberMeta = true;
|
||||
public bool IsShowWrappedMemberMeta
|
||||
private bool _isPauseOnSnapshot = true;
|
||||
public bool IsPauseOnSnapshot
|
||||
{
|
||||
get => _isShowWrappedMemberMeta;
|
||||
get => _isPauseOnSnapshot;
|
||||
set
|
||||
{
|
||||
if (_isShowWrappedMemberMeta != value)
|
||||
if (_isPauseOnSnapshot != value)
|
||||
{
|
||||
_isShowWrappedMemberMeta = value;
|
||||
_isPauseOnSnapshot = value;
|
||||
AutoSave();
|
||||
}
|
||||
}
|
||||
|
||||
234
src/DebugUtils/Monitors/Editor/QuerySnapshotWindow.cs
Normal file
234
src/DebugUtils/Monitors/Editor/QuerySnapshotWindow.cs
Normal file
@ -0,0 +1,234 @@
|
||||
#if UNITY_EDITOR
|
||||
using DCFApixels.DragonECS.Core.Unchecked;
|
||||
using DCFApixels.DragonECS.Unity.Internal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static DCFApixels.DragonECS.Unity.Editors.DragonGUI;
|
||||
|
||||
namespace DCFApixels.DragonECS.Unity.Editors
|
||||
{
|
||||
internal class QuerySnapshotWindow : EditorWindow
|
||||
{
|
||||
private EcsWorld _world;
|
||||
private StructList<entlong> _list;
|
||||
|
||||
private readonly Color _selectionColor = new Color(0.12f, 0.5f, 1f, 0.40f);
|
||||
private readonly Color _isAliveColor = new Color(0.2f, 0.6f, 1f);
|
||||
private readonly Color _hoverColor = new Color(1f, 1f, 1f, 0.12f);
|
||||
|
||||
public static void ShowNew(EcsSpan entites)
|
||||
{
|
||||
var newWin = CreateWindow<QuerySnapshotWindow>("Query Snapshot");
|
||||
newWin.Setup(entites);
|
||||
newWin.ShowUtility();
|
||||
|
||||
if (UserSettingsPrefs.instance.IsPauseOnSnapshot)
|
||||
{
|
||||
Debug.Break();
|
||||
}
|
||||
}
|
||||
private void Setup(EcsSpan entites)
|
||||
{
|
||||
_world = entites.World;
|
||||
_list = new StructList<entlong>(entites.Count);
|
||||
_list._count = entites.Longs.ToArray(ref _list._items);
|
||||
}
|
||||
private Vector2 _scrollState;
|
||||
private void OnGUI()
|
||||
{
|
||||
if (_world.IsDestroyed) { _world = null; }
|
||||
if (_world == null)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
int selectedEntity = -1;
|
||||
var selectedGO = Selection.activeGameObject;
|
||||
if (selectedGO != null &&
|
||||
selectedGO.TryGetComponent<EntityMonitor>(out var selectedMonitor) &&
|
||||
selectedMonitor.Entity.TryUnpack(_world, out selectedEntity) == false)
|
||||
{
|
||||
selectedEntity = -1;
|
||||
}
|
||||
SelectEvent selectEvent = default;
|
||||
|
||||
|
||||
var line = EditorGUIUtility.singleLineHeight;
|
||||
var space = EditorGUIUtility.standardVerticalSpacing;
|
||||
var step = line + space;
|
||||
Event current = Event.current;
|
||||
|
||||
if (hasFocus && current.type == EventType.KeyUp && current.isKey)
|
||||
{
|
||||
if (current.keyCode == KeyCode.DownArrow)
|
||||
{
|
||||
selectEvent.Type = SelectEventType.Down;
|
||||
}
|
||||
if (current.keyCode == KeyCode.UpArrow)
|
||||
{
|
||||
selectEvent.Type = SelectEventType.Up;
|
||||
}
|
||||
}
|
||||
|
||||
var rect = position;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
|
||||
Rect hyperlinkButtonRect;
|
||||
Rect topLineRect;
|
||||
(topLineRect, rect) = rect.VerticalSliceTop(line + space);
|
||||
topLineRect = topLineRect.AddPadding(0, 0, 0, space);
|
||||
(topLineRect, hyperlinkButtonRect) = topLineRect.HorizontalSliceRight(18f);
|
||||
EditorGUI.IntField(topLineRect, "World: ", _world.ID);
|
||||
using (DragonGUI.SetEnable(_world != null))
|
||||
{
|
||||
DragonGUI.WorldHyperlinkButton(hyperlinkButtonRect, _world);
|
||||
}
|
||||
|
||||
(topLineRect, rect) = rect.VerticalSliceTop(line + space);
|
||||
UserSettingsPrefs.instance.IsPauseOnSnapshot = EditorGUI.ToggleLeft(topLineRect, "Pause On Snapshot", UserSettingsPrefs.instance.IsPauseOnSnapshot);
|
||||
|
||||
var viewRect = rect;
|
||||
viewRect.x = 0;
|
||||
viewRect.y = 0;
|
||||
viewRect.height = (line + space) * _list.Count;
|
||||
viewRect.xMax -= GUI.skin.verticalScrollbar.fixedWidth;
|
||||
|
||||
|
||||
var lineRect = viewRect;
|
||||
lineRect.height = line;
|
||||
Rect statusR;
|
||||
(statusR, lineRect) = lineRect.HorizontalSliceLeft(3f);
|
||||
|
||||
_scrollState = GUI.BeginScrollView(rect, _scrollState, viewRect, false, true);
|
||||
var checkRect = rect;
|
||||
checkRect.position = Vector2.zero;
|
||||
|
||||
bool foundSelected = false;
|
||||
for (int i = 0; i < _list.Count; i++)
|
||||
{
|
||||
EntitySlotInfo entity = (EntitySlotInfo)_list[i];
|
||||
bool isAlive = _world.IsAlive(entity.id, entity.gen);
|
||||
bool selected = selectedEntity == entity.id && isAlive;
|
||||
foundSelected |= selected;
|
||||
bool isClick = false;
|
||||
|
||||
|
||||
bool visible = lineRect.Overlaps(checkRect.AddOffset(_scrollState));
|
||||
if (visible)
|
||||
{
|
||||
using (DragonGUI.SetAlpha(0)) { GUI.Label(lineRect, string.Empty, GUI.skin.button); }
|
||||
if (DragonGUI.HitTest(lineRect))
|
||||
{
|
||||
EditorGUI.DrawRect(lineRect, _hoverColor);
|
||||
if (current.type == EventType.MouseUp)
|
||||
{
|
||||
isClick = true;
|
||||
}
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
DragonGUI.DrawRect(lineRect, _selectionColor);
|
||||
}
|
||||
if (isAlive)
|
||||
{
|
||||
DragonGUI.DrawRect(statusR, _isAliveColor);
|
||||
}
|
||||
var (labelR, infoR) = lineRect.HorizontalSliceLeft(45f);
|
||||
infoR.width = Mathf.Min(infoR.width, 200f);
|
||||
var (lR, rR) = infoR.HorizontalSliceLerp(0.5f);
|
||||
GUI.Label(labelR, "Entity", GUI.skin.label);
|
||||
EditorGUI.IntField(lR, entity.id, GUI.skin.label);
|
||||
EditorGUI.IntField(rR, entity.gen, GUI.skin.label);
|
||||
|
||||
if (isClick && isAlive)
|
||||
{
|
||||
selectEvent.Type = SelectEventType.Click;
|
||||
selectEvent.EntityID = entity.id;
|
||||
selectEvent.Index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAlive)
|
||||
{
|
||||
switch (selectEvent.Type)
|
||||
{
|
||||
case SelectEventType.Up:
|
||||
{
|
||||
if (foundSelected == false)
|
||||
{
|
||||
selectEvent.EntityID = entity.id;
|
||||
selectEvent.Index = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SelectEventType.Down:
|
||||
{
|
||||
if (foundSelected && selected == false && selectEvent.EntityID == 0)
|
||||
{
|
||||
selectEvent.EntityID = entity.id;
|
||||
selectEvent.Index = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineRect.y += step;
|
||||
statusR.y += step;
|
||||
}
|
||||
GUI.EndScrollView();
|
||||
|
||||
|
||||
if (selectEvent.IsSelected && selectedEntity != selectEvent.EntityID)
|
||||
{
|
||||
float top = selectEvent.Index * step;
|
||||
float bottom = top + step;
|
||||
|
||||
if (top < _scrollState.y)
|
||||
{
|
||||
_scrollState.y = top;
|
||||
}
|
||||
else if (bottom > _scrollState.y + checkRect.height)
|
||||
{
|
||||
_scrollState.y = bottom - checkRect.height;
|
||||
}
|
||||
GUIUtility.keyboardControl = 0;
|
||||
|
||||
SelectEntity(selectEvent.EntityID);
|
||||
}
|
||||
|
||||
|
||||
if (selectEvent.IsSelected)
|
||||
{
|
||||
Repaint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectEntity(int entityID)
|
||||
{
|
||||
var monitor = _world.Get<EntityLinksComponent>().GetMonitorLink(entityID);
|
||||
EditorGUIUtility.PingObject(monitor);
|
||||
Selection.activeObject = monitor;
|
||||
}
|
||||
private void OnDestroy() { }
|
||||
|
||||
|
||||
private struct SelectEvent
|
||||
{
|
||||
public SelectEventType Type;
|
||||
public int EntityID;
|
||||
public int Index;
|
||||
public bool IsSelected { get { return Type != SelectEventType.None && EntityID != 0; } }
|
||||
}
|
||||
private enum SelectEventType
|
||||
{
|
||||
None,
|
||||
Click,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
src/DebugUtils/Monitors/Editor/QuerySnapshotWindow.cs.meta
Normal file
11
src/DebugUtils/Monitors/Editor/QuerySnapshotWindow.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09884db6f09e654498c72416a199e357
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,5 +1,5 @@
|
||||
#if UNITY_EDITOR
|
||||
using DCFApixels.DragonECS.UncheckedCore;
|
||||
using DCFApixels.DragonECS.Core.Unchecked;
|
||||
using DCFApixels.DragonECS.Unity.Internal;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
|
||||
@ -23,7 +23,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
_pattern = pattern ?? throw new ArgumentNullException(nameof(pattern));
|
||||
_separator = separator;
|
||||
}
|
||||
public Enumerator GetEnumerator() => new Enumerator(_pattern, _separator);
|
||||
public Enumerator GetEnumerator() { return new Enumerator(_pattern, _separator); }
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private readonly string _pattern;
|
||||
@ -43,25 +43,24 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
|
||||
public ReadOnlySpan<char> Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentStart < 0)
|
||||
throw new InvalidOperationException("Enumeration not started or already finished");
|
||||
return _pattern.AsSpan(_currentStart, _currentLength);
|
||||
}
|
||||
get { return _pattern.AsSpan(_currentStart, _currentLength); }
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_pattern == null || _start > _pattern.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = _pattern.Length;
|
||||
while (_start <= len)
|
||||
{
|
||||
int i = _start;
|
||||
while (i < len && _pattern[i] != _separator)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
int subLen = i - _start;
|
||||
if (subLen > 0) // возвращаем только непустые подстроки
|
||||
@ -143,8 +142,10 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
|
||||
var incs = query.Mask.Incs;
|
||||
var excs = query.Mask.Excs;
|
||||
var anys = query.Mask.Anys;
|
||||
var incsI = 0;
|
||||
var excsI = 0;
|
||||
var anysI = 0;
|
||||
for (int j = 0; j < allpools.Length; j++)
|
||||
{
|
||||
var pool = allpools[j];
|
||||
@ -165,6 +166,13 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
excsI++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (anysI < anys.Length && anys[anysI] == j)
|
||||
{
|
||||
sb.Append($"~");
|
||||
anysI++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
@ -194,8 +202,9 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.IntField("Count: ", executors.Count);
|
||||
GUILayout.Space(10f);
|
||||
|
||||
EditorGUILayout.IntField("Total Count: ", executors.Count);
|
||||
|
||||
HasSearchPattern = true;
|
||||
if (string.IsNullOrEmpty(Target.SearchPattern))
|
||||
@ -203,13 +212,15 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
Target.SearchPattern = string.Empty;
|
||||
HasSearchPattern = false;
|
||||
}
|
||||
GUILayout.Space(10f);
|
||||
|
||||
Target.SearchPattern = EditorGUILayout.TextField("Search: ", Target.SearchPattern);
|
||||
|
||||
|
||||
Target.SearchPattern = EditorGUILayout.TextField(Target.SearchPattern, EditorStyles.toolbarSearchField);
|
||||
string searchPattern = Target.SearchPattern;
|
||||
|
||||
GUILayout.Space(20);
|
||||
var r = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, 3f);
|
||||
DragonGUI.DrawRect(r, Color.white.SetAlpha(0.5f));
|
||||
GUILayout.Space(10f);
|
||||
|
||||
|
||||
//using (EcsGUI.Layout.BeginVertical(UnityEditorUtility.GetStyle(Color.black, 0.2f)))
|
||||
{
|
||||
@ -273,10 +284,15 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
var mask = executor.Mask;
|
||||
DrawConstraint("+", mask.Incs);
|
||||
DrawConstraint("-", mask.Excs);
|
||||
DrawConstraint("~", mask.Anys);
|
||||
}
|
||||
|
||||
EditorGUILayout.LongField("Version: ", executor.Version);
|
||||
EditorGUILayout.IntField("Entites Count: ", executor.LastCachedCount);
|
||||
if (GUILayout.Button("Snapshot"))
|
||||
{
|
||||
QuerySnapshotWindow.ShowNew(executor.Snapshot());
|
||||
}
|
||||
|
||||
//var rect = GUILayoutUtility.GetLastRect();
|
||||
//
|
||||
|
||||
@ -19,6 +19,9 @@ namespace DCFApixels.DragonECS.Unity.Internal
|
||||
public void Set(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
#if UNITY_EDITOR
|
||||
world.Get<DragonGUI.EntityLinksComponent>().SetWorldMonitor(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,5 +133,7 @@ namespace DCFApixels.DragonECS.Unity.Internal
|
||||
_entityMonitorRef.Set(_world.GetEntityLong(entityID));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMigrateEntity(int entityID) { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
{
|
||||
EntityField(UnityEditorUtility.GetLabel(label), entity);
|
||||
}
|
||||
public static unsafe void EntityField(GUIContent label, entlong entity)
|
||||
public static void EntityField(GUIContent label, entlong entity)
|
||||
{
|
||||
float width = EditorGUIUtility.currentViewWidth;
|
||||
float height = EntityBarHeight;
|
||||
|
||||
@ -464,7 +464,25 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
DrawIcon(position, Icons.Instance.HelpIcon, 0, description);
|
||||
}
|
||||
}
|
||||
public static void WorldHyperlinkButton(Rect position, EcsWorld world)
|
||||
{
|
||||
var current = Event.current;
|
||||
var hover = IconHoverScan(position, current);
|
||||
|
||||
var click = IconButton(position, Icons.Instance.HyperlinkIcon, 2f, string.Empty);
|
||||
if (GUI.enabled)
|
||||
{
|
||||
if (click)
|
||||
{
|
||||
var monitor = world.Get<EntityLinksComponent>().GetWorldMonitor();
|
||||
if (monitor != null)
|
||||
{
|
||||
EditorGUIUtility.PingObject(monitor);
|
||||
Selection.activeObject = monitor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void EntityHyperlinkButton(Rect position, EcsWorld world, int entityID)
|
||||
{
|
||||
var current = Event.current;
|
||||
@ -565,13 +583,15 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
{
|
||||
private readonly Storage _storage;
|
||||
private EntityLinksComponent(Storage storage) { _storage = storage; }
|
||||
public void SetConnectLink(int entityID, EcsEntityConnect link) { _storage.links[entityID].connect = link; }
|
||||
public void SetMonitorLink(int entityID, EntityMonitor link) { _storage.links[entityID].monitor = link; }
|
||||
public EcsEntityConnect GetConnectLink(int entityID) { return _storage.links[entityID].connect; }
|
||||
public EntityMonitor GetMonitorLink(int entityID) { return _storage.links[entityID].monitor; }
|
||||
public void SetWorldMonitor(WorldMonitor monitor) { _storage.WorldMonitor = monitor; }
|
||||
public WorldMonitor GetWorldMonitor() { return _storage.WorldMonitor; }
|
||||
public void SetConnectLink(int entityID, EcsEntityConnect link) { _storage.Links[entityID].connect = link; }
|
||||
public void SetMonitorLink(int entityID, EntityMonitor link) { _storage.Links[entityID].monitor = link; }
|
||||
public EcsEntityConnect GetConnectLink(int entityID) { return _storage.Links[entityID].connect; }
|
||||
public EntityMonitor GetMonitorLink(int entityID) { return _storage.Links[entityID].monitor; }
|
||||
public UnityEngine.Object GetLink(int entityID)
|
||||
{
|
||||
ref var links = ref _storage.links[entityID];
|
||||
ref var links = ref _storage.Links[entityID];
|
||||
if (links.connect != null)
|
||||
{
|
||||
return links.connect;
|
||||
@ -588,15 +608,16 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
}
|
||||
private class Storage : IEcsWorldEventListener
|
||||
{
|
||||
private readonly EcsWorld _world;
|
||||
public (EcsEntityConnect connect, EntityMonitor monitor)[] links;
|
||||
public readonly EcsWorld World;
|
||||
public WorldMonitor WorldMonitor;
|
||||
public (EcsEntityConnect connect, EntityMonitor monitor)[] Links;
|
||||
public Storage(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
_world.AddListener(this);
|
||||
links = new (EcsEntityConnect, EntityMonitor)[_world.Capacity];
|
||||
World = world;
|
||||
World.AddListener(this);
|
||||
Links = new (EcsEntityConnect, EntityMonitor)[World.Capacity];
|
||||
}
|
||||
public void OnWorldResize(int newSize) { Array.Resize(ref links, newSize); }
|
||||
public void OnWorldResize(int newSize) { Array.Resize(ref Links, newSize); }
|
||||
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
|
||||
public void OnWorldDestroy() { }
|
||||
}
|
||||
@ -605,7 +626,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
|
||||
{
|
||||
EntityField(position, DragonGUIContent.Empty, entity);
|
||||
}
|
||||
public static unsafe void EntityField(Rect position, DragonGUIContent label, entlong entity)
|
||||
public static void EntityField(Rect position, DragonGUIContent label, entlong entity)
|
||||
{
|
||||
EntityField(position, label, (EntitySlotInfo)entity);
|
||||
}
|
||||
|
||||
@ -6,6 +6,12 @@ namespace DCFApixels.DragonECS.Unity.Internal
|
||||
{
|
||||
internal static class RectUtility
|
||||
{
|
||||
public static Rect AddOffset(in this Rect rect, Vector2 offset)
|
||||
{
|
||||
var r = rect;
|
||||
r.position += offset;
|
||||
return r;
|
||||
}
|
||||
public static (Rect, Rect) HorizontalSliceLerp(in this Rect rect, float t)
|
||||
{
|
||||
Rect l = rect;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#if UNITY_EDITOR
|
||||
using DCFApixels.DragonECS.Unity.Editors;
|
||||
using DCFApixels.DragonECS.Unity.Internal;
|
||||
using DCFApixels.DragonECS.Unity.RefRepairer.Editors;
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user