add runtime editing

This commit is contained in:
Mikhail 2024-03-07 21:22:48 +08:00
parent a356d31937
commit cb4ef1c853
4 changed files with 169 additions and 27 deletions

View File

@ -24,25 +24,45 @@ namespace DCFApixels.DragonECS
[Header("Default Configs")] [Header("Default Configs")]
[Header("Entites")] [Header("Entites")]
[SerializeField] [SerializeField]
private int EntitiesCapacity = EcsWorldConfig.Default.EntitiesCapacity; private int _entitiesCapacity = EcsWorldConfig.Default.EntitiesCapacity;
[Header("Groups")] [Header("Groups")]
[SerializeField] [SerializeField]
private int GroupCapacity = EcsWorldConfig.Default.GroupCapacity; private int _groupCapacity = EcsWorldConfig.Default.GroupCapacity;
[Header("Pools/Components")] [Header("Pools/Components")]
[SerializeField] [SerializeField]
private int PoolsCapacity = EcsWorldConfig.Default.PoolsCapacity; private int _poolsCapacity = EcsWorldConfig.Default.PoolsCapacity;
[SerializeField] [SerializeField]
private int PoolComponentsCapacity = EcsWorldConfig.Default.PoolComponentsCapacity; private int _poolComponentsCapacity = EcsWorldConfig.Default.PoolComponentsCapacity;
[SerializeField] [SerializeField]
private int PoolRecycledComponentsCapacity = EcsWorldConfig.Default.PoolRecycledComponentsCapacity; private int _poolRecycledComponentsCapacity = EcsWorldConfig.Default.PoolRecycledComponentsCapacity;
#region Properties #region Properties
public sealed override bool IsEmpty public sealed override bool IsEmpty
{ {
get { return _world == null; } get { return _world == null; }
} }
public int EntitiesCapacity
{
get { return _entitiesCapacity; }
}
public int GroupCapacity
{
get { return _groupCapacity; }
}
public int PoolsCapacity
{
get { return _poolsCapacity; }
}
public int PoolComponentsCapacity
{
get { return _poolComponentsCapacity; }
}
public int PoolRecycledComponentsCapacity
{
get { return _poolRecycledComponentsCapacity; }
}
#endregion #endregion
#region Methods #region Methods
@ -94,7 +114,7 @@ namespace DCFApixels.DragonECS
#region Events #region Events
protected virtual TWorld BuildWorld() protected virtual TWorld BuildWorld()
{ {
EcsWorldConfig config = new EcsWorldConfig(EntitiesCapacity, GroupCapacity, PoolsCapacity, PoolComponentsCapacity, PoolRecycledComponentsCapacity); EcsWorldConfig config = new EcsWorldConfig(_entitiesCapacity, _groupCapacity, _poolsCapacity, _poolComponentsCapacity, _poolRecycledComponentsCapacity);
ConfigContainer configs = new ConfigContainer().Set(config); ConfigContainer configs = new ConfigContainer().Set(config);
return (TWorld)Activator.CreateInstance(typeof(TWorld), new object[] { configs, _worldID }); return (TWorld)Activator.CreateInstance(typeof(TWorld), new object[] { configs, _worldID });
} }

View File

@ -12,32 +12,32 @@ namespace DCFApixels.DragonECS.Unity.Editors
private static readonly Rect RemoveButtonRect = new Rect(0f, 0f, 17f, 19f); private static readonly Rect RemoveButtonRect = new Rect(0f, 0f, 17f, 19f);
private static readonly Rect TooltipIconRect = new Rect(0f, 0f, 21f, 15f); private static readonly Rect TooltipIconRect = new Rect(0f, 0f, 21f, 15f);
private GUIStyle removeButtonStyle; private GUIStyle _removeButtonStyle;
private GenericMenu genericMenu; private GenericMenu _genericMenu;
private bool _isInit = false; private bool _isInit = false;
#region Init #region Init
private void Init() private void Init()
{ {
if (genericMenu == null) { _isInit = false; } if (_genericMenu == null) { _isInit = false; }
if (_isInit) { return; } if (_isInit) { return; }
var tmpstylebase = UnityEditorUtility.GetStyle(new Color(0.9f, 0f, 0.22f), 0.5f); var tmpstylebase = UnityEditorUtility.GetStyle(new Color(0.9f, 0f, 0.22f), 0.5f);
var tmpStyle = UnityEditorUtility.GetStyle(new Color(1f, 0.5f, 0.7f), 0.5f); var tmpStyle = UnityEditorUtility.GetStyle(new Color(1f, 0.5f, 0.7f), 0.5f);
removeButtonStyle = new GUIStyle(EditorStyles.linkLabel); _removeButtonStyle = new GUIStyle(EditorStyles.linkLabel);
removeButtonStyle.alignment = TextAnchor.MiddleCenter; _removeButtonStyle.alignment = TextAnchor.MiddleCenter;
removeButtonStyle.normal = tmpstylebase.normal; _removeButtonStyle.normal = tmpstylebase.normal;
removeButtonStyle.hover = tmpStyle.normal; _removeButtonStyle.hover = tmpStyle.normal;
removeButtonStyle.active = tmpStyle.normal; _removeButtonStyle.active = tmpStyle.normal;
removeButtonStyle.focused = tmpStyle.normal; _removeButtonStyle.focused = tmpStyle.normal;
removeButtonStyle.padding = new RectOffset(0, 0, 0, 0); _removeButtonStyle.padding = new RectOffset(0, 0, 0, 0);
removeButtonStyle.margin = new RectOffset(0, 0, 0, 0); _removeButtonStyle.margin = new RectOffset(0, 0, 0, 0);
removeButtonStyle.border = new RectOffset(0, 0, 0, 0); _removeButtonStyle.border = new RectOffset(0, 0, 0, 0);
genericMenu = new GenericMenu(); _genericMenu = new GenericMenu();
var componentTemplateDummies = ComponentTemplateTypeCache.Dummies; var componentTemplateDummies = ComponentTemplateTypeCache.Dummies;
foreach (var dummy in componentTemplateDummies) foreach (var dummy in componentTemplateDummies)
@ -56,7 +56,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
{ {
name = $"{name} {EcsUnityConsts.INFO_MARK}"; name = $"{name} {EcsUnityConsts.INFO_MARK}";
} }
genericMenu.AddItem(new GUIContent(name, description), false, OnAddComponent, dummy); _genericMenu.AddItem(new GUIContent(name, description), false, OnAddComponent, dummy);
} }
_isInit = true; _isInit = true;
@ -122,7 +122,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
if (GUILayout.Button("Add Component", GUILayout.Height(24f))) if (GUILayout.Button("Add Component", GUILayout.Height(24f)))
{ {
Init(); Init();
genericMenu.ShowAsContext(); _genericMenu.ShowAsContext();
} }
} }
private void DrawFooter(ITemplateInternal target) private void DrawFooter(ITemplateInternal target)
@ -167,8 +167,6 @@ namespace DCFApixels.DragonECS.Unity.Editors
string description = meta.Description; string description = meta.Description;
Color panelColor = meta.Color.ToUnityColor().Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE); Color panelColor = meta.Color.ToUnityColor().Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE);
Rect removeButtonRect = GUILayoutUtility.GetLastRect();
//GUIContent label = new GUIContent(name); //GUIContent label = new GUIContent(name);
GUIContent label = UnityEditorUtility.GetLabel(name); GUIContent label = UnityEditorUtility.GetLabel(name);
bool isEmpty = componentType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length <= 0; bool isEmpty = componentType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length <= 0;
@ -176,6 +174,8 @@ namespace DCFApixels.DragonECS.Unity.Editors
Color alphaPanelColor = panelColor; Color alphaPanelColor = panelColor;
alphaPanelColor.a = EscEditorConsts.COMPONENT_DRAWER_ALPHA; alphaPanelColor.a = EscEditorConsts.COMPONENT_DRAWER_ALPHA;
Rect removeButtonRect = GUILayoutUtility.GetLastRect();
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
GUILayout.BeginVertical(UnityEditorUtility.GetStyle(alphaPanelColor)); GUILayout.BeginVertical(UnityEditorUtility.GetStyle(alphaPanelColor));
@ -231,7 +231,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
removeButtonRect.center = new Vector2(lastrect.xMax + removeButtonRect.width, lastrect.yMin + removeButtonRect.height / 2f); removeButtonRect.center = new Vector2(lastrect.xMax + removeButtonRect.width, lastrect.yMin + removeButtonRect.height / 2f);
GUILayout.Label("", GUILayout.Width(removeButtonRect.width)); GUILayout.Label("", GUILayout.Width(removeButtonRect.width));
if (GUI.Button(removeButtonRect, "x", removeButtonStyle)) if (GUI.Button(removeButtonRect, "x", _removeButtonStyle))
{ {
OnRemoveComponentAt(index); OnRemoveComponentAt(index);
} }

View File

@ -13,6 +13,8 @@ namespace DCFApixels.DragonECS.Unity.Editors
internal readonly static Color GreenColor = new Color32(75, 255, 0, 255); internal readonly static Color GreenColor = new Color32(75, 255, 0, 255);
internal readonly static Color RedColor = new Color32(255, 0, 75, 255); internal readonly static Color RedColor = new Color32(255, 0, 75, 255);
private static readonly Rect RemoveButtonRect = new Rect(0f, 0f, 17f, 19f);
private static readonly Rect TooltipIconRect = new Rect(0f, 0f, 21f, 15f);
//private static GUILayoutOption[] _defaultParams; //private static GUILayoutOption[] _defaultParams;
//private static bool _isInit = false; //private static bool _isInit = false;
//private static void Init() //private static void Init()
@ -68,6 +70,13 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
if (GUILayout.Button("Add Component", GUILayout.Height(24f)))
{
GenericMenu genericMenu = RuntimeComponentsUtility.GetAddComponentGenericMenu(world);
RuntimeComponentsUtility.CurrentEntityID = entityID;
genericMenu.ShowAsContext();
}
} }
private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private static void DrawRuntimeComponent(int entityID, IEcsPool pool) private static void DrawRuntimeComponent(int entityID, IEcsPool pool)
@ -77,15 +86,36 @@ namespace DCFApixels.DragonECS.Unity.Editors
{ {
object data = pool.GetRaw(entityID); object data = pool.GetRaw(entityID);
Color panelColor = meta.Color.ToUnityColor().Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE); Color panelColor = meta.Color.ToUnityColor().Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE);
float padding = EditorGUIUtility.standardVerticalSpacing;
Rect removeButtonRect = GUILayoutUtility.GetLastRect();
GUILayout.BeginVertical(UnityEditorUtility.GetStyle(panelColor, EscEditorConsts.COMPONENT_DRAWER_ALPHA)); GUILayout.BeginVertical(UnityEditorUtility.GetStyle(panelColor, EscEditorConsts.COMPONENT_DRAWER_ALPHA));
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
bool isRemoveComponent = false;
removeButtonRect.yMin = removeButtonRect.yMax;
removeButtonRect.yMax += RemoveButtonRect.height;
removeButtonRect.xMin = removeButtonRect.xMax - RemoveButtonRect.width;
removeButtonRect.center += Vector2.up * padding * 2f;
if (GUI.Button(removeButtonRect, "x"))
{
isRemoveComponent = true;
}
Type componentType = pool.ComponentType; Type componentType = pool.ComponentType;
ExpandMatrix expandMatrix = ExpandMatrix.Take(componentType); ExpandMatrix expandMatrix = ExpandMatrix.Take(componentType);
bool changed = DrawRuntimeData(componentType, UnityEditorUtility.GetLabel(meta.Name), expandMatrix, data, out object resultData); bool changed = DrawRuntimeData(componentType, UnityEditorUtility.GetLabel(meta.Name), expandMatrix, data, out object resultData);
if (changed) if (changed || isRemoveComponent)
{ {
pool.SetRaw(entityID, resultData); if (isRemoveComponent)
{
pool.Del(entityID);
}
else
{
pool.SetRaw(entityID, resultData);
}
} }
GUILayout.EndVertical(); GUILayout.EndVertical();

View File

@ -1,5 +1,7 @@
#if UNITY_EDITOR #if UNITY_EDITOR
using DCFApixels.DragonECS.Unity.Internal; using DCFApixels.DragonECS.Unity.Internal;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using UnityEditor; using UnityEditor;
@ -156,5 +158,95 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
#endregion #endregion
} }
internal static class RuntimeComponentsUtility
{
public struct WorldData
{
public GenericMenu addComponentGenericMenu;
public int poolsCount;
public WorldData(GenericMenu addComponentGenericMenu, int poolsCount)
{
this.addComponentGenericMenu = addComponentGenericMenu;
this.poolsCount = poolsCount;
}
}
//world id
private static Dictionary<EcsWorld, WorldData> _worldDatas = new Dictionary<EcsWorld, WorldData>();
public static GenericMenu GetAddComponentGenericMenu(EcsWorld world)
{
if (_worldDatas.TryGetValue(world, out WorldData data))
{
if (data.poolsCount != world.PoolsCount)
{
data = CreateWorldData(world);
_worldDatas[world] = data;
}
}
else
{
data = CreateWorldData(world);
_worldDatas[world] = data;
world.AddListener(new Listener(world));
}
return data.addComponentGenericMenu;
}
private static WorldData CreateWorldData(EcsWorld world)
{
GenericMenu genericMenu = new GenericMenu();
var pools = world.AllPools;
for (int i = 0; i < world.PoolsCount; i++)
{
var pool = pools[i];
if (pool.IsNullOrDummy())
{
i--;
continue;
}
var meta = pool.ComponentType.ToMeta();
genericMenu.AddItem(new GUIContent(meta.Name, meta.Description), false, OnAddComponent, pool);
}
return new WorldData(genericMenu, world.PoolsCount);
}
public static int CurrentEntityID = 0;
private static void OnAddComponent(object userData)
{
IEcsPool pool = (IEcsPool)userData;
if (pool.World.IsUsed(CurrentEntityID) == false)
{
return;
}
if (pool.Has(CurrentEntityID) == false)
{
pool.AddRaw(CurrentEntityID, Activator.CreateInstance(pool.ComponentType));
}
else
{
Debug.LogWarning($"Entity({CurrentEntityID}) already has component {EcsDebugUtility.GetGenericTypeName(pool.ComponentType)}.");
}
}
private class Listener : IEcsWorldEventListener
{
private EcsWorld _world;
public Listener(EcsWorld world)
{
_world = world;
}
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
public void OnWorldDestroy()
{
_worldDatas.Remove(_world);
}
public void OnWorldResize(int newSize) { }
}
}
} }
#endif #endif