update? add runtime component editing

This commit is contained in:
Mikhail 2024-03-03 19:59:39 +08:00
parent ebfaf5bc98
commit 84445bfb7c
23 changed files with 492 additions and 742 deletions

View File

@ -91,7 +91,6 @@ namespace DCFApixels.DragonECS
#if UNITY_EDITOR
namespace DCFApixels.DragonECS.Unity.Editors
{
using System.Collections.Generic;
using UnityEditor;
[CustomEditor(typeof(EcsEntityConnect))]
@ -127,7 +126,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
DrawTemplates();
DrawButtons();
DrawComponents();
DrawComponents(targets);
}
private void DrawTop()
{
@ -179,9 +178,16 @@ namespace DCFApixels.DragonECS.Unity.Editors
}
else
{
EditorGUI.TextField(idRect, "-");
EditorGUI.TextField(genRect, "-");
EditorGUI.TextField(worldRect, "-");
//Color defColor = GUI.contentColor;
//Color c = defColor;
//c.a = 0.55f;
//GUI.contentColor = c;
GUI.enabled = false;
EditorGUI.TextField(idRect, "Entity ID");
EditorGUI.TextField(genRect, "Gen");
EditorGUI.TextField(worldRect, "World ID");
GUI.enabled = true;
//GUI.contentColor = defColor;
}
}
@ -217,13 +223,22 @@ namespace DCFApixels.DragonECS.Unity.Editors
}
}
private void DrawComponents()
private void DrawComponents(EcsEntityConnect[] targets)
{
if (IsMultipleTargets)
{
for (int i = 0; i < targets.Length; i++)
{
if (targets[i].IsConected == true)
{
EditorGUILayout.HelpBox("Multiple component editing is not available.", MessageType.Warning);
break;
}
}
}
if (Target.IsConected)
{
List<object> comps = new List<object>();
Target.World.GetComponents(Target.Entity.ID, comps);
GUILayout.TextArea(string.Join("\r\n", comps));
EcsGUI.Layout.DrawComponents(Target.Entity);
}
}
}

View File

@ -4,6 +4,7 @@
{
public const string DEBUG_LAYER = nameof(DEBUG_LAYER);
public EcsWorld[] _worlds;
public DebugModule(params EcsWorld[] worlds)
{
_worlds = worlds;
@ -12,10 +13,10 @@
void IEcsModule.Import(EcsPipeline.Builder b)
{
b.Layers.Insert(EcsConsts.POST_END_LAYER, DEBUG_LAYER);
b.Add(new PipelineDebugSystem(), DEBUG_LAYER);
//b.Add(new PipelineDebugSystem(), DEBUG_LAYER);
foreach (var world in _worlds)
{
b.Add(new WorldDebugSystem(world), DEBUG_LAYER);
//b.Add(new WorldDebugSystem(world), DEBUG_LAYER);
}
}
}

View File

@ -3,7 +3,7 @@ using UnityEditor;
namespace DCFApixels.DragonECS.Unity.Editors
{
[FilePath("DragonECS/DebugMonitorPrefs.prefs", FilePathAttribute.Location.ProjectFolder)]
[FilePath(EcsConsts.FRAMEWORK_NAME + "/" + nameof(DebugMonitorPrefs) + ".prefs", FilePathAttribute.Location.ProjectFolder)]
public class DebugMonitorPrefs : ScriptableSingleton<DebugMonitorPrefs>
{
private bool _isShowInterfaces = false;
@ -34,7 +34,6 @@ namespace DCFApixels.DragonECS.Unity.Editors
Save(false);
}
}
}
}
#endif

View File

@ -1,190 +0,0 @@
#if UNITY_EDITOR
using DCFApixels.DragonECS.Unity.Internal;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Editors
{
public static class EcsGUI
{
private static GUIStyle _grayStyle;
private static GUIStyle _greenStyle;
private static GUIStyle _redStyle;
private static GUILayoutOption[] _defaultParams;
private static bool _isInit = false;
private static void Init()
{
if (_isInit)
{
return;
}
_defaultParams = new GUILayoutOption[] { GUILayout.ExpandWidth(true) };
_grayStyle = EcsEditor.GetStyle(new Color32(100, 100, 100, 100));
_greenStyle = EcsEditor.GetStyle(new Color32(75, 255, 0, 100));
_redStyle = EcsEditor.GetStyle(new Color32(255, 0, 75, 100));
_isInit = true;
}
private const string CONNECTED = "Connected";
private const string NOT_CONNECTED = "Not connected";
private const string UNDETERMINED_CONNECTED = "---";
public static void DrawConnectStatus(Rect position, bool status)
{
Init();
if (status)
{
GUI.Box(position, CONNECTED, _greenStyle);
}
else
{
GUI.Box(position, NOT_CONNECTED, _redStyle);
}
}
public static void DrawUndeterminedConnectStatus(Rect position)
{
Init();
GUI.Box(position, UNDETERMINED_CONNECTED, _grayStyle);
}
public static class Layout
{
public static void DrawConnectStatus(bool status, params GUILayoutOption[] options)
{
Init();
if(options == null || options.Length <= 0)
{
options = _defaultParams;
}
if (status)
{
GUILayout.Box(CONNECTED, _greenStyle, options);
}
else
{
GUILayout.Box(NOT_CONNECTED, _redStyle, options);
}
}
public static void DrawUndeterminedConnectStatus(params GUILayoutOption[] options)
{
Init();
if (options == null || options.Length <= 0)
{
options = _defaultParams;
}
GUILayout.Box(UNDETERMINED_CONNECTED, _grayStyle, options);
}
}
}
[InitializeOnLoad]
public static class EcsEditor
{
static EcsEditor()
{
colorBoxeStyles = new SparseArray<GUIStyle>();
}
private static SparseArray<GUIStyle> colorBoxeStyles = new SparseArray<GUIStyle>();
public static GUIStyle GetStyle(Color color, float alphaMultiplier)
{
color.a *= alphaMultiplier;
return GetStyle(color);
}
public static GUIStyle GetStyle(Color32 color32)
{
int colorCode = new Color32Union(color32).colorCode;
if (colorBoxeStyles.TryGetValue(colorCode, out GUIStyle style))
{
if (style == null || style.normal.background == null)
{
style = CreateStyle(color32, colorCode);
colorBoxeStyles[colorCode] = style;
}
return style;
}
style = CreateStyle(color32, colorCode);
colorBoxeStyles.Add(colorCode, style);
return style;
}
private static GUIStyle CreateStyle(Color32 color32, int colorCode)
{
GUIStyle result = new GUIStyle(GUI.skin.box);
Color componentColor = color32;
Texture2D texture2D = CreateTexture(2, 2, componentColor);
result.hover.background = texture2D;
result.focused.background = texture2D;
result.active.background = texture2D;
result.normal.background = texture2D;
return result;
}
private static Texture2D CreateTexture(int width, int height, Color color)
{
var pixels = new Color[width * height];
for (var i = 0; i < pixels.Length; ++i)
pixels[i] = color;
var result = new Texture2D(width, height);
result.SetPixels(pixels);
result.Apply();
return result;
}
public static string GetGenericName(Type type) => EcsDebugUtility.GetGenericTypeName(type);
public static string GetName<T>() => GetName(typeof(T));
public static string GetName(Type type) => EcsDebugUtility.GetName(type);
public static string GetDescription<T>() => GetDescription(typeof(T));
public static string GetDescription(Type type) => EcsDebugUtility.GetDescription(type);
#region Utils
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
private readonly ref struct Color32Union
{
[FieldOffset(0)]
public readonly int colorCode;
[FieldOffset(0)]
public readonly byte r;
[FieldOffset(1)]
public readonly byte g;
[FieldOffset(2)]
public readonly byte b;
[FieldOffset(3)]
public readonly byte a;
public Color32Union(byte r, byte g, byte b, byte a) : this()
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color32Union(Color32 color) : this()
{
r = color.r;
g = color.g;
b = color.b;
a = color.a;
}
}
#endregion
}
public static class ReflectionExtensions
{
public static bool TryGetAttribute<T>(this MemberInfo self, out T attrbiute) where T : Attribute
{
attrbiute = self.GetCustomAttribute<T>();
return attrbiute != null;
}
public static bool HasAttribute<T>(this MemberInfo self) where T : Attribute
{
return self.GetCustomAttribute<T>() != null;
}
}
}
#endif

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a124aa9f464ff79439c89b52281215d3
guid: 8b7186fd57cbf9c42b4759c49920a688
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,31 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
[MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)]
public class EntityMonitor : MonoBehaviour, IEcsProcess
{
private entlong _entity;
private int _entityID;
private short _gen;
private EcsWorld _world;
public EcsWorld World
{
get { return _world; }
}
public int EntityID
{
get { return _entityID; }
}
public EntityMonitor(entlong entity)
{
_entity = entity;
if (_entity.TryUnpack(out _entityID, out _gen, out _world))
{
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1caa902906afea3409fc63ed94cbbc11
guid: b3c51f9f4aa13e74fbb4339f8c1746d4
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,9 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
[MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)]
public class PipelineMonitor : MonoBehaviour, IEcsProcess
{
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d36835beef2392f40854af962ff47472
guid: 03154a226ad86e24ebfa4faf43da8310
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,9 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
[MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)]
public class WorldMonitor : MonoBehaviour, IEcsProcess
{
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b7b4bd406553d64589aa4e085d4ff28
guid: 3d65916dbb4c8f74cb5239c5bbf7ba31
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,17 +0,0 @@
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Debug
{
public class DebugMonitorBase : MonoBehaviour
{
internal string monitorName;
private void Awake()
{
DontDestroyOnLoad(this.gameObject);
gameObject.SetActive(false);
}
}
}

View File

@ -1,341 +0,0 @@
using DCFApixels.DragonECS.Unity.Debug;
using UnityEngine;
using System.Linq;
namespace DCFApixels.DragonECS
{
[MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)]
public class PipelineDebugSystem : IEcsPreInit, IEcsPipelineMember
{
public EcsPipeline Pipeline { get; set; }
private string _monitorName;
public PipelineDebugSystem(string monitorName = "Pipeline")
{
_monitorName = monitorName;
}
void IEcsPreInit.PreInit()
{
PipelineDebugMonitor monitor = new GameObject(EcsConsts.DEBUG_PREFIX + _monitorName).AddComponent<PipelineDebugMonitor>();
monitor.source = this;
monitor.pipeline = Pipeline;
monitor.monitorName = _monitorName;
PipelineProcessesDebugMonitor processesMonitor = new GameObject(EcsConsts.DEBUG_PREFIX + "Processes Matrix").AddComponent<PipelineProcessesDebugMonitor>();
processesMonitor.transform.parent = monitor.transform;
processesMonitor.source = this;
processesMonitor.pipeline = Pipeline;
processesMonitor.monitorName = "Processes Matrix";
//foreach (var item in pipeline.AllSystems) //Вырезано пока не сделаю TODO в SystemDebugMonitor
//{
// DebugNameAttribute debugName = item.GetType().GetCustomAttribute<DebugNameAttribute>();
// string name = debugName == null ? item.GetType().Name : debugName.name;
// SystemDebugMonitor.CreateMonitor(monitor.transform, item, name);
//}
}
}
public class PipelineDebugMonitor : DebugMonitorBase
{
internal PipelineDebugSystem source;
internal EcsPipeline pipeline;
}
public class PipelineProcessesDebugMonitor : DebugMonitorBase
{
internal PipelineDebugSystem source;
internal EcsPipeline pipeline;
}
}
#if UNITY_EDITOR
namespace DCFApixels.DragonECS.Unity.Editors
{
using DCFApixels.DragonECS.RunnersCore;
using DCFApixels.DragonECS.Unity.Internal;
using System;
using System.Collections.Generic;
using UnityEditor;
[CustomEditor(typeof(PipelineDebugMonitor))]
public class PipelineDebugMonitorEditor : Editor
{
private MetaColorAttribute _fakeDebugColorAttribute = new MetaColorAttribute(190, 190, 190);
private Type _debugColorAttributeType = typeof(MetaColorAttribute);
private GUIStyle _headerStyle;
private GUIStyle _interfacesStyle;
private Color _interfaceColor = new Color(0.96f, 1f, 0.16f);
private PipelineDebugMonitor Target => (PipelineDebugMonitor)target;
private GUIStyle systemsListStyle;
public override void OnInspectorGUI()
{
systemsListStyle = new GUIStyle(EditorStyles.miniLabel);
systemsListStyle.wordWrap = true;
if (Target.source == null)
return;
if (_headerStyle == null)
{
_headerStyle = new GUIStyle(EditorStyles.boldLabel);
_interfacesStyle = new GUIStyle(EditorStyles.miniLabel);
_interfacesStyle.hover.textColor = _interfaceColor;
_interfacesStyle.focused.textColor = _interfaceColor;
_interfacesStyle.active.textColor = _interfaceColor;
_interfacesStyle.normal.textColor = _interfaceColor;
_interfacesStyle.wordWrap = true;
_headerStyle.fontSize = 28;
}
GUILayout.Label("[Systems]", _headerStyle);
DebugMonitorPrefs.instance.IsShowInterfaces = EditorGUILayout.Toggle("Show Interfaces", DebugMonitorPrefs.instance.IsShowInterfaces);
DebugMonitorPrefs.instance.IsShowHidden = EditorGUILayout.Toggle("Show Hidden", DebugMonitorPrefs.instance.IsShowHidden);
GUILayout.BeginVertical();
foreach (var item in Target.pipeline.AllSystems)
{
DrawSystem(item);
}
GUILayout.EndVertical();
GUILayout.Label("[Runners]", _headerStyle);
GUILayout.BeginVertical(EcsEditor.GetStyle(Color.black, 0.2f));
foreach (var item in Target.pipeline.AllRunners)
{
DrawRunner(item.Value);
}
GUILayout.EndVertical();
}
private void DrawSystem(IEcsProcess system)
{
if (system is SystemsLayerMarkerSystem markerSystem)
{
GUILayout.EndVertical();
GUILayout.BeginVertical(EcsEditor.GetStyle(Color.black, 0.2f));
GUILayout.BeginHorizontal();
GUILayout.Label("<");
GUILayout.Label($"{markerSystem.name}", EditorStyles.boldLabel);
GUILayout.Label(">", GUILayout.ExpandWidth(false));
GUILayout.EndHorizontal();
return;
}
Type type = system.GetType();
if (CheckIsHidden(type))
return;
string name = EcsEditor.GetGenericName(type);
//Color color = (GetAttribute<DebugColorAttribute>(type) ?? _fakeDebugColorAttribute).GetUnityColor();
Color color = EcsDebugUtility.GetColor(type).ToUnityColor();
GUILayout.BeginVertical(EcsEditor.GetStyle(color, 0.2f));
if (DebugMonitorPrefs.instance.IsShowInterfaces)
{
GUILayout.Label(string.Join(", ", type.GetInterfaces().Select(o => o.Name)), _interfacesStyle);
}
GUILayout.Label(name, EditorStyles.boldLabel);
GUILayout.EndVertical();
}
private void DrawRunner(IEcsRunner runner)
{
Type type = runner.GetType();
if (CheckIsHidden(type))
return;
//Color color = (GetAttribute<DebugColorAttribute>(type) ?? _fakeDebugColorAttribute).GetUnityColor();
Color color = EcsDebugUtility.GetColor(type).ToUnityColor();
GUILayout.BeginVertical(EcsEditor.GetStyle(color, 0.2f));
GUILayout.Label(EcsEditor.GetGenericName(type), EditorStyles.boldLabel);
GUILayout.Label(string.Join(", ", runner.ProcessRaw.Cast<object>().Select(o => o.GetType().Name)), systemsListStyle);
GUILayout.EndVertical();
}
private TAttribute GetAttribute<TAttribute>(Type target) where TAttribute : Attribute
{
var result = target.GetCustomAttributes(_debugColorAttributeType, false);
if (result.Length > 0)
return (TAttribute)result[0];
return null;
}
private bool CheckIsHidden(Type target)
{
if (DebugMonitorPrefs.instance.IsShowHidden)
return false;
return target.GetCustomAttribute<DebugHideAttribute>() != null;
}
}
[CustomEditor(typeof(PipelineProcessesDebugMonitor))]
public class PipelineProcessesDebugMonitorEditor : Editor
{
private bool _isInit = false;
private List<ProcessData> _processesList = new List<ProcessData>();
private Dictionary<Type, int> _processeIndexes = new Dictionary<Type, int>();
private PipelineProcessesDebugMonitor Target => (PipelineProcessesDebugMonitor)target;
private Type systemInterfaceType = typeof(IEcsProcess);
private IEcsProcess[] _systems;
private void Init()
{
if (_isInit)
return;
bool showHidden = DebugMonitorPrefs.instance.IsShowHidden;
_processesList.Clear();
_processeIndexes.Clear();
if (showHidden)
_systems = Target.pipeline.AllSystems.Where(o => o is SystemsLayerMarkerSystem == false).ToArray();
else
_systems = Target.pipeline.AllSystems.Where(o => o.GetType().GetCustomAttribute<DebugHideAttribute>() == null).ToArray();
int i = 0;
foreach (var system in _systems)
{
foreach (var intr in system.GetType().GetInterfaces())
{
if (systemInterfaceType.IsAssignableFrom(intr) && systemInterfaceType != intr && (showHidden || intr.GetCustomAttribute<DebugHideAttribute>() == null))
{
ProcessData data;
if (!_processeIndexes.TryGetValue(intr, out int index))
{
index = _processesList.Count;
_processeIndexes.Add(intr, index);
data = new ProcessData();
_processesList.Add(data);
data.name = EcsEditor.GetGenericName(intr);
data.interfaceType = intr;
data.systemsBitMask = new BitMask(_systems.Length);
}
data = _processesList[index];
data.systemsBitMask[i] = true;
}
}
i++;
}
_isInit = true;
}
private Vector2 _position;
private Vector2 _cellsize = new Vector2(EditorGUIUtility.singleLineHeight, EditorGUIUtility.singleLineHeight);
private Vector2 _nameCellSize = new Vector2(200f, 200f);
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
DebugMonitorPrefs.instance.IsShowHidden = EditorGUILayout.Toggle("Show Hidden", DebugMonitorPrefs.instance.IsShowHidden);
if (EditorGUI.EndChangeCheck())
{
_isInit = false;
}
Init();
Rect rect;
Rect lineRect;
GUILayout.Label("", GUILayout.ExpandWidth(true), GUILayout.Height(400f));
rect = GUILayoutUtility.GetLastRect();
rect.height = 400f;
Rect rectView = new Rect(0f, 0f, _nameCellSize.x + _cellsize.x * _processesList.Count, _nameCellSize.y + _cellsize.y * _systems.Length);
_position = GUI.BeginScrollView(rect, _position, rectView, true, true);
List<string> systeNames = new List<string>();
var blackStyle = EcsEditor.GetStyle(Color.black, 0.04f);
var whiteStyle = EcsEditor.GetStyle(Color.white, 0.04f);
GUIContent label = new GUIContent();
Vector2 pivod = _nameCellSize;
rect = new Rect();
rect.y = _nameCellSize.y;
rect.width = _nameCellSize.x;
rect.height = _cellsize.x;
rect.y -= _cellsize.y;
for (int i = 0; i < _processesList.Count; i++)
{
lineRect = rect;
lineRect.y = 0f;
lineRect.x = _nameCellSize.x + _cellsize.x * i;
lineRect.width = _cellsize.x;
lineRect.height = rectView.height;
GUI.Label(lineRect, "", i % 2 == 1 ? whiteStyle : blackStyle);
GUIUtility.RotateAroundPivot(90, pivod);
//GUIContent label = new GUIContent(_processesList[i].name, "." + _processesList[i].name);
label.text = _processesList[i].name;
label.tooltip = "." + _processesList[i].name;
GUI.Label(rect, label, EditorStyles.miniBoldLabel);
GUIUtility.RotateAroundPivot(-90, pivod);
pivod.x += _cellsize.x;
rect.x += _cellsize.x;
}
//GUIUtility.RotateAroundPivot(-90, _nameCellSize);
rect = new Rect();
rect.y = _nameCellSize.y;
rect.width = _nameCellSize.x;
rect.height = _cellsize.x;
for (int i = 0; i < _systems.Length; i++)
{
string name = EcsEditor.GetGenericName(_systems[i].GetType());
systeNames.Add(name);
lineRect = rect;
lineRect.width = rectView.width;
GUI.Label(lineRect, "", i % 2 == 1 ? whiteStyle : blackStyle);
// GUIContent label = new GUIContent(name, i + " " + name);
label.text = name;
label.tooltip = i + " " + name;
GUI.Label(rect, label, EditorStyles.miniBoldLabel);
rect.y += _cellsize.y;
}
for (int x = 0; x < _processesList.Count; x++)
{
var process = _processesList[x];
for (int y = 0; y < _systems.Length; y++)
{
string systemName = systeNames[x];
rect = new Rect(x * _cellsize.x + _nameCellSize.x, y * _cellsize.y + _nameCellSize.y, _cellsize.x, _cellsize.y);
bool flag = process.systemsBitMask[y];
string labeltext = flag ? "^" : " ";
label.text = labeltext;
label.tooltip = $"{process.name}-{systemName}";
GUI.Label(rect, label);
//GUI.Label(rect, lable, flag ? whiteStyle : blackStyle);
// GUI.Label(rect, label, EditorStyles.helpBox);
}
}
GUI.EndScrollView();
}
private class ProcessData
{
public Type interfaceType;
public string name;
public BitMask systemsBitMask;
}
}
}
#endif

View File

@ -1,177 +0,0 @@
using DCFApixels.DragonECS.Unity.Debug;
using UnityEngine;
using System.Linq;
namespace DCFApixels.DragonECS
{
[MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)]
public class WorldDebugSystem : IEcsRun
{
private string _monitorName;
private EcsWorld _ecsWorld;
public WorldDebugSystem(EcsWorld ecsWorld, string monitorName = null)
{
_monitorName = monitorName;
if (string.IsNullOrEmpty(_monitorName)) _monitorName = ecsWorld.GetType().Name;
_ecsWorld = ecsWorld;
WorldDebugMonitor monitor = new GameObject(EcsConsts.DEBUG_PREFIX + _monitorName).AddComponent<WorldDebugMonitor>();
WorldPoolsMonitor poolsmonitor = new GameObject(EcsConsts.DEBUG_PREFIX + "Pools").AddComponent<WorldPoolsMonitor>();
poolsmonitor.transform.SetParent(monitor.transform);
monitor.source = this;
monitor.world = _ecsWorld;
monitor.monitorName = _monitorName;
poolsmonitor.source = this;
poolsmonitor.world = _ecsWorld;
poolsmonitor.monitorName = "pools";
}
public void Run()
{
}
}
public class WorldDebugMonitor : DebugMonitorBase
{
internal WorldDebugSystem source;
internal EcsWorld world;
}
#if UNITY_EDITOR
namespace Editors
{
using UnityEditor;
[CustomEditor(typeof(WorldDebugMonitor))]
public class WorldDebugMonitorEditor : Editor
{
private WorldDebugMonitor Target => (WorldDebugMonitor)target;
public override void OnInspectorGUI()
{
GUILayout.Label($"Size: {Target.world.Capacity}");
GUILayout.Label($"Total entities: {Target.world.Count}");
}
}
}
#endif
public class WorldPoolsMonitor : DebugMonitorBase
{
internal WorldDebugSystem source;
internal EcsWorld world;
}
}
#if UNITY_EDITOR
namespace DCFApixels.DragonECS.Unity.Editors
{
using UnityEditor;
[CustomEditor(typeof(WorldPoolsMonitor))]
public class WorldPoolsMonitorEditor : Editor
{
private static Vector2 _poolBlockMinSize = new Vector2(80, 160);
private static Vector2 _poolProgressBasrSize = _poolBlockMinSize * new Vector2(1f, 0.8f);
private WorldPoolsMonitor Target => (WorldPoolsMonitor)target;
private Vector2 _scroll;
public override void OnInspectorGUI()
{
_scroll = GUILayout.BeginScrollView(_scroll, GUILayout.Height(800f));
var pools = Target.world.AllPools.ToArray().Where(o => !o.IsNullOrDummy()).OfType<IEcsPool>();
GUILayout.Label("", GUILayout.ExpandWidth(true));
float width = GUILayoutUtility.GetLastRect().width;
Vector3 newPoolBlockSize = _poolBlockMinSize;
int widthCount = Mathf.Max(1, Mathf.Min((Mathf.FloorToInt(width / _poolBlockMinSize.x)), pools.Count()));
newPoolBlockSize.x = width / widthCount;
int x = -1, y = 0;
foreach (var pool in pools)
{
if (++x >= widthCount)
{
x = 0;
y++;
}
DrawPoolBlock(pool, new Rect(newPoolBlockSize.x * x, newPoolBlockSize.y * y, newPoolBlockSize.x, newPoolBlockSize.y));
}
GUILayout.EndScrollView();
}
/// Äåôîëòíûé öâåò äëÿ ïóëà: new Color(0.3f, 1f, 0f, 1f);
private void DrawPoolBlock(IEcsPool pool, Rect position)
{
var meta = pool.GetMeta();
int count = pool.Count;
int capacity = pool.Capacity < 0 ? count : pool.Capacity;
Color defaultContentColor = GUI.contentColor;
GUI.contentColor = Color.black * 0.925f;
position = AddMargin(position, 1f, 1f);
EditorGUI.DrawRect(position, Color.black * 0.16f);
Rect progressBar = new Rect(Vector2.zero, _poolProgressBasrSize);
progressBar.width = position.width;
progressBar.center = position.center - Vector2.up * _poolBlockMinSize.y * 0.09f;
Color mainColor = meta.Color.ToUnityColor();
Color backgroundColor = mainColor * 0.3f + Color.white * 0.2f;
EditorGUI.DrawRect(progressBar, backgroundColor);
progressBar.yMin = progressBar.yMax - ((float)count / capacity) * progressBar.height;
GUIStyle textStyle0 = new GUIStyle(EditorStyles.miniBoldLabel);
textStyle0.alignment = TextAnchor.MiddleCenter;
Color foregroundColor = mainColor;
EditorGUI.DrawRect(progressBar, foregroundColor);
GUI.Label(progressBar, count.ToString(), textStyle0);
GUIStyle textStyle1 = new GUIStyle(EditorStyles.miniBoldLabel);
textStyle1.alignment = TextAnchor.UpperCenter;
GUI.Label(AddMargin(position, 3f, 3f), "Total\r\n" + capacity, textStyle1);
GUI.contentColor = defaultContentColor;
GUIStyle textStyle2 = new GUIStyle(EditorStyles.miniBoldLabel);
textStyle2.wordWrap = true;
textStyle2.alignment = TextAnchor.LowerCenter;
string name = EcsEditor.GetGenericName(pool.ComponentType);
GUIContent label = new GUIContent(name, $"{name} e:{count}");
GUI.Label(AddMargin(position, -10f, 3f), label, textStyle2);
}
private Rect AddMargin(Rect rect, Vector2 value)
{
return AddMargin(rect, value.x, value.y);
}
private Rect AddMargin(Rect rect, float x, float y)
{
rect.yMax -= y;
rect.yMin += y;
rect.xMax -= x;
rect.xMin += x;
return rect;
}
}
}
#endif

8
src/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ff185425dcbca44f820bfc6c564d1d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

365
src/Editor/EditorUtility.cs Normal file
View File

@ -0,0 +1,365 @@
#if UNITY_EDITOR
using DCFApixels.DragonECS.Unity.Internal;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Editors
{
internal class WrapperBase<TSelf> : ScriptableObject
where TSelf : WrapperBase<TSelf>
{
private SerializedObject _so;
private SerializedProperty _property;
private bool _isReleased = false;
private static Stack<TSelf> _wrappers = new Stack<TSelf>();
public SerializedObject SO
{
get { return _so; }
}
public SerializedProperty Property
{
get { return _property; }
}
public static TSelf Take()
{
TSelf result;
if (_wrappers.Count <= 0)
{
result = CreateInstance<TSelf>();
result._so = new SerializedObject(result);
result._property = result._so.FindProperty("data");
}
else
{
result = _wrappers.Pop();
}
return result;
}
public static void Release(TSelf wrapper)
{
if (wrapper._isReleased)
{
return;
}
wrapper._isReleased = true;
_wrappers.Push(wrapper);
}
}
internal class RefEditorWrapper : WrapperBase<RefEditorWrapper>
{
[SerializeReference]
public object data;
}
internal class UnityObjEditorWrapper : WrapperBase<UnityObjEditorWrapper>
{
[SerializeField]
public UnityEngine.Object data;
}
public static class EcsGUI
{
private static Color _grayColor = new Color32(100, 100, 100, 100);
private static Color _greenColor = new Color32(75, 255, 0, 100);
private static Color _redColor = new Color32(255, 0, 75, 100);
private static GUIStyle _grayStyle;
private static GUIStyle _greenStyle;
private static GUIStyle _redStyle;
private static GUILayoutOption[] _defaultParams;
private static bool _isInit = false;
private static void Init()
{
if (_isInit)
{
return;
}
_defaultParams = new GUILayoutOption[] { GUILayout.ExpandWidth(true) };
_grayStyle = EcsEditor.GetStyle(_grayColor);
_greenStyle = EcsEditor.GetStyle(_greenColor);
_redStyle = EcsEditor.GetStyle(_redColor);
_isInit = true;
}
private const string CONNECTED = "Connected";
private const string NOT_CONNECTED = "Not connected";
private const string UNDETERMINED_CONNECTED = "---";
public static void DrawConnectStatus(Rect position, bool status)
{
Init();
if (status)
{
GUI.Box(position, CONNECTED, _greenStyle);
}
else
{
GUI.Box(position, NOT_CONNECTED, _redStyle);
}
}
public static void DrawUndeterminedConnectStatus(Rect position)
{
Init();
GUI.Box(position, UNDETERMINED_CONNECTED, _grayStyle);
}
public static class Layout
{
public static void DrawConnectStatus(bool status, params GUILayoutOption[] options)
{
Init();
if (options == null || options.Length <= 0)
{
options = _defaultParams;
}
GUILayout.Box("", options);
Rect lastRect = GUILayoutUtility.GetLastRect();
if (status)
{
Color color = _greenColor;
color.a = 0.6f;
EditorGUI.DrawRect(lastRect, color);
GUI.Box(lastRect, CONNECTED);
}
else
{
Color color = _redColor;
color.a = 0.6f;
EditorGUI.DrawRect(lastRect, color);
GUI.Box(lastRect, NOT_CONNECTED);
}
}
public static void DrawUndeterminedConnectStatus(params GUILayoutOption[] options)
{
Init();
if (options == null || options.Length <= 0)
{
options = _defaultParams;
}
GUILayout.Box(UNDETERMINED_CONNECTED, _grayStyle, options);
}
public static void DrawComponents(entlong entity)
{
if (entity.TryUnpack(out int entityID, out EcsWorld world))
{
DrawComponents(entityID, world);
}
}
public static void DrawComponents(int entityID, EcsWorld world)
{
var componentTypeIDs = world.GetComponentTypeIDs(entityID);
foreach (var componentTypeID in componentTypeIDs)
{
var pool = world.GetPool(componentTypeID);
{
DrawComponent(entityID, world, pool);
}
}
}
private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private static void DrawComponent(int entityID, EcsWorld world, IEcsPool pool)
{
object data = pool.GetRaw(entityID);
var meta = data.GetMeta();
Color panelColor = meta.Color.ToUnityColor();
GUILayout.BeginVertical(EcsEditor.GetStyle(panelColor, 0.22f));
EditorGUI.BeginChangeCheck();
bool changed = DrawData(pool.ComponentType, data, out object resultData);
if (changed)
{
pool.SetRaw(entityID, resultData);
}
GUILayout.EndVertical();
Rect lineRect = GUILayoutUtility.GetLastRect();
lineRect.y = lineRect.yMax;
lineRect.height = 3f;
Color rectColor = panelColor;
rectColor.a = 0.34f;
EditorGUI.DrawRect(lineRect, rectColor);
GUILayout.Space(2f);
}
private static bool DrawData(Type fieldType, object data, out object outData)
{
var meta = data.GetMeta();
GUIContent label = new GUIContent(meta.Name);
Type type = data.GetType();
var uobj = data as UnityEngine.Object;
if (uobj == false && type.IsGenericType)
{
bool result = false;
foreach (var field in type.GetFields(fieldFlags))
{
if (DrawData(field.FieldType, field.GetValue(data), out object fieldData))
{
field.SetValue(data, fieldData);
result = true;
}
}
outData = data;
return result;
}
else
{
if (uobj == null)
{
EditorGUI.BeginChangeCheck();
var w = RefEditorWrapper.Take();
w.data = data;
w.SO.Update();
EditorGUILayout.PropertyField(w.Property, true);
RefEditorWrapper.Release(w);
if (EditorGUI.EndChangeCheck())
{
w.SO.ApplyModifiedProperties();
outData = w.data;
return true;
}
}
else
{
EditorGUI.BeginChangeCheck();
var w = UnityObjEditorWrapper.Take();
w.data = uobj;
w.SO.Update();
EditorGUILayout.PropertyField(w.Property, true);
UnityObjEditorWrapper.Release(w);
if (EditorGUI.EndChangeCheck())
{
w.SO.ApplyModifiedProperties();
outData = uobj;
return true;
}
}
outData = data;
return false;
}
}
}
}
[InitializeOnLoad]
public static class EcsEditor
{
static EcsEditor()
{
colorBoxeStyles = new SparseArray<GUIStyle>();
}
private static SparseArray<GUIStyle> colorBoxeStyles = new SparseArray<GUIStyle>();
public static GUIStyle GetStyle(Color color, float alphaMultiplier)
{
color.a *= alphaMultiplier;
return GetStyle(color);
}
public static GUIStyle GetStyle(Color32 color32)
{
int colorCode = new Color32Union(color32).colorCode;
if (colorBoxeStyles.TryGetValue(colorCode, out GUIStyle style))
{
if (style == null || style.normal.background == null)
{
style = CreateStyle(color32, colorCode);
colorBoxeStyles[colorCode] = style;
}
return style;
}
style = CreateStyle(color32, colorCode);
colorBoxeStyles.Add(colorCode, style);
return style;
}
private static GUIStyle CreateStyle(Color32 color32, int colorCode)
{
GUIStyle result = new GUIStyle(GUI.skin.box);
Color componentColor = color32;
Texture2D texture2D = CreateTexture(2, 2, componentColor);
result.hover.background = texture2D;
result.focused.background = texture2D;
result.active.background = texture2D;
result.normal.background = texture2D;
return result;
}
private static Texture2D CreateTexture(int width, int height, Color color)
{
var pixels = new Color[width * height];
for (var i = 0; i < pixels.Length; ++i)
pixels[i] = color;
var result = new Texture2D(width, height);
result.SetPixels(pixels);
result.Apply();
return result;
}
#region Utils
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
private readonly ref struct Color32Union
{
[FieldOffset(0)]
public readonly int colorCode;
[FieldOffset(0)]
public readonly byte r;
[FieldOffset(1)]
public readonly byte g;
[FieldOffset(2)]
public readonly byte b;
[FieldOffset(3)]
public readonly byte a;
public Color32Union(byte r, byte g, byte b, byte a) : this()
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color32Union(Color32 color) : this()
{
r = color.r;
g = color.g;
b = color.b;
a = color.a;
}
}
#endregion
}
public static class ReflectionExtensions
{
public static bool TryGetAttribute<T>(this MemberInfo self, out T attrbiute) where T : Attribute
{
attrbiute = self.GetCustomAttribute<T>();
return attrbiute != null;
}
public static bool HasAttribute<T>(this MemberInfo self) where T : Attribute
{
return self.GetCustomAttribute<T>() != null;
}
}
}
#endif

View File

@ -0,0 +1,25 @@
using System;
using System.Reflection;
namespace DCFApixels.DragonECS.Unity.Editors
{
public struct FieldDrawerInfo
{
private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
public Type type;
public object data;
public FieldInfo[] fields;
public FieldDrawerInfo(object data)
{
type = data.GetType();
this.data = data;
fields = type.GetFields(fieldFlags);
}
public void Set(object data)
{
type = data.GetType();
this.data = data;
fields = type.GetFields(fieldFlags);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9047f4ae8f7a24c4d8b053ffeeb259f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -192,7 +192,9 @@ namespace DCFApixels.DragonECS.Unity.Editors
GUILayout.Label("", GUILayout.Width(removeButtonRect.width));
if (GUI.Button(removeButtonRect, "x", removeButtonStyle))
{
OnRemoveComponentAt(index);
}
if (!string.IsNullOrEmpty(description))
{