This commit is contained in:
Mikhail 2023-05-07 00:50:44 +08:00
parent dd41a6f2b8
commit dc575bd516
54 changed files with 1464 additions and 215 deletions

View File

@ -1,6 +1,7 @@
#if UNITY_EDITOR
using System;
using System.IO;
using System.Runtime.Versioning;
using System.Text;
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
@ -12,9 +13,9 @@ namespace DCFApixels.DragonECS.Editors
{
private const int MENU_ITEM_PRIORITY = -198;
private const string TITLE = "DragonECS Template Generator";
private const string TITLE = EcsConsts.FRAMEWORK_NAME + " Template Generator";
private const string MENU_ITEM_PATH = "Assets/Create/DragonECS/";
private const string MENU_ITEM_PATH = "Assets/Create/" + EcsConsts.FRAMEWORK_NAME + "/";
private const string NAMESPACE_TAG = "#NAMESPACE#";
private const string SCRIPTANAME_TAG = "#SCRIPTNAME#";
@ -29,25 +30,25 @@ namespace DCFApixels.DragonECS.Editors
#endregion
#region GenerateMethods
[MenuItem(MENU_ITEM_PATH + "[Template]Startup", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]Startup", false, MENU_ITEM_PRIORITY)]
public static void CreateSturtupScript() => CreateScript("Startup");
[MenuItem(MENU_ITEM_PATH + "[Template]System", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]System", false, MENU_ITEM_PRIORITY)]
public static void CreateSystemSimpleScript() => CreateScript("System");
[MenuItem(MENU_ITEM_PATH + "[Template]Component", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]Component", false, MENU_ITEM_PRIORITY)]
public static void CreateComponentSimpleScript() => CreateScript("Component");
[MenuItem(MENU_ITEM_PATH + "[Template]Runner", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]Runner", false, MENU_ITEM_PRIORITY)]
public static void CreateRunnerSimpleScript() => CreateScript("Runner");
[MenuItem(MENU_ITEM_PATH + "[Template]System Extended", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]System Extended", false, MENU_ITEM_PRIORITY)]
public static void CreateSystemScript() => CreateScript("SystemExtended");
[MenuItem(MENU_ITEM_PATH + "[Template]Component Extended", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]Component Extended", false, MENU_ITEM_PRIORITY)]
public static void CreateComponentScript() => CreateScript("ComponentExtended");
[MenuItem(MENU_ITEM_PATH + "[Template]Runner Extended", false, MENU_ITEM_PRIORITY)]
[MenuItem(MENU_ITEM_PATH + "[CodeTemplate]Runner Extended", false, MENU_ITEM_PRIORITY)]
public static void CreateRunnerScript() => CreateScript("RunnerExtended");

View File

@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS
string log;
if (!string.IsNullOrEmpty(tag))
{
log = $"[{tag}] {v}";
log = $".[{tag}] {v}";
string taglower = tag.ToLower();
if (taglower.Contains("warning"))
{

View File

@ -1,20 +1,40 @@
#if UNITY_EDITOR
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEngine;
namespace DCFApixels.DragonECS.Editors
{
public static class EcsEditor
{
private static SparseArray<GUIStyle> colorBoxeStyles = new SparseArray<GUIStyle>();
public static GUIStyle GetStyle(Color color, float alphaMultiplier)
{
GUIStyle style = new GUIStyle(GUI.skin.box);
Color componentColor = color;
componentColor.a *= alphaMultiplier;
style.normal.background = CreateTexture(2, 2, componentColor);
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 = CreateStyle(color32, colorCode);
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;
result.normal.background = CreateTexture(2, 2, componentColor);
return result;
}
private static Texture2D CreateTexture(int width, int height, Color color)
{
var pixels = new Color[width * height];
@ -26,6 +46,73 @@ namespace DCFApixels.DragonECS.Editors
result.Apply();
return result;
}
public static string GetGenericName(Type type)
{
string friendlyName = type.Name;
if (type.IsGenericType)
{
int iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0)
friendlyName = friendlyName.Remove(iBacktick);
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetGenericName(typeParameters[i]);
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";
}
return friendlyName;
}
public static string GetName<T>() => GetName(typeof(T));
public static string GetName(Type type)
{
var atr = type.GetCustomAttribute<DebugNameAttribute>();
return atr != null ? atr.name : GetGenericName(type);
}
public static string GetDescription<T>() => GetDescription(typeof(T));
public static string GetDescription(Type type)
{
var atr = type.GetCustomAttribute<DebugDescriptionAttribute>();
return atr != null ? atr.description : string.Empty;
}
#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
}
}
#endif

View File

@ -2,7 +2,7 @@
{
public sealed class DebugModule : IEcsModule
{
public const string DEBUG_SYSTEMS_BLOCK = nameof(DEBUG_SYSTEMS_BLOCK);
public const string DEBUG_LAYER = nameof(DEBUG_LAYER);
public EcsWorld[] _worlds;
public DebugModule(params EcsWorld[] worlds)
{
@ -11,11 +11,11 @@
void IEcsModule.ImportSystems(EcsPipeline.Builder b)
{
b.InsertSystemsBlock(DEBUG_SYSTEMS_BLOCK, EcsConsts.POST_END_SYSTEMS_BLOCK);
b.Add(new PipelineDebugSystem(), DEBUG_SYSTEMS_BLOCK);
b.Layers.Insert(EcsConsts.POST_END_LAYER, DEBUG_LAYER);
b.Add(new PipelineDebugSystem(), DEBUG_LAYER);
foreach (var world in _worlds)
{
b.Add(new WorldDebugSystem(world), DEBUG_SYSTEMS_BLOCK);
b.Add(new WorldDebugSystem(world), DEBUG_LAYER);
}
}
}

View File

@ -1,6 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Debug

View File

@ -1,5 +1,4 @@
using DCFApixels.DragonECS.Unity;
using DCFApixels.DragonECS.Unity.Debug;
using DCFApixels.DragonECS.Unity.Debug;
using System.Reflection;
using UnityEngine;
@ -7,7 +6,7 @@ using UnityEngine;
namespace DCFApixels.DragonECS
{
[DebugHide, DebugColor(DebugColor.Gray)]
public class PipelineDebugSystem : IEcsPreInitSystem
public class PipelineDebugSystem : IEcsPreInitProcess
{
private string _monitorName;
public PipelineDebugSystem(string monitorName = "Pipeline")
@ -15,13 +14,20 @@ namespace DCFApixels.DragonECS
_monitorName = monitorName;
}
void IEcsPreInitSystem.PreInit(EcsPipeline pipeline)
void IEcsPreInitProcess.PreInit(EcsPipeline pipeline)
{
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>();
@ -37,11 +43,18 @@ namespace DCFApixels.DragonECS
internal EcsPipeline pipeline;
}
public class PipelineProcessesDebugMonitor : DebugMonitorBase
{
internal PipelineDebugSystem source;
internal EcsPipeline pipeline;
}
#if UNITY_EDITOR
namespace Editors
{
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
@ -102,7 +115,7 @@ namespace DCFApixels.DragonECS
private void DrawSystem(IEcsSystem system)
{
if(system is SystemsBlockMarkerSystem markerSystem)
if (system is SystemsBlockMarkerSystem markerSystem)
{
GUILayout.EndVertical();
GUILayout.BeginVertical(EcsEditor.GetStyle(Color.black, 0.2f));
@ -120,7 +133,7 @@ namespace DCFApixels.DragonECS
if (CheckIsHidden(type))
return;
string name = type.Name;
string name = EcsEditor.GetGenericName(type);
Color color = (GetAttribute<DebugColorAttribute>(type) ?? _fakeDebugColorAttribute).GetUnityColor();
GUILayout.BeginVertical(EcsEditor.GetStyle(color, 0.2f));
@ -140,7 +153,7 @@ namespace DCFApixels.DragonECS
Color color = (GetAttribute<DebugColorAttribute>(type) ?? _fakeDebugColorAttribute).GetUnityColor();
GUILayout.BeginVertical(EcsEditor.GetStyle(color, 0.2f));
GUILayout.Label(type.Name, EditorStyles.boldLabel);
GUILayout.Label(EcsEditor.GetGenericName(type), EditorStyles.boldLabel);
GUILayout.Label(string.Join(", ", runner.Targets.Cast<object>().Select(o => o.GetType().Name)), systemsListStyle);
GUILayout.EndVertical();
}
@ -161,7 +174,166 @@ namespace DCFApixels.DragonECS
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(IEcsSystem);
private IEcsSystem[] _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 SystemsBlockMarkerSystem == 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,29 +0,0 @@
using DCFApixels.DragonECS.Unity.Debug;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity
{
public class SystemDebugMonitor :MonoBehaviour
{
[SerializeReference]
private IEcsSystem _target; //TODO переделать подручнуюотрисовку, потому как [SerializeReference] не работает с generic-ами
public static SystemDebugMonitor CreateMonitor(Transform parent, IEcsSystem system, string name)
{
GameObject go = new GameObject(name);
go.transform.parent = parent;
go.transform.position = Vector3.zero;
go.transform.rotation = Quaternion.identity;
go.SetActive(false);
SystemDebugMonitor result = go.AddComponent<SystemDebugMonitor>();
result._target = system;
return result;
}
}
}

View File

@ -6,17 +6,18 @@ using UnityEngine;
namespace DCFApixels.DragonECS
{
[DebugHide, DebugColor(DebugColor.Gray)]
public class WorldDebugSystem : IEcsRunSystem
public class WorldDebugSystem : IEcsRunProcess
{
private string _monitorName;
private EcsWorld _ecsWorld;
public WorldDebugSystem(EcsWorld ecsWorld, string monitorName = "World")
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 + _monitorName).AddComponent<WorldPoolsMonitor>();
WorldPoolsMonitor poolsmonitor = new GameObject(EcsConsts.DEBUG_PREFIX + "Pools").AddComponent<WorldPoolsMonitor>();
poolsmonitor.transform.SetParent(monitor.transform);
monitor.source = this;
@ -49,8 +50,11 @@ namespace DCFApixels.DragonECS
{
private WorldDebugMonitor Target => (WorldDebugMonitor)target;
public override void OnInspectorGUI()
{
GUILayout.Label($"Size: {Target.world.Capacity}");
GUILayout.Label($"Total entities: {Target.world.Count}");
}
}
}
#endif
@ -83,75 +87,81 @@ namespace DCFApixels.DragonECS
public override void OnInspectorGUI()
{
// _scroll = GUILayout.BeginScrollView(_scroll, GUILayout.Height(800f));
// var pools = Target.world.GetAllPools().ToArray().Where(o => !(o is EcsNullPool)).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();
_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();
}
// private void DrawPoolBlock(IEcsPool pool, Rect position)
// {
// 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 = new Color(0.3f, 1f, 0f, 1f);
// var debugColor = pool.ComponentType.GetCustomAttribute<DebugColorAttribute>();
// if (debugColor != null)
// {
// mainColor = debugColor.GetUnityColor();
// }
// Color backgroundColor = mainColor * 0.3f + Color.white * 0.2f;
//
// EditorGUI.DrawRect(progressBar, backgroundColor);
//
// progressBar.yMin = progressBar.yMax - ((float)pool.Count / pool.Capacity) * progressBar.height;
//
// GUIStyle textStyle0 = EditorStyles.miniBoldLabel;
// textStyle0.alignment = TextAnchor.MiddleCenter;
//
// Color foregroundColor = mainColor;
// EditorGUI.DrawRect(progressBar, foregroundColor);
// GUI.Label(progressBar, pool.Count.ToString(), textStyle0);
//
// GUIStyle textStyle1 = EditorStyles.miniBoldLabel;
// textStyle1.alignment = TextAnchor.UpperCenter;
// GUI.Label(AddMargin(position, 3f, 3f), "Total\r\n"+ pool.Capacity, textStyle1);
//
// GUI.contentColor = defaultContentColor;
// GUIStyle textStyle2 = EditorStyles.miniBoldLabel;
// textStyle2.alignment = TextAnchor.LowerCenter;
// GUI.Label(AddMargin(position, -10f, 3f), pool.ComponentType.Name, textStyle2);
//
// }
private void DrawPoolBlock(IEcsPool pool, Rect position)
{
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 = new Color(0.3f, 1f, 0f, 1f);
var debugColor = pool.ComponentType.GetCustomAttribute<DebugColorAttribute>();
if (debugColor != null)
{
mainColor = debugColor.GetUnityColor();
}
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, $"t({name})");
GUI.Label(AddMargin(position, -10f, 3f), label, textStyle2);
}
private Rect AddMargin(Rect rect, Vector2 value)
{

7
src/EcsUnityConsts.cs Normal file
View File

@ -0,0 +1,7 @@
namespace DCFApixels.DragonECS
{
public static class EcsUnityConsts
{
public const string INFO_MARK = "[i]";
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: bf4cdc68b5b89a14c81a53b600078536
guid: b33e303fef8298b4db42987fe9b94e45
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 70c8e3cb9125ee14fad9fdee48c3e8ba
guid: 11dc6a014c6c7e84ab61979e25010c57
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,17 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
public class EntityTemplate : MonoBehaviour, ITemplateInternal
{
[SerializeReference]
private ITemplateComponent[] _components;
string ITemplateInternal.ComponentsPropertyName => nameof(_components);
public void Apply(EcsWorld world, int entityID)
{
foreach (var item in _components)
item.Add(world, entityID);
}
}
}

View File

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

View File

@ -0,0 +1,235 @@
using System;
using System.Reflection;
namespace DCFApixels.DragonECS
{
#if UNITY_EDITOR
namespace Editors
{
using UnityEditor;
using UnityEngine;
public class EntityTemplateEditorBase: Editor
{
private static readonly Rect RemoveButtonRect = new Rect(0f, 0f, 15f, 15f);
private static readonly Rect TooltipIconRect = new Rect(0f, 0f, 15f, 15f);
private GUIStyle removeButtonStyle;
private GenericMenu genericMenu;
private bool _isInit = false;
#region Init
private void Init()
{
if (genericMenu == null)
_isInit = false;
if (_isInit)
return;
var tmpstylebase = EcsEditor.GetStyle(new Color(0.9f, 0f, 0.22f), 0.5f);
var tmpStyle = EcsEditor.GetStyle(new Color(1f, 0.5f, 0.7f), 0.5f);
removeButtonStyle = new GUIStyle(EditorStyles.linkLabel);
removeButtonStyle.alignment = TextAnchor.MiddleCenter;
removeButtonStyle.normal = tmpstylebase.normal;
removeButtonStyle.hover = tmpStyle.normal;
removeButtonStyle.active = tmpStyle.normal;
removeButtonStyle.focused = tmpStyle.normal;
removeButtonStyle.padding = new RectOffset(0, 0, 0, 0);
removeButtonStyle.margin = new RectOffset(0, 0, 0, 0);
removeButtonStyle.border = new RectOffset(0, 0, 0, 0);
genericMenu = new GenericMenu();
var dummies = TemplateBrowsableTypeCache.Dummies;
foreach ( var dummy in dummies )
{
string name, description;
if (dummy is ITemplateComponentName browsableName)
name = browsableName.Name;
else
name = EcsEditor.GetName(dummy.GetType());
if (dummy is TemplateComponentInitializerBase initializer)
description = initializer.Description;
else
description = EcsEditor.GetDescription(dummy.GetType());
if (!string.IsNullOrEmpty(description))
{
name = $"{name} {EcsUnityConsts.INFO_MARK}";
}
genericMenu.AddItem(new GUIContent(name, description), false, OnAddComponent, dummy);
}
_isInit = true;
}
#endregion
#region Add/Remove
private void OnAddComponent(object obj)
{
Type componentType = obj.GetType();
if (this.target is ITemplateInternal target)
{
SerializedProperty componentsProp = serializedObject.FindProperty(target.ComponentsPropertyName);
for (int i = 0; i < componentsProp.arraySize; i++)
{
if (componentsProp.GetArrayElementAtIndex(i).managedReferenceValue.GetType() == componentType)
return;
}
componentsProp.InsertArrayElementAtIndex(0);
componentsProp.GetArrayElementAtIndex(0).managedReferenceValue = ((ITemplateComponent)obj).Clone();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(this.target);
}
}
private void OnRemoveComponentAt(int index)
{
if (this.target is ITemplateInternal target)
{
SerializedProperty componentsProp = serializedObject.FindProperty(target.ComponentsPropertyName);
componentsProp.DeleteArrayElementAtIndex(index);
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(this.target);
}
}
#endregion
protected void Draw(ITemplateInternal target)
{
Init();
SerializedProperty componentsProp = serializedObject.FindProperty(target.ComponentsPropertyName);
if (componentsProp == null)
return;
DrawTop(target);
for (int i = 0; i < componentsProp.arraySize; i++)
{
DrawComponentData(componentsProp.GetArrayElementAtIndex(i), i);
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing * 2);
}
}
private void DrawTop(ITemplateInternal target)
{
if (GUILayout.Button("Add Component", GUILayout.Height(24f)))
{
Init();
genericMenu.ShowAsContext();
}
}
private void DrawComponentData(SerializedProperty componentRefProp, int index)
{
ITemplateComponent browsable = (ITemplateComponent)componentRefProp.managedReferenceValue;
ITemplateComponentName browsableName = browsable as ITemplateComponentName;
if (componentRefProp.managedReferenceValue == null)
{
DrawDamagedComponent(componentRefProp, index);
return;
}
Type componentType;
SerializedProperty componentProperty = componentRefProp;
TemplateComponentInitializerBase customInitializer = componentProperty.managedReferenceValue as TemplateComponentInitializerBase;
if (customInitializer != null)
{
componentProperty = componentProperty.FindPropertyRelative("component");
componentType = customInitializer.Type;
}
else
{
componentType = componentProperty.managedReferenceValue.GetType();
}
Type type = browsable.GetType();
string name = browsableName == null ? type.Name : GetLastPathComponent(browsableName.Name);
string description = customInitializer != null ? customInitializer.Description : componentType.GetCustomAttribute<DebugDescriptionAttribute>()?.description;
Color panelColor = customInitializer != null ? customInitializer.Color : componentType.GetCustomAttribute<DebugColorAttribute>()?.GetUnityColor() ?? Color.black;
GUILayout.BeginHorizontal();
GUILayout.BeginVertical(EcsEditor.GetStyle(panelColor, 0.2f));
EditorGUI.BeginChangeCheck();
GUIContent label = new GUIContent(name, $"{name} ");
EditorGUILayout.PropertyField(componentProperty, label, true);
if (EditorGUI.EndChangeCheck())
{
componentProperty.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(componentProperty.serializedObject.targetObject);
}
Rect lastrect = GUILayoutUtility.GetLastRect();
Rect removeButtonRect = RemoveButtonRect;
removeButtonRect.center = new Vector2(lastrect.xMax + removeButtonRect.width, lastrect.yMin + removeButtonRect.height / 2f);
GUILayout.EndVertical();
GUILayout.Label("", GUILayout.Width(removeButtonRect.width));
if (GUI.Button(removeButtonRect, "x", removeButtonStyle))
OnRemoveComponentAt(index);
if (!string.IsNullOrEmpty(description))
{
Rect tooltipIconRect = TooltipIconRect;
tooltipIconRect.center = new Vector2(lastrect.xMax - removeButtonRect.width / 2f, lastrect.yMin + removeButtonRect.height / 2f);
GUIContent descriptionLabel = new GUIContent(EcsUnityConsts.INFO_MARK, description);
GUI.Label(tooltipIconRect, descriptionLabel, EditorStyles.boldLabel);
}
GUILayout.EndHorizontal();
}
private void DrawDamagedComponent(SerializedProperty componentRefProp, int index)
{
GUILayout.BeginHorizontal();
EditorGUILayout.HelpBox($"Damaged component. If the problem occurred after renaming a component or initializer. use MovedFromAttrubute", MessageType.Warning);
Rect lastrect = GUILayoutUtility.GetLastRect();
Rect removeButtonRect = RemoveButtonRect;
removeButtonRect.center = new Vector2(lastrect.xMax + removeButtonRect.width, lastrect.yMin + removeButtonRect.height / 2f);
GUILayout.Label("", GUILayout.Width(removeButtonRect.width));
if (GUI.Button(removeButtonRect, "x", removeButtonStyle))
OnRemoveComponentAt(index);
GUILayout.EndHorizontal();
}
public string GetLastPathComponent(string input)
{
int lastSlashIndex = input.LastIndexOfAny(new char[] { '/', '\\' });
if (lastSlashIndex == -1)
return input;
else
return input.Substring(lastSlashIndex + 1);
}
}
[CustomEditor(typeof(EntityTemplatePreset), true)]
public class EntityTemplatePresetEditor : EntityTemplateEditorBase
{
public override void OnInspectorGUI()
{
Draw((ITemplateInternal)target);
}
}
[CustomEditor(typeof(EntityTemplate), true)]
public class EntityTemplateEditor : EntityTemplateEditorBase
{
public override void OnInspectorGUI()
{
Draw((ITemplateInternal)target);
}
}
}
#endif
}

View File

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

View File

@ -0,0 +1,24 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
[CreateAssetMenu(fileName = "EntityTemplatePreset", menuName = EcsConsts.FRAMEWORK_NAME + "/EntityTemplatePreset", order = 1)]
public class EntityTemplatePreset : ScriptableObject, ITemplateInternal
{
[SerializeReference]
private ITemplateComponent[] _components;
string ITemplateInternal.ComponentsPropertyName => nameof(_components);
//ITemplateBrowsable[] ITemplateInternal.Components
//{
// get => _components;
// set => _components = value;
//}
public void Apply(EcsWorld world, int entityID)
{
foreach (var item in _components)
item.Add(world, entityID);
}
}
}

View File

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

View File

@ -0,0 +1,25 @@
using UnityEngine;
namespace DCFApixels.DragonECS
{
public interface ITemplate
{
public void Apply(EcsWorld world, int entityID);
}
public interface ITemplateInternal : ITemplate
{
// internal ITemplateBrowsable[] Components { get; set; }
internal string ComponentsPropertyName { get; }
}
public static class ITemplateExt
{
public static int NewEntity(this ITemplate self, EcsWorld world)
{
int e = world.NewEntity();
self.Apply(world, e);
return e;
}
}
}

View File

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

View File

@ -0,0 +1,131 @@
using DCFApixels.DragonECS.Editors;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace DCFApixels.DragonECS
{
public interface ITemplateComponent
{
public void Add(EcsWorld w, int e);
}
public interface ITemplateComponentName : ITemplateComponent
{
public string Name { get; }
}
[Serializable]
public abstract class TemplateComponentInitializerBase
{
public virtual string Name => string.Empty;
public virtual Color Color => Color.black;
public virtual string Description => string.Empty;
public abstract Type Type { get; }
#region Get meta
internal static Color GetColor(Type type)
{
var atr = type.GetCustomAttribute<DebugColorAttribute>();
if (atr == null) return Color.black;
return atr.GetUnityColor();
}
internal static string GetName(Type type)
{
string friendlyName = type.Name;
if (type.IsGenericType)
{
int iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0)
friendlyName = friendlyName.Remove(iBacktick);
friendlyName += "/" + friendlyName;
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetName(typeParameters[i]);
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";
}
return friendlyName;
}
internal static string GetDescription(Type type)
{
var atr = type.GetCustomAttribute<DebugDescriptionAttribute>();
if (atr == null) return string.Empty;
return atr.description;
}
#endregion
}
[Serializable]
public abstract class TemplateComponentInitializer<T> : TemplateComponentInitializerBase, ITemplateComponentName
{
private static string _autoname = GetName(typeof(T));
private static Color _autoColor = GetColor(typeof(T));
private static string _autoDescription = GetDescription(typeof(T));
[SerializeField]
protected T component;
#region Properties
public override string Name => _autoname;
public override Color Color => _autoColor;
public override string Description => _autoDescription;
public sealed override Type Type => typeof(T);
#endregion
public abstract void Add(EcsWorld w, int e);
}
internal static class ITemplateBrowsableExt
{
private static MethodInfo memberwiseCloneMethdo = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
internal static ITemplateComponent Clone(this ITemplateComponent obj)
{
return (ITemplateComponent)memberwiseCloneMethdo.Invoke(obj, null);
}
}
#if UNITY_EDITOR
namespace Editors
{
internal static class TemplateBrowsableTypeCache
{
private static Type[] _types;
private static ITemplateComponent[] _dummies;
internal static ReadOnlySpan<Type> Types => _types;
internal static ReadOnlySpan<ITemplateComponent> Dummies => _dummies;
static TemplateBrowsableTypeCache()
{
List<Type> types = new List<Type>();
Type interfaceType = typeof(ITemplateComponent);
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var targetTypes = assembly.GetTypes().Where(type => !type.IsGenericType && (type.IsValueType|| type.IsClass) && type.GetCustomAttribute<SerializableAttribute>() != null);
types.AddRange(targetTypes.Where(type => interfaceType.IsAssignableFrom(type)));
foreach (var t in targetTypes)
{
if (t.IsSubclassOf(typeof(TemplateComponentInitializer<>)))
{
if(t.GetCustomAttribute<SerializableAttribute>() != null)
types.Add(t);
}
}
}
_types = types.ToArray();
_dummies = new ITemplateComponent[_types.Length];
for (int i = 0; i < _types.Length; i++)
_dummies[i] = (ITemplateComponent)Activator.CreateInstance(_types[i]);
}
}
}
#endif
}

View File

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

8
src/Extensions.meta Normal file
View File

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

View File

@ -0,0 +1,143 @@
using System.Runtime.CompilerServices;
using UnityEngine;
namespace DCFApixels.DragonECS
{
public class EcsEntityConnect : MonoBehaviour
{
private sealed class Subject : EcsSubject
{
public readonly EcsPool<UnityGameObject> unityGameObjects;
public Subject(Builder b)
{
unityGameObjects = b.Include<UnityGameObject>();
}
}
private entlong _entity;
private EcsWorld _world;
[SerializeField]
private EntityTemplatePreset[] _entityTemplatePresets;
[SerializeField]
private EntityTemplate[] _entityTemplates;
internal void SetTemplates_Editor(EntityTemplate[] tempaltes)
{
_entityTemplates = tempaltes;
}
#region Properties
public entlong Entity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entity;
}
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _world;
}
public bool IsAlive
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entity.IsAlive;
}
#endregion
public void ConnectWith(entlong entity, bool applyTemplates = false)
{
if(_entity.TryGetID(out int oldE) && _world != null)
{
var s = _world.GetSubject<Subject>();
s.unityGameObjects.Del(oldE);
}
_world = null;
if (entity.TryGetID(out int newE))
{
_entity = entity;
_world = _entity.World;
var s = _world.GetSubject<Subject>();
if (!s.unityGameObjects.Has(newE)) s.unityGameObjects.Add(newE) = new UnityGameObject(gameObject);
if (applyTemplates)
ApplyTemplates();
}
else
{
_entity = entlong.NULL;
}
}
public void ApplyTemplates() => ApplyTemplatesFor(_entity.ID);
public void ApplyTemplatesFor(int entityID)
{
foreach (var t in _entityTemplatePresets)
t.Apply(_world, entityID);
foreach (var t in _entityTemplates)
t.Apply(_world, entityID);
}
}
#if UNITY_EDITOR
namespace Editors
{
using UnityEditor;
[CustomEditor(typeof(EcsEntityConnect))]
public class EcsEntityEditor : Editor
{
private EcsEntityConnect Target => (EcsEntityConnect)target;
private GUIStyle _greenStyle;
private GUIStyle _redStyle;
private bool _isInit = false;
private void Init()
{
if (_isInit)
return;
_greenStyle = EcsEditor.GetStyle(new Color32(75, 255, 0, 100));
_redStyle = EcsEditor.GetStyle(new Color32(255, 0, 75, 100));
_isInit = true;
}
public override void OnInspectorGUI()
{
Init();
if (Target.IsAlive)
GUILayout.Box("Connected", _greenStyle, GUILayout.ExpandWidth(true));
else
GUILayout.Box("Not connected", _redStyle, GUILayout.ExpandWidth(true));
if(Target.Entity.TryGetID(out int id))
EditorGUILayout.IntField(id);
else
EditorGUILayout.IntField(0);
GUILayout.Label(Target.Entity.ToString());
base.OnInspectorGUI();
if(GUILayout.Button("Autoset Templates"))
{
Target.SetTemplates_Editor(Target.GetComponents<EntityTemplate>());
EditorUtility.SetDirty(target);
}
if (GUILayout.Button("Autoset Templates Cascade"))
{
foreach (var item in Target.GetComponentsInChildren<EcsEntityConnect>())
{
item.SetTemplates_Editor(item.GetComponents<EntityTemplate>());
EditorUtility.SetDirty(item);
}
}
}
}
}
#endif
}

View File

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

View File

@ -2,7 +2,7 @@
namespace DCFApixels.DragonECS
{
public interface IEcsLateRunSystem : IEcsSystem
public interface IEcsLateRunProcess : IEcsSystem
{
public void LateRun(EcsPipeline pipeline);
}
@ -10,10 +10,10 @@ namespace DCFApixels.DragonECS
{
public static void LateRun(this EcsPipeline systems)
{
systems.GetRunner<IEcsLateRunSystem>().LateRun(systems);
systems.GetRunner<IEcsLateRunProcess>().LateRun(systems);
}
}
public interface IEcsFixedRunSystem : IEcsSystem
public interface IEcsFixedRunProcess : IEcsSystem
{
public void FixedRun(EcsPipeline pipeline);
}
@ -21,14 +21,14 @@ namespace DCFApixels.DragonECS
{
public static void FixedRun(this EcsPipeline pipeline)
{
pipeline.GetRunner<IEcsFixedRunSystem>().FixedRun(pipeline);
pipeline.GetRunner<IEcsFixedRunProcess>().FixedRun(pipeline);
}
}
namespace Internal
{
[DebugColor(DebugColor.Orange)]
public class EcsLateRunSystemRunner : EcsRunner<IEcsLateRunSystem>, IEcsLateRunSystem
public class EcsLateRunSystemRunner : EcsRunner<IEcsLateRunProcess>, IEcsLateRunProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;
@ -58,7 +58,7 @@ namespace DCFApixels.DragonECS
#endif
}
[DebugColor(DebugColor.Orange)]
public class EcsFixedRunSystemRunner : EcsRunner<IEcsFixedRunSystem>, IEcsFixedRunSystem
public class EcsFixedRunSystemRunner : EcsRunner<IEcsFixedRunProcess>, IEcsFixedRunProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;

56
src/Extensions/Systems.cs Normal file
View File

@ -0,0 +1,56 @@
namespace DCFApixels.DragonECS
{
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteOneFrameComponentFixedSystem<TWorld, TComponent> : IEcsFixedRunProcess, IEcsInject<TWorld>
where TWorld : EcsWorld<TWorld>
where TComponent : struct, IEcsComponent
{
private TWorld _world;
public void Inject(TWorld obj) => _world = obj;
private sealed class Subject : EcsSubject
{
public EcsPool<TComponent> pool;
public Subject(Builder b)
{
pool = b.Include<TComponent>();
}
}
public void FixedRun(EcsPipeline pipeline)
{
foreach (var e in _world.Where(out Subject s))
{
//try
//{
s.pool.Del(e);
//}
//catch (System.Exception)
//{
//
// throw;
//}
}
}
}
public static class DeleteOneFrameComponentFixedSystemExt
{
private const string AUTO_DEL_FIXED_LAYER = nameof(AUTO_DEL_FIXED_LAYER);
public static EcsPipeline.Builder AutoDelFixed<TWorld, TComponent>(this EcsPipeline.Builder b)
where TWorld : EcsWorld<TWorld>
where TComponent : struct, IEcsComponent
{
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_FIXED_LAYER);
b.AddUnique(new DeleteOneFrameComponentFixedSystem<TWorld, TComponent>(), AUTO_DEL_FIXED_LAYER);
return b;
}
/// <summary> for EcsDefaultWorld </summary>
public static EcsPipeline.Builder AutoDelFixed<TComponent>(this EcsPipeline.Builder b)
where TComponent : struct, IEcsComponent
{
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_FIXED_LAYER);
b.AddUnique(new DeleteOneFrameComponentFixedSystem<EcsDefaultWorld, TComponent>(), AUTO_DEL_FIXED_LAYER);
return b;
}
}
}

View File

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

View File

@ -0,0 +1,72 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
namespace DCFApixels.DragonECS
{
[Serializable]
[DebugColor(255 / 3, 255, 0)]
public struct UnityComponent<T> : IEcsComponent, IEnumerable<T>//IntelliSense hack
where T : class
{
public T obj;
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException(); //IntelliSense hack
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); //IntelliSense hack
}
[Serializable]
[MovedFrom(false, "Client", null, "RefRigitBodyInitializer")]
public sealed class UnityComponentRigitBodyInitializer : TemplateComponentInitializer<UnityComponent<Rigidbody>>
{
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<Rigidbody>>().Add(e) = component;
}
[Serializable]
[MovedFrom(false, "Client", null, "RefAnimatorInitializer")]
public sealed class UnityComponentAnimatorInitializer : TemplateComponentInitializer<UnityComponent<Animator>>
{
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<Animator>>().Add(e) = component;
}
[Serializable]
public sealed class UnityComponentCharacterControllerInitializer : TemplateComponentInitializer<UnityComponent<CharacterController>>
{
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<CharacterController>>().Add(e) = component;
}
#region Colliders
[Serializable]
public sealed class UnityComponentColliderInitializer : TemplateComponentInitializer<UnityComponent<Collider>>
{
public override string Name => "UnityComponent/Collider/" + nameof(Collider);
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<Collider>>().Add(e) = component;
}
[Serializable]
public sealed class UnityComponentBoxColliderInitializer : TemplateComponentInitializer<UnityComponent<BoxCollider>>
{
public override string Name => "UnityComponent/Collider/" + nameof(BoxCollider);
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<BoxCollider>>().Add(e) = component;
}
[Serializable]
public sealed class UnityComponentSphereColliderInitializer : TemplateComponentInitializer<UnityComponent<SphereCollider>>
{
public override string Name => "UnityComponent/Collider/" + nameof(SphereCollider);
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<SphereCollider>>().Add(e) = component;
}
[Serializable]
public sealed class UnityComponentCapsuleColliderInitializer : TemplateComponentInitializer<UnityComponent<CapsuleCollider>>
{
public override string Name => "UnityComponent/Collider/" + nameof(CapsuleCollider);
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<CapsuleCollider>>().Add(e) = component;
}
[Serializable]
public sealed class UnityComponentMeshColliderInitializer : TemplateComponentInitializer<UnityComponent<MeshCollider>>
{
public override string Name => "UnityComponent/Collider/" + nameof(MeshCollider);
public override void Add(EcsWorld w, int e) => w.GetPool<UnityComponent<MeshCollider>>().Add(e) = component;
}
#endregion
}

View File

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

View File

@ -1,7 +1,5 @@
using System.Runtime.CompilerServices;
using UnityEngine;
using System.Linq;
using UnityEditor.ShortcutManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
@ -62,11 +60,11 @@ namespace DCFApixels.DragonECS
public static class GameObjectRefExt
{
public static EcsEntity NewEntityWithGameObject(this EcsWorld self, string name = "EcsEntity", GameObjectIcon icon = GameObjectIcon.NONE)
public static entlong NewEntityWithGameObject(this EcsWorld self, string name = "EcsEntity", GameObjectIcon icon = GameObjectIcon.NONE)
{
EcsEntity result = self.NewEntity();
entlong result = self.GetEntityLong(self.NewEntity());
GameObject newGameObject = new GameObject(name);
newGameObject.AddComponent<EcsEntityConnect>().ConectWith(result);
newGameObject.AddComponent<EcsEntityConnect>().ConnectWith(result);
// self.GetPool<UnityGameObject>().Add(result.id) =
#if UNITY_EDITOR
if (icon != GameObjectIcon.NONE)
@ -90,76 +88,4 @@ namespace DCFApixels.DragonECS
return result;
}
}
public class EcsEntityConnect : MonoBehaviour
{
private sealed class Query : EcsQuery
{
public readonly EcsPool<UnityGameObject> unityGameObjects;
public Query(Builder b)
{
unityGameObjects = b.Include<UnityGameObject>();
}
}
private EcsEntity _entity;
private EcsWorld _world;
#region Properties
public EcsEntity Entity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entity;
}
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _world;
}
public bool IsAlive
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entity.IsAlive;
}
#endregion
public void ConectWith(EcsEntity entity)
{
int e = _entity.id;
if (_world != null && _entity.IsNotNull)
{
var q = _world.Select<Query>();
q.unityGameObjects.Del(e);
}
_world = null;
_entity = entity;
if (_entity.IsNotNull)
{
_world = _entity.GetWorld();
var q = _world.Select<Query>();
if (!q.unityGameObjects.Has(e)) q.unityGameObjects.Add(e) = new UnityGameObject(gameObject);
}
}
}
#if UNITY_EDITOR
namespace Editors
{
using UnityEditor;
[CustomEditor(typeof(EcsEntityConnect))]
public class EcsEntityEditor : Editor
{
private EcsEntityConnect Target => (EcsEntityConnect)target;
public override void OnInspectorGUI()
{
EditorGUILayout.IntField(Target.Entity.id);
GUILayout.Label(Target.Entity.ToString());
}
}
}
#endif
}

44
src/Utils/BitMask.cs Normal file
View File

@ -0,0 +1,44 @@
using System;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS.Editors
{
internal class BitMask
{
private const int OFFSET = 5;
private const int MOD_MASK = 31;
private const int DATA_BITS = 32;
private int[] _data;
private int _size;
public BitMask(int size)
{
_data = Array.Empty<int>();
Resize(size);
}
public bool this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (_data[index >> OFFSET] & (1 << (index & MOD_MASK))) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if(value)
_data[index >> OFFSET] |= (1 << (index & MOD_MASK));
else
_data[index >> OFFSET] &= ~(1 << (index & MOD_MASK));
}
}
public void Resize(int newSize)
{
if (newSize <= _size)
return;
_size = newSize / DATA_BITS + 1;
Array.Resize(ref _data, _size);
}
}
}

11
src/Utils/BitMask.cs.meta Normal file
View File

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

212
src/Utils/SparseArray.cs Normal file
View File

@ -0,0 +1,212 @@
//SparseArray. Analogous to Dictionary<int, T>, but faster.
//Benchmark result of indexer.get speed test with 300 elements:
//[Dictinary: 5.786us] [SparseArray: 2.047us].
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Editors
{
internal class SparseArray<TValue>
{
public const int MIN_CAPACITY_BITS_OFFSET = 4;
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
private const int EMPTY = -1;
private int[] _buckets = Array.Empty<int>();
private Entry[] _entries = Array.Empty<Entry>();
private int _count;
private int _freeList;
private int _freeCount;
private int _modBitMask;
#region Properties
public TValue this[int key]
{
get => _entries[FindEntry(key)].value;
set => Insert(key, value);
}
public int Count => _count;
#endregion
#region Constructors
public SparseArray(int minCapacity = MIN_CAPACITY)
{
minCapacity = NormalizeCapacity(minCapacity);
_buckets = new int[minCapacity];
for (int i = 0; i < minCapacity; i++)
_buckets[i] = EMPTY;
_entries = new Entry[minCapacity];
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
}
#endregion
#region Add
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int key, TValue value)
{
#if DEBUG
if (Contains(key))
throw new ArgumentException("Contains(hashKey) is true");
#endif
Insert(key, value);
}
#endregion
#region Find/Insert/Remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindEntry(int key)
{
for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next)
if (_entries[i].hashKey == key) return i;
return -1;
}
private void Insert(int key, TValue value)
{
int targetBucket = key & _modBitMask;
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
_entries[i].value = value;
return;
}
}
int index;
if (_freeCount > 0)
{
index = _freeList;
_freeList = _entries[index].next;
_freeCount--;
}
else
{
if (_count == _entries.Length)
{
Resize();
targetBucket = key & _modBitMask;
}
index = _count++;
}
_entries[index].next = _buckets[targetBucket];
_entries[index].hashKey = key;
_entries[index].value = value;
_buckets[targetBucket] = index;
}
public bool Remove(int key)
{
int bucket = key & _modBitMask;
int last = -1;
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
if (last < 0)
{
_buckets[bucket] = _entries[i].next;
}
else
{
_entries[last].next = _entries[i].next;
}
_entries[i].next = _freeList;
_entries[i].hashKey = -1;
_entries[i].value = default;
_freeList = i;
_freeCount++;
return true;
}
}
return false;
}
#endregion
#region TryGetValue
public bool TryGetValue(int key, out TValue value)
{
int index = FindEntry(key);
if (index < 0)
{
value = default;
return false;
}
value = _entries[index].value;
return true;
}
#endregion
#region Contains
public bool Contains(int key)
{
return FindEntry(key) >= 0;
}
#endregion
#region Clear
public void Clear()
{
if (_count > 0)
{
for (int i = 0; i < _buckets.Length; i++)
{
_buckets[i] = -1;
}
Array.Clear(_entries, 0, _count);
_count = 0;
}
}
#endregion
#region Resize
private void Resize()
{
int newSize = _buckets.Length << 1;
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
Contract.Assert(newSize >= _entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++)
newBuckets[i] = EMPTY;
Entry[] newEntries = new Entry[newSize];
Array.Copy(_entries, 0, newEntries, 0, _count);
for (int i = 0; i < _count; i++)
{
if (newEntries[i].hashKey >= 0)
{
int bucket = newEntries[i].hashKey % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
_buckets = newBuckets;
_entries = newEntries;
}
private int NormalizeCapacity(int capacity)
{
int result = MIN_CAPACITY;
while (result < capacity) result <<= 1;
return result;
}
#endregion
#region Utils
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct Entry
{
public int next; // Index of next entry, -1 if last
public int hashKey;
public TValue value;
}
#endregion
}
}

View File

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