This commit is contained in:
DESKTOP-5RP3AKU\Jisol
2024-10-17 03:20:22 +08:00
parent dd2e0e8a46
commit 0d600a2786
1490 changed files with 12905 additions and 7825 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2dc2b025cc73430e8ca2ddb34aff461d
timeCreated: 1715159440

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 36445c10f96840848b0a286dcfe054d1
timeCreated: 1720699079

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class CleanupAttribute : Attribute
{
public readonly CleanupMode cleanupMode;
public CleanupAttribute(CleanupMode cleanupMode)
{
this.cleanupMode = cleanupMode;
}
}
public enum CleanupMode
{
RemoveComponent,
DestroyEntity
}
}

View File

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

View File

@@ -0,0 +1,30 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class ComponentNameAttribute : Attribute
{
public readonly string[] componentNames;
public ComponentNameAttribute(params string[] componentNames)
{
this.componentNames = componentNames;
}
}
}
namespace Entitas.CodeGeneration.Attributes
{
[Obsolete("Use [ComponentName] instead", false)]
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class CustomComponentNameAttribute : Attribute
{
public readonly string[] componentNames;
public CustomComponentNameAttribute(params string[] componentNames)
{
this.componentNames = componentNames;
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true)]
public class ContextAttribute : Attribute
{
public readonly string contextName;
public ContextAttribute(string contextName)
{
this.contextName = contextName;
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class)]
public class CustomEntityIndexAttribute : Attribute
{
public readonly Type contextType;
public CustomEntityIndexAttribute(Type contextType)
{
this.contextType = contextType;
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class DontGenerateAttribute : Attribute
{
public readonly bool generateIndex;
public DontGenerateAttribute(bool generateIndex = true)
{
this.generateIndex = generateIndex;
}
}
}

View File

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

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 58d2df893a4de1b42aba52402241032e
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,14 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
public abstract class AbstractEntityIndexAttribute : Attribute
{
public readonly EntityIndexType entityIndexType;
protected AbstractEntityIndexAttribute(EntityIndexType entityIndexType)
{
this.entityIndexType = entityIndexType;
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class)]
public class EntityIndexAttribute : AbstractEntityIndexAttribute
{
public EntityIndexAttribute() : base(EntityIndexType.EntityIndex) { }
}
}

View File

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

View File

@@ -0,0 +1,7 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Method)]
public class EntityIndexGetMethodAttribute : Attribute { }
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Entitas.CodeGeneration.Attributes
{
public enum EntityIndexType
{
EntityIndex,
PrimaryEntityIndex
}
}

View File

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

View File

@@ -0,0 +1,10 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class)]
public class PrimaryEntityIndexAttribute : AbstractEntityIndexAttribute
{
public PrimaryEntityIndexAttribute() : base(EntityIndexType.PrimaryEntityIndex) { }
}
}

View File

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

View File

@@ -0,0 +1,31 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true)]
public class EventAttribute : Attribute
{
public readonly EventTarget eventTarget;
public readonly EventType eventType;
public readonly int priority;
public EventAttribute(EventTarget eventTarget, EventType eventType = EventType.Added, int priority = 0)
{
this.eventTarget = eventTarget;
this.eventType = eventType;
this.priority = priority;
}
}
public enum EventTarget
{
Any,
Self
}
public enum EventType
{
Added,
Removed
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class FlagPrefixAttribute : Attribute
{
public readonly string prefix;
public FlagPrefixAttribute(string prefix)
{
this.prefix = prefix;
}
}
}

View File

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

View File

@@ -0,0 +1,7 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Method)]
public class PostConstructorAttribute : Attribute { }
}

View File

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

View File

@@ -0,0 +1,7 @@
using System;
namespace Entitas.CodeGeneration.Attributes
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class UniqueAttribute : Attribute { }
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,59 @@
using System;
using UnityEngine;
namespace Entitas.Unity
{
public class EntityLink : MonoBehaviour
{
public IEntity entity => _entity;
IEntity _entity;
bool _applicationIsQuitting;
public void Link(IEntity entity)
{
if (_entity != null)
throw new Exception($"EntityLink is already linked to {_entity}!");
_entity = entity;
_entity.Retain(this);
}
public void Unlink()
{
if (_entity == null)
throw new Exception("EntityLink is already unlinked!");
_entity.Release(this);
_entity = null;
}
void OnDestroy()
{
if (!_applicationIsQuitting && _entity != null)
Debug.LogWarning($"EntityLink got destroyed but is still linked to {_entity}!\nPlease call gameObject.Unlink() before it is destroyed.");
}
void OnApplicationQuit() => _applicationIsQuitting = true;
public override string ToString() => $"EntityLink({gameObject.name})";
}
public static class EntityLinkExtension
{
public static EntityLink GetEntityLink(this GameObject gameObject) => gameObject.GetComponent<EntityLink>();
public static EntityLink Link(this GameObject gameObject, IEntity entity)
{
var link = gameObject.GetEntityLink();
if (link == null)
link = gameObject.AddComponent<EntityLink>();
link.Link(entity);
return link;
}
public static void Unlink(this GameObject gameObject) =>
gameObject.GetEntityLink().Unlink();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
public class ContextObserver
{
public IContext context => _context;
public IGroup[] groups => _groups.ToArray();
public GameObject gameObject => _gameObject;
readonly IContext _context;
readonly List<IGroup> _groups;
readonly GameObject _gameObject;
readonly Stack<EntityBehaviour> _entityBehaviourPool = new Stack<EntityBehaviour>();
readonly StringBuilder _toStringBuilder = new StringBuilder();
public ContextObserver(IContext context)
{
_context = context;
_groups = new List<IGroup>();
_gameObject = new GameObject();
_gameObject.AddComponent<ContextObserverBehaviour>().Init(this);
_context.OnEntityCreated += onEntityCreated;
_context.OnGroupCreated += onGroupCreated;
}
public void Deactivate()
{
_context.OnEntityCreated -= onEntityCreated;
_context.OnGroupCreated -= onGroupCreated;
}
void onEntityCreated(IContext context, IEntity entity)
{
var entityBehaviour = _entityBehaviourPool.Count > 0
? _entityBehaviourPool.Pop()
: new GameObject().AddComponent<EntityBehaviour>();
entityBehaviour.Init(context, entity, _entityBehaviourPool);
entityBehaviour.transform.SetParent(_gameObject.transform, false);
entityBehaviour.transform.SetAsLastSibling();
}
void onGroupCreated(IContext context, IGroup group) => _groups.Add(group);
public override string ToString()
{
_toStringBuilder.Length = 0;
_toStringBuilder
.Append(_context.contextInfo.name).Append(" (")
.Append(_context.count).Append(" entities, ")
.Append(_context.reusableEntitiesCount).Append(" reusable, ");
if (_context.retainedEntitiesCount != 0)
{
_toStringBuilder
.Append(_context.retainedEntitiesCount).Append(" retained, ");
}
_toStringBuilder
.Append(_groups.Count)
.Append(" groups)");
var str = _toStringBuilder.ToString();
_gameObject.name = str;
return str;
}
}
}

View File

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

View File

@@ -0,0 +1,28 @@
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
[ExecuteInEditMode]
public class ContextObserverBehaviour : MonoBehaviour
{
public ContextObserver contextObserver => _contextObserver;
ContextObserver _contextObserver;
public void Init(ContextObserver contextObserver)
{
_contextObserver = contextObserver;
Update();
}
void Update()
{
if (_contextObserver == null)
gameObject.DestroyGameObject();
else if (_contextObserver.gameObject != null)
_contextObserver.gameObject.name = _contextObserver.ToString();
}
void OnDestroy() => _contextObserver.Deactivate();
}
}

View File

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

View File

@@ -0,0 +1,20 @@
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
public static class ContextObserverExtension
{
public static ContextObserverBehaviour FindContextObserver(this IContext context)
{
var observers = Object.FindObjectsOfType<ContextObserverBehaviour>();
for (var i = 0; i < observers.Length; i++)
{
var observer = observers[i];
if (observer.contextObserver.context == context)
return observer;
}
return null;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,267 @@
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
public enum AvgResetInterval
{
Always = 1,
VeryFast = 30,
Fast = 60,
Normal = 120,
Slow = 300,
Never = int.MaxValue
}
public class DebugSystems : Systems
{
public static AvgResetInterval avgResetInterval = AvgResetInterval.Never;
public int totalInitializeSystemsCount
{
get
{
var total = 0;
foreach (var system in _initializeSystems)
total += system is DebugSystems debugSystems ? debugSystems.totalInitializeSystemsCount : 1;
return total;
}
}
public int totalExecuteSystemsCount
{
get
{
var total = 0;
foreach (var system in _executeSystems)
total += system is DebugSystems debugSystems ? debugSystems.totalExecuteSystemsCount : 1;
return total;
}
}
public int totalCleanupSystemsCount
{
get
{
var total = 0;
foreach (var system in _cleanupSystems)
total += system is DebugSystems debugSystems ? debugSystems.totalCleanupSystemsCount : 1;
return total;
}
}
public int totalTearDownSystemsCount
{
get
{
var total = 0;
foreach (var system in _tearDownSystems)
total += system is DebugSystems debugSystems ? debugSystems.totalTearDownSystemsCount : 1;
return total;
}
}
public int totalSystemsCount
{
get
{
var total = 0;
foreach (var system in _systems)
total += system is DebugSystems debugSystems ? debugSystems.totalSystemsCount : 1;
return total;
}
}
public int initializeSystemsCount => _initializeSystems.Count;
public int executeSystemsCount => _executeSystems.Count;
public int cleanupSystemsCount => _cleanupSystems.Count;
public int tearDownSystemsCount => _tearDownSystems.Count;
public string name => _name;
public GameObject gameObject => _gameObject;
public SystemInfo systemInfo => _systemInfo;
public double executeDuration => _executeDuration;
public double cleanupDuration => _cleanupDuration;
public SystemInfo[] initializeSystemInfos => _initializeSystemInfos.ToArray();
public SystemInfo[] executeSystemInfos => _executeSystemInfos.ToArray();
public SystemInfo[] syncSystemInfos => _syncSystemInfos.ToArray();
public SystemInfo[] cleanupSystemInfos => _cleanupSystemInfos.ToArray();
public SystemInfo[] tearDownSystemInfos => _tearDownSystemInfos.ToArray();
public bool paused;
string _name;
List<ISystem> _systems;
GameObject _gameObject;
SystemInfo _systemInfo;
List<SystemInfo> _initializeSystemInfos;
List<SystemInfo> _executeSystemInfos;
List<SystemInfo> _syncSystemInfos;
List<SystemInfo> _cleanupSystemInfos;
List<SystemInfo> _tearDownSystemInfos;
Stopwatch _stopwatch;
public Stopwatch StopWatch;
double _executeDuration;
double _cleanupDuration;
public double SyncDuration;
public DebugSystems(string name)
{
initialize(name);
}
protected DebugSystems(bool noInit) { }
protected void initialize(string name)
{
_name = name;
_gameObject = new GameObject(name);
_gameObject.AddComponent<DebugSystemsBehaviour>().Init(this);
_systemInfo = new SystemInfo(this);
_systems = new List<ISystem>();
_initializeSystemInfos = new List<SystemInfo>();
_executeSystemInfos = new List<SystemInfo>();
_syncSystemInfos = new List<SystemInfo>();
_cleanupSystemInfos = new List<SystemInfo>();
_tearDownSystemInfos = new List<SystemInfo>();
_stopwatch = new Stopwatch();
}
public override Systems Add(ISystem system)
{
_systems.Add(system);
SystemInfo childSystemInfo;
if (system is DebugSystems debugSystems)
{
childSystemInfo = debugSystems.systemInfo;
debugSystems.gameObject.transform.SetParent(_gameObject.transform, false);
}
else
{
childSystemInfo = new SystemInfo(system);
}
childSystemInfo.parentSystemInfo = _systemInfo;
if (childSystemInfo.isInitializeSystems) _initializeSystemInfos.Add(childSystemInfo);
if (childSystemInfo.isExecuteSystems || childSystemInfo.isReactiveSystems) _executeSystemInfos.Add(childSystemInfo);
if (childSystemInfo.isSyncSystems) _syncSystemInfos.Add(childSystemInfo);
if (childSystemInfo.isCleanupSystems) _cleanupSystemInfos.Add(childSystemInfo);
if (childSystemInfo.isTearDownSystems) _tearDownSystemInfos.Add(childSystemInfo);
return base.Add(system);
}
public void ResetDurations()
{
foreach (var systemInfo in _executeSystemInfos)
systemInfo.ResetDurations();
foreach (var system in _systems)
if (system is DebugSystems debugSystems)
debugSystems.ResetDurations();
}
public override void Initialize()
{
for (var i = 0; i < _initializeSystems.Count; i++)
{
var systemInfo = _initializeSystemInfos[i];
if (systemInfo.isActive)
{
_stopwatch.Reset();
_stopwatch.Start();
_initializeSystems[i].Initialize();
_stopwatch.Stop();
systemInfo.initializationDuration = _stopwatch.Elapsed.TotalMilliseconds;
}
}
}
public override void Execute()
{
if (!paused)
StepExecute();
}
public override void Cleanup()
{
if (!paused)
StepCleanup();
}
public void StepExecute()
{
_executeDuration = 0;
if (Time.frameCount % (int)avgResetInterval == 0)
ResetDurations();
for (var i = 0; i < _executeSystems.Count; i++)
{
var systemInfo = _executeSystemInfos[i];
if (systemInfo.isActive)
{
_stopwatch.Reset();
_stopwatch.Start();
_executeSystems[i].Execute();
_stopwatch.Stop();
var duration = _stopwatch.Elapsed.TotalMilliseconds;
_executeDuration += duration;
systemInfo.AddExecutionDuration(duration);
}
}
}
public void StepCleanup()
{
_cleanupDuration = 0;
for (var i = 0; i < _cleanupSystems.Count; i++)
{
var systemInfo = _cleanupSystemInfos[i];
if (systemInfo.isActive)
{
_stopwatch.Reset();
_stopwatch.Start();
_cleanupSystems[i].Cleanup();
_stopwatch.Stop();
var duration = _stopwatch.Elapsed.TotalMilliseconds;
_cleanupDuration += duration;
systemInfo.AddCleanupDuration(duration);
}
}
}
public override void TearDown()
{
for (var i = 0; i < _tearDownSystems.Count; i++)
{
var systemInfo = _tearDownSystemInfos[i];
if (systemInfo.isActive)
{
_stopwatch.Reset();
_stopwatch.Start();
_tearDownSystems[i].TearDown();
_stopwatch.Stop();
systemInfo.teardownDuration = _stopwatch.Elapsed.TotalMilliseconds;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,16 @@
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
public class DebugSystemsBehaviour : MonoBehaviour
{
public DebugSystems systems => _systems;
DebugSystems _systems;
public void Init(DebugSystems systems)
{
_systems = systems;
}
}
}

View File

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

View File

@@ -0,0 +1,152 @@
using System;
using JNGame.Sync;
namespace Entitas.VisualDebugging.Unity
{
[Flags]
public enum SystemInterfaceFlags
{
None = 0,
IInitializeSystem = 1 << 1,
IExecuteSystem = 1 << 2,
ICleanupSystem = 1 << 3,
ITearDownSystem = 1 << 4,
IReactiveSystem = 1 << 5,
ISyncSystem = 1 << 6
}
public class SystemInfo
{
public ISystem system => _system;
public string systemName => _systemName;
public bool isInitializeSystems => (_interfaceFlags & SystemInterfaceFlags.IInitializeSystem) == SystemInterfaceFlags.IInitializeSystem;
public bool isExecuteSystems => (_interfaceFlags & SystemInterfaceFlags.IExecuteSystem) == SystemInterfaceFlags.IExecuteSystem;
public bool isCleanupSystems => (_interfaceFlags & SystemInterfaceFlags.ICleanupSystem) == SystemInterfaceFlags.ICleanupSystem;
public bool isTearDownSystems => (_interfaceFlags & SystemInterfaceFlags.ITearDownSystem) == SystemInterfaceFlags.ITearDownSystem;
public bool isReactiveSystems => (_interfaceFlags & SystemInterfaceFlags.IReactiveSystem) == SystemInterfaceFlags.IReactiveSystem;
public bool isSyncSystems => (_interfaceFlags & SystemInterfaceFlags.ISyncSystem) == SystemInterfaceFlags.ISyncSystem;
public double initializationDuration { get; set; }
public double accumulatedExecutionDuration => _accumulatedExecutionDuration;
public double minExecutionDuration => _minExecutionDuration;
public double maxExecutionDuration => _maxExecutionDuration;
public double averageExecutionDuration => _executionDurationsCount == 0 ? 0 : _accumulatedExecutionDuration / _executionDurationsCount;
public double minSyncDuration => _minSyncDuration;
public double maxSyncDuration => _maxSyncDuration;
public double averageSyncDuration => _syncDurationsCount == 0 ? 0 : _accumulatedSyncDuration / _syncDurationsCount;
public double accumulatedCleanupDuration => _accumulatedCleanupDuration;
public double minCleanupDuration => _minCleanupDuration;
public double maxCleanupDuration => _maxCleanupDuration;
public double averageCleanupDuration => _cleanupDurationsCount == 0 ? 0 : _accumulatedCleanupDuration / _cleanupDurationsCount;
public double cleanupDuration { get; set; }
public double teardownDuration { get; set; }
public bool areAllParentsActive => parentSystemInfo == null || (parentSystemInfo.isActive && parentSystemInfo.areAllParentsActive);
public SystemInfo parentSystemInfo;
public bool isActive;
readonly ISystem _system;
readonly SystemInterfaceFlags _interfaceFlags;
readonly string _systemName;
double _accumulatedExecutionDuration;
double _minExecutionDuration;
double _maxExecutionDuration;
int _executionDurationsCount;
double _accumulatedSyncDuration;
double _minSyncDuration;
double _maxSyncDuration;
int _syncDurationsCount;
double _accumulatedCleanupDuration;
double _minCleanupDuration;
double _maxCleanupDuration;
int _cleanupDurationsCount;
public SystemInfo(ISystem system)
{
_system = system;
_interfaceFlags = getInterfaceFlags(system);
var debugSystem = system as DebugSystems;
_systemName = debugSystem != null
? debugSystem.name
: system.GetType().Name.RemoveSystemSuffix();
isActive = true;
}
public void AddExecutionDuration(double executionDuration)
{
if (executionDuration < _minExecutionDuration || _minExecutionDuration == 0)
_minExecutionDuration = executionDuration;
if (executionDuration > _maxExecutionDuration)
_maxExecutionDuration = executionDuration;
_accumulatedExecutionDuration += executionDuration;
_executionDurationsCount += 1;
}
public void AddSyncUpdateDuration(double executionDuration)
{
if (executionDuration < _minSyncDuration || _minSyncDuration == 0)
_minSyncDuration = executionDuration;
if (executionDuration > _maxSyncDuration)
_maxSyncDuration = executionDuration;
_accumulatedSyncDuration += executionDuration;
_syncDurationsCount += 1;
}
public void AddCleanupDuration(double cleanupDuration)
{
if (cleanupDuration < _minCleanupDuration || _minCleanupDuration == 0)
_minCleanupDuration = cleanupDuration;
if (cleanupDuration > _maxCleanupDuration)
_maxCleanupDuration = cleanupDuration;
_accumulatedCleanupDuration += cleanupDuration;
_cleanupDurationsCount += 1;
}
public void ResetDurations()
{
_accumulatedExecutionDuration = 0;
_executionDurationsCount = 0;
_accumulatedSyncDuration = 0;
_syncDurationsCount = 0;
_accumulatedCleanupDuration = 0;
_cleanupDurationsCount = 0;
}
static SystemInterfaceFlags getInterfaceFlags(ISystem system)
{
var flags = SystemInterfaceFlags.None;
if (system is IInitializeSystem)
flags |= SystemInterfaceFlags.IInitializeSystem;
if (system is IReactiveSystem)
flags |= SystemInterfaceFlags.IReactiveSystem;
else if (system is IExecuteSystem)
flags |= SystemInterfaceFlags.IExecuteSystem;
if (system is ICleanupSystem)
flags |= SystemInterfaceFlags.ICleanupSystem;
if (system is ITearDownSystem)
flags |= SystemInterfaceFlags.ITearDownSystem;
if (system is IJNSyncCycle)
flags |= SystemInterfaceFlags.ISyncSystem;
return flags;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
using System;
namespace Entitas.VisualDebugging.Unity
{
[AttributeUsage(AttributeTargets.Class)]
public class DontDrawComponentAttribute : Attribute { }
}

View File

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

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
[ExecuteInEditMode]
public class EntityBehaviour : MonoBehaviour
{
public IContext context => _context;
public IEntity entity => _entity;
IContext _context;
IEntity _entity;
Stack<EntityBehaviour> _entityBehaviourPool;
string _cachedName;
public void Init(IContext context, IEntity entity, Stack<EntityBehaviour> entityBehaviourPool)
{
_context = context;
_entity = entity;
_entityBehaviourPool = entityBehaviourPool;
_entity.OnEntityReleased += onEntityReleased;
gameObject.hideFlags = HideFlags.None;
gameObject.SetActive(true);
Update();
}
void onEntityReleased(IEntity e)
{
_entity.OnEntityReleased -= onEntityReleased;
gameObject.SetActive(false);
gameObject.hideFlags = HideFlags.HideInHierarchy;
_entityBehaviourPool.Push(this);
_cachedName = null;
name = string.Empty;
}
void Update()
{
if (_entity != null && _cachedName != _entity.ToString())
name = _cachedName = _entity.ToString();
}
void OnDestroy()
{
if (_entity != null)
_entity.OnEntityReleased -= onEntityReleased;
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using UnityEngine;
namespace Entitas.VisualDebugging.Unity
{
public static class GameObjectDestroyExtension
{
public static void DestroyGameObject(this GameObject gameObject)
{
if (Application.isPlaying)
Object.Destroy(gameObject);
else
Object.DestroyImmediate(gameObject);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Entitas
{
/// A Collector can observe one or more groups from the same context
/// and collects changed entities based on the specified groupEvent.
public class Collector<TEntity> : ICollector<TEntity> where TEntity : class, IEntity
{
/// Returns all collected entities.
/// Call collector.ClearCollectedEntities()
/// once you processed all entities.
public HashSet<TEntity> collectedEntities => _collectedEntities;
/// Returns the number of all collected entities.
public int count => _collectedEntities.Count;
readonly HashSet<TEntity> _collectedEntities;
readonly IGroup<TEntity>[] _groups;
readonly GroupEvent[] _groupEvents;
readonly GroupChanged<TEntity> _addEntityCache;
string _toStringCache;
StringBuilder _toStringBuilder;
/// Creates a Collector and will collect changed entities
/// based on the specified groupEvent.
public Collector(IGroup<TEntity> group, GroupEvent groupEvent) : this(new[] {group}, new[] {groupEvent}) { }
/// Creates a Collector and will collect changed entities
/// based on the specified groupEvents.
public Collector(IGroup<TEntity>[] groups, GroupEvent[] groupEvents)
{
_groups = groups;
_collectedEntities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
_groupEvents = groupEvents;
if (groups.Length != groupEvents.Length)
throw new CollectorException(
$"Unbalanced count with groups ({groups.Length}) and group events ({groupEvents.Length}).",
"Group and group events count must be equal."
);
_addEntityCache = addEntity;
Activate();
}
/// Activates the Collector and will start collecting
/// changed entities. Collectors are activated by default.
public void Activate()
{
for (var i = 0; i < _groups.Length; i++)
{
var group = _groups[i];
var groupEvent = _groupEvents[i];
switch (groupEvent)
{
case GroupEvent.Added:
group.OnEntityAdded -= _addEntityCache;
group.OnEntityAdded += _addEntityCache;
break;
case GroupEvent.Removed:
group.OnEntityRemoved -= _addEntityCache;
group.OnEntityRemoved += _addEntityCache;
break;
case GroupEvent.AddedOrRemoved:
group.OnEntityAdded -= _addEntityCache;
group.OnEntityAdded += _addEntityCache;
group.OnEntityRemoved -= _addEntityCache;
group.OnEntityRemoved += _addEntityCache;
break;
}
}
}
/// Deactivates the Collector.
/// This will also clear all collected entities.
/// Collectors are activated by default.
public void Deactivate()
{
for (var i = 0; i < _groups.Length; i++)
{
var group = _groups[i];
group.OnEntityAdded -= _addEntityCache;
group.OnEntityRemoved -= _addEntityCache;
}
ClearCollectedEntities();
}
/// Returns all collected entities and casts them.
/// Call collector.ClearCollectedEntities()
/// once you processed all entities.
public IEnumerable<TCast> GetCollectedEntities<TCast>() where TCast : class, IEntity =>
_collectedEntities.Cast<TCast>();
/// Clears all collected entities.
public void ClearCollectedEntities()
{
foreach (var entity in _collectedEntities)
entity.Release(this);
_collectedEntities.Clear();
}
void addEntity(IGroup<TEntity> group, TEntity entity, int index, IComponent component)
{
if (_collectedEntities.Add(entity))
entity.Retain(this);
}
public override string ToString()
{
if (_toStringCache == null)
{
_toStringBuilder ??= new StringBuilder();
_toStringBuilder.Length = 0;
_toStringBuilder.Append("Collector(");
const string separator = ", ";
var lastSeparator = _groups.Length - 1;
for (var i = 0; i < _groups.Length; i++)
{
_toStringBuilder.Append(_groups[i]);
if (i < lastSeparator)
_toStringBuilder.Append(separator);
}
_toStringBuilder.Append(")");
_toStringCache = _toStringBuilder.ToString();
}
return _toStringCache;
}
~Collector() => Deactivate();
}
}

View File

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

View File

@@ -0,0 +1,28 @@
namespace Entitas
{
public static class CollectorContextExtension
{
/// Creates a Collector.
public static ICollector<TEntity> CreateCollector<TEntity>(
this IContext<TEntity> context, IMatcher<TEntity> matcher) where TEntity : class, IEntity
{
return context.CreateCollector(new TriggerOnEvent<TEntity>(matcher, GroupEvent.Added));
}
/// Creates a Collector.
public static ICollector<TEntity> CreateCollector<TEntity>(
this IContext<TEntity> context, params TriggerOnEvent<TEntity>[] triggers) where TEntity : class, IEntity
{
var groups = new IGroup<TEntity>[triggers.Length];
var groupEvents = new GroupEvent[triggers.Length];
for (var i = 0; i < triggers.Length; i++)
{
groups[i] = context.GetGroup(triggers[i].matcher);
groupEvents[i] = triggers[i].groupEvent;
}
return new Collector<TEntity>(groups, groupEvents);
}
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace Entitas
{
public class CollectorException : EntitasException
{
public CollectorException(string message, string hint) : base(message, hint) { }
}
}

View File

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

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace Entitas
{
public interface ICollector
{
int count { get; }
void Activate();
void Deactivate();
void ClearCollectedEntities();
IEnumerable<TCast> GetCollectedEntities<TCast>() where TCast : class, IEntity;
}
public interface ICollector<TEntity> : ICollector where TEntity : class, IEntity
{
HashSet<TEntity> collectedEntities { get; }
}
}

View File

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

View File

@@ -0,0 +1,14 @@
namespace Entitas
{
public struct TriggerOnEvent<TEntity> where TEntity : class, IEntity
{
public readonly IMatcher<TEntity> matcher;
public readonly GroupEvent groupEvent;
public TriggerOnEvent(IMatcher<TEntity> matcher, GroupEvent groupEvent)
{
this.matcher = matcher;
this.groupEvent = groupEvent;
}
}
}

View File

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

View File

@@ -0,0 +1,14 @@
namespace Entitas
{
public static class TriggerOnEventMatcherExtension
{
public static TriggerOnEvent<TEntity> Added<TEntity>(this IMatcher<TEntity> matcher) where TEntity : class, IEntity =>
new TriggerOnEvent<TEntity>(matcher, GroupEvent.Added);
public static TriggerOnEvent<TEntity> Removed<TEntity>(this IMatcher<TEntity> matcher) where TEntity : class, IEntity =>
new TriggerOnEvent<TEntity>(matcher, GroupEvent.Removed);
public static TriggerOnEvent<TEntity> AddedOrRemoved<TEntity>(this IMatcher<TEntity> matcher) where TEntity : class, IEntity =>
new TriggerOnEvent<TEntity>(matcher, GroupEvent.AddedOrRemoved);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,344 @@
using System;
using System.Collections.Generic;
using DesperateDevs.Caching;
namespace Entitas
{
/// A context manages the lifecycle of entities and groups.
/// You can create and destroy entities and get groups of entities.
/// The preferred way to create a context is to use the generated methods
/// from the code generator, e.g. var context = new GameContext();
public class Context<TEntity> : IContext<TEntity> where TEntity : class, IEntity
{
/// Occurs when an entity gets created.
public event ContextEntityChanged OnEntityCreated;
/// Occurs when an entity will be destroyed.
public event ContextEntityChanged OnEntityWillBeDestroyed;
/// Occurs when an entity got destroyed.
public event ContextEntityChanged OnEntityDestroyed;
/// Occurs when a group gets created for the first time.
public event ContextGroupChanged OnGroupCreated;
/// The total amount of components an entity can possibly have.
/// This value is generated by the code generator,
/// e.g ComponentLookup.TotalComponents.
public int totalComponents => _totalComponents;
/// Returns all componentPools. componentPools is used to reuse
/// removed components.
/// Removed components will be pushed to the componentPool.
/// Use entity.CreateComponent(index, type) to get a new or reusable
/// component from the componentPool.
public Stack<IComponent>[] componentPools => _componentPools;
/// The contextInfo contains information about the context.
/// It's used to provide better error messages.
public ContextInfo contextInfo => _contextInfo;
/// Returns the number of entities in the context.
public int count => _entities.Count;
/// Returns the number of entities in the internal ObjectPool
/// for entities which can be reused.
public int reusableEntitiesCount => _reusableEntities.Count;
/// Returns the number of entities that are currently retained by
/// other objects (e.g. Group, Collector, ReactiveSystem).
public int retainedEntitiesCount => _retainedEntities.Count;
readonly int _totalComponents;
readonly Stack<IComponent>[] _componentPools;
readonly ContextInfo _contextInfo;
readonly Func<IEntity, IAERC> _aercFactory;
readonly Func<TEntity> _entityFactory;
readonly HashSet<TEntity> _entities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
readonly Stack<TEntity> _reusableEntities = new Stack<TEntity>();
readonly HashSet<TEntity> _retainedEntities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
readonly Dictionary<IMatcher<TEntity>, IGroup<TEntity>> _groups = new Dictionary<IMatcher<TEntity>, IGroup<TEntity>>();
readonly List<IGroup<TEntity>>[] _groupsForIndex;
readonly ObjectPool<List<GroupChanged<TEntity>>> _groupChangedListPool;
readonly Dictionary<string, IEntityIndex> _entityIndices;
int _creationIndex;
protected TEntity[] _entitiesCache;
// Cache delegates to avoid gc allocations
readonly EntityComponentChanged _cachedEntityChanged;
readonly EntityComponentReplaced _cachedComponentReplaced;
readonly EntityEvent _cachedEntityReleased;
readonly EntityEvent _cachedDestroyEntity;
/// The preferred way to create a context is to use the generated methods
/// from the code generator, e.g. var context = new GameContext();
public Context(int totalComponents, Func<TEntity> entityFactory) : this(totalComponents, 0, null, null, entityFactory) { }
/// The preferred way to create a context is to use the generated methods
/// from the code generator, e.g. var context = new GameContext();
public Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo, Func<IEntity, IAERC> aercFactory, Func<TEntity> entityFactory)
{
_totalComponents = totalComponents;
_creationIndex = startCreationIndex;
if (contextInfo != null)
{
_contextInfo = contextInfo;
if (contextInfo.componentNames.Length != totalComponents)
throw new ContextInfoException(this, contextInfo);
}
else
{
_contextInfo = createDefaultContextInfo();
}
_aercFactory = aercFactory ?? (entity => new SafeAERC(entity));
_entityFactory = entityFactory;
_groupsForIndex = new List<IGroup<TEntity>>[totalComponents];
_componentPools = new Stack<IComponent>[totalComponents];
_entityIndices = new Dictionary<string, IEntityIndex>();
_groupChangedListPool = new ObjectPool<List<GroupChanged<TEntity>>>(
() => new List<GroupChanged<TEntity>>(),
list => list.Clear()
);
// Cache delegates to avoid gc allocations
_cachedEntityChanged = updateGroupsComponentAddedOrRemoved;
_cachedComponentReplaced = updateGroupsComponentReplaced;
_cachedEntityReleased = onEntityReleased;
_cachedDestroyEntity = onDestroyEntity;
}
ContextInfo createDefaultContextInfo()
{
var componentNames = new string[_totalComponents];
const string prefix = "Index ";
for (var i = 0; i < componentNames.Length; i++)
componentNames[i] = prefix + i;
return new ContextInfo("Unnamed Context", componentNames, null);
}
/// Creates a new entity or gets a reusable entity from the
/// internal ObjectPool for entities.
public virtual TEntity CreateEntity()
{
TEntity entity;
if (_reusableEntities.Count > 0)
{
entity = _reusableEntities.Pop();
entity.Reactivate(_creationIndex++);
}
else
{
entity = _entityFactory();
entity.Initialize(_creationIndex++, _totalComponents, _componentPools, _contextInfo, _aercFactory(entity));
}
_entities.Add(entity);
entity.Retain(this);
_entitiesCache = null;
entity.OnComponentAdded += _cachedEntityChanged;
entity.OnComponentRemoved += _cachedEntityChanged;
entity.OnComponentReplaced += _cachedComponentReplaced;
entity.OnEntityReleased += _cachedEntityReleased;
entity.OnDestroyEntity += _cachedDestroyEntity;
OnEntityCreated?.Invoke(this, entity);
return entity;
}
/// Destroys all entities in the context.
/// Throws an exception if there are still retained entities.
public void DestroyAllEntities()
{
var entities = GetEntities();
for (var i = 0; i < entities.Length; i++)
entities[i].Destroy();
_entities.Clear();
if (_retainedEntities.Count != 0)
throw new ContextStillHasRetainedEntitiesException(this, _retainedEntities);
}
/// Determines whether the context has the specified entity.
public bool HasEntity(TEntity entity) => _entities.Contains(entity);
/// Returns all entities which are currently in the context.
public virtual TEntity[] GetEntities()
{
if (_entitiesCache == null)
{
_entitiesCache = new TEntity[_entities.Count];
_entities.CopyTo(_entitiesCache);
}
return _entitiesCache;
}
/// Returns a group for the specified matcher.
/// Calling context.GetGroup(matcher) with the same matcher will always
/// return the same instance of the group.
public IGroup<TEntity> GetGroup(IMatcher<TEntity> matcher)
{
if (!_groups.TryGetValue(matcher, out var group))
{
group = new Group<TEntity>(matcher);
var entities = GetEntities();
for (var i = 0; i < entities.Length; i++)
group.HandleEntitySilently(entities[i]);
_groups.Add(matcher, group);
for (var i = 0; i < matcher.indices.Length; i++)
{
var index = matcher.indices[i];
if (_groupsForIndex[index] == null)
_groupsForIndex[index] = new List<IGroup<TEntity>>();
_groupsForIndex[index].Add(group);
}
OnGroupCreated?.Invoke(this, group);
}
return group;
}
/// Adds the IEntityIndex for the specified name.
/// There can only be one IEntityIndex per name.
public void AddEntityIndex(IEntityIndex entityIndex)
{
if (_entityIndices.ContainsKey(entityIndex.name))
throw new ContextEntityIndexDoesAlreadyExistException(this, entityIndex.name);
_entityIndices.Add(entityIndex.name, entityIndex);
}
/// Gets the IEntityIndex for the specified name.
public IEntityIndex GetEntityIndex(string name)
{
if (!_entityIndices.TryGetValue(name, out var entityIndex))
throw new ContextEntityIndexDoesNotExistException(this, name);
return entityIndex;
}
/// Resets the creationIndex back to 0.
public void ResetCreationIndex() => _creationIndex = 0;
/// Clears the componentPool at the specified index.
public void ClearComponentPool(int index) => _componentPools[index]?.Clear();
/// Clears all componentPools.
public void ClearComponentPools()
{
for (var i = 0; i < _componentPools.Length; i++)
ClearComponentPool(i);
}
/// Resets the context (destroys all entities and
/// resets creationIndex back to 0).
public void Reset()
{
DestroyAllEntities();
ResetCreationIndex();
}
/// Removes all event handlers
/// OnEntityCreated, OnEntityWillBeDestroyed,
/// OnEntityDestroyed and OnGroupCreated
public void RemoveAllEventHandlers()
{
OnEntityCreated = null;
OnEntityWillBeDestroyed = null;
OnEntityDestroyed = null;
OnGroupCreated = null;
}
public override string ToString()
{
return _contextInfo.name;
}
void updateGroupsComponentAddedOrRemoved(IEntity entity, int index, IComponent component)
{
var groups = _groupsForIndex[index];
if (groups != null)
{
var events = _groupChangedListPool.Get();
var tEntity = (TEntity)entity;
for (var i = 0; i < groups.Count; i++)
events.Add(groups[i].HandleEntity(tEntity));
for (var i = 0; i < events.Count; i++)
events[i]?.Invoke(groups[i], tEntity, index, component);
_groupChangedListPool.Push(events);
}
}
void updateGroupsComponentReplaced(IEntity entity, int index, IComponent previousComponent, IComponent newComponent)
{
var groups = _groupsForIndex[index];
if (groups != null)
for (var i = 0; i < groups.Count; i++)
groups[i].UpdateEntity((TEntity)entity, index, previousComponent, newComponent);
}
void onEntityReleased(IEntity entity)
{
if (entity.isEnabled)
throw new EntityIsNotDestroyedException($"Cannot release {entity}!");
var tEntity = (TEntity)entity;
entity.RemoveAllOnEntityReleasedHandlers();
_retainedEntities.Remove(tEntity);
_reusableEntities.Push(tEntity);
}
void onDestroyEntity(IEntity entity)
{
var tEntity = (TEntity)entity;
var removed = _entities.Remove(tEntity);
if (!removed)
throw new ContextDoesNotContainEntityException(
$"'{this}' cannot destroy {tEntity}!",
"This cannot happen!?!"
);
_entitiesCache = null;
OnEntityWillBeDestroyed?.Invoke(this, tEntity);
tEntity.InternalDestroy();
OnEntityDestroyed?.Invoke(this, tEntity);
if (tEntity.retainCount == 1)
{
// Can be released immediately without
// adding to _retainedEntities
tEntity.OnEntityReleased -= _cachedEntityReleased;
_reusableEntities.Push(tEntity);
tEntity.Release(this);
tEntity.RemoveAllOnEntityReleasedHandlers();
}
else
{
_retainedEntities.Add(tEntity);
tEntity.Release(this);
}
}
}
}

View File

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

View File

@@ -0,0 +1,23 @@
namespace Entitas
{
public static class ContextExtension
{
/// Returns all entities matching the specified matcher.
public static TEntity[] GetEntities<TEntity>(this IContext<TEntity> context, IMatcher<TEntity> matcher) where TEntity : class, IEntity =>
context.GetGroup(matcher).GetEntities();
/// Creates a new entity and adds copies of all
/// specified components to it.
/// If replaceExisting is true it will replace exising components.
public static TEntity CloneEntity<TEntity>(this IContext<TEntity> context,
IEntity entity,
bool replaceExisting = false,
params int[] indices)
where TEntity : class, IEntity
{
var target = context.CreateEntity();
entity.CopyTo(target, replaceExisting, indices);
return target;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
namespace Entitas
{
public class ContextDoesNotContainEntityException : EntitasException
{
public ContextDoesNotContainEntityException(string message, string hint) : base(
$"{message}\nContext does not contain entity!", hint) { }
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace Entitas
{
public class ContextEntityIndexDoesAlreadyExistException : EntitasException
{
public ContextEntityIndexDoesAlreadyExistException(IContext context, string name) : base(
$"Cannot add EntityIndex '{name}' to context '{context}'!",
"An EntityIndex with this name has already been added.") { }
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace Entitas
{
public class ContextEntityIndexDoesNotExistException : EntitasException
{
public ContextEntityIndexDoesNotExistException(IContext context, string name) : base(
$"Cannot get EntityIndex '{name}' from context '{context}'!",
"No EntityIndex with this name has been added.") { }
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace Entitas
{
public class ContextInfoException : EntitasException
{
public ContextInfoException(IContext context, ContextInfo contextInfo) : base(
$"Invalid ContextInfo for '{context}'!\nExpected {context.totalComponents} componentName(s) but got {contextInfo.componentNames.Length}:",
string.Join("\n", contextInfo.componentNames)) { }
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
namespace Entitas
{
public class ContextStillHasRetainedEntitiesException : EntitasException
{
public ContextStillHasRetainedEntitiesException(IContext context, IEnumerable<IEntity> entities) : base(
$"'{context}' detected retained entities although all entities got destroyed!",
$"Did you release all entities? Try calling systems.DeactivateReactiveSystems() before calling context.DestroyAllEntities() to avoid memory leaks. Do not forget to activate them back when needed.\n{EntitiesToString(entities)}") { }
static string EntitiesToString(IEnumerable<IEntity> entities) => string.Join("\n",
entities.Select(e => e.aerc is SafeAERC safeAerc
? $"{e} - {string.Join(", ", safeAerc.owners.Select(o => o.ToString()))}"
: e.ToString())
);
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace Entitas
{
public class EntityIsNotDestroyedException : EntitasException
{
public EntityIsNotDestroyedException(string message) : base(
$"{message}\nEntity is not destroyed yet!",
"Did you manually call entity.Release(context) yourself? If so, please don\'t :)") { }
}
}

View File

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

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace Entitas
{
public delegate void ContextEntityChanged(IContext context, IEntity entity);
public delegate void ContextGroupChanged(IContext context, IGroup group);
public interface IContext
{
event ContextEntityChanged OnEntityCreated;
event ContextEntityChanged OnEntityWillBeDestroyed;
event ContextEntityChanged OnEntityDestroyed;
event ContextGroupChanged OnGroupCreated;
int totalComponents { get; }
Stack<IComponent>[] componentPools { get; }
ContextInfo contextInfo { get; }
int count { get; }
int reusableEntitiesCount { get; }
int retainedEntitiesCount { get; }
void DestroyAllEntities();
void AddEntityIndex(IEntityIndex entityIndex);
IEntityIndex GetEntityIndex(string name);
void ResetCreationIndex();
void ClearComponentPool(int index);
void ClearComponentPools();
void RemoveAllEventHandlers();
void Reset();
}
public interface IContext<TEntity> : IContext where TEntity : class, IEntity
{
TEntity CreateEntity();
bool HasEntity(TEntity entity);
TEntity[] GetEntities();
IGroup<TEntity> GetGroup(IMatcher<TEntity> matcher);
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace Entitas
{
public interface IContexts
{
IContext[] allContexts { get; }
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System;
namespace Entitas
{
public class ContextInfo
{
public readonly string name;
public readonly string[] componentNames;
public readonly Type[] componentTypes;
public ContextInfo(string name, string[] componentNames, Type[] componentTypes)
{
this.name = name;
this.componentNames = componentNames;
this.componentTypes = componentTypes;
}
}
}

Some files were not shown because too many files have changed in this diff Show More