parent
							
								
									6d59d7f23f
								
							
						
					
					
						commit
						3a1e97df6f
					
				
				 20 changed files with 653 additions and 2 deletions
			
			
		| @ -0,0 +1,7 @@ | ||||
| using GameCore.TinyECS; | ||||
| 
 | ||||
| namespace GameCore.Test | ||||
| { | ||||
|     using Entity = Entity<EntityType>; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 72e85a26219664e7f843536ec14a06bc | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,9 @@ | ||||
| namespace GameCore.Test | ||||
| { | ||||
|     public enum EntityType | ||||
|     { | ||||
|         Unit, | ||||
|         Monster, | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 8fbbe7882877348f8a41a85ea6281b10 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using Unity.Burst; | ||||
| using Unity.Collections; | ||||
| using Unity.Collections.LowLevel.Unsafe; | ||||
| using Unity.Jobs; | ||||
| using Unity.Mathematics; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace GameCore.Test | ||||
| { | ||||
|     [BurstCompile] | ||||
|     public struct TestJob : IJobParallelFor | ||||
|     { | ||||
|         public void Execute(int index) | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 68aafcef1265449e6b6b2dd81d7173a8 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,8 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 4ebb82ca8005849b48d82e7b2799da17 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,48 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace GameCore.TinyECS | ||||
| { | ||||
|     public readonly struct Entity<TEnum> : IEquatable<Entity<TEnum>> where TEnum : unmanaged | ||||
|     { | ||||
|         public readonly TEnum type; | ||||
|         public readonly int index; | ||||
|         public readonly int generation; | ||||
| 
 | ||||
|         public Entity(TEnum type, int index, int generation) | ||||
|         { | ||||
|             this.type = type; | ||||
|             this.index = index; | ||||
|             this.generation = generation; | ||||
|         } | ||||
| 
 | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return $"Entity<{type}>({index},{generation})"; | ||||
|         } | ||||
| 
 | ||||
|         public bool Equals(Entity<TEnum> other) | ||||
|         { | ||||
|             return type.Equals(other.type) && index == other.index && generation == other.generation; | ||||
|         } | ||||
| 
 | ||||
|         public override bool Equals(object obj) | ||||
|         { | ||||
|             return obj is Entity<TEnum> other && Equals(other); | ||||
|         } | ||||
| 
 | ||||
|         public override int GetHashCode() | ||||
|         { | ||||
|             return HashCode.Combine(type, index, generation); | ||||
|         } | ||||
| 
 | ||||
|         public static bool operator ==(Entity<TEnum> lhs, Entity<TEnum> rhs) | ||||
|         { | ||||
|             return lhs.Equals(rhs); | ||||
|         } | ||||
| 
 | ||||
|         public static bool operator !=(Entity<TEnum> lhs, Entity<TEnum> rhs) | ||||
|         { | ||||
|             return !(lhs == rhs); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: ad57411486945444c95e8a0aec2cf8b7 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,129 @@ | ||||
| using System; | ||||
| using Unity.Collections; | ||||
| using System.Diagnostics; | ||||
| 
 | ||||
| namespace GameCore.TinyECS | ||||
| { | ||||
|     public struct EntityCollection<TEnum> : IDisposable where TEnum : unmanaged | ||||
|     { | ||||
|         private struct Buffer | ||||
|         { | ||||
|             public int index; | ||||
|             public int generation; | ||||
|         } | ||||
| 
 | ||||
|         private NativeList<Buffer> entityBuffer; | ||||
|         private NativeList<int> entityIndexToDataIndex; | ||||
|         private TEnum entityType; | ||||
| 
 | ||||
|         public EntityCollection(TEnum type, int capacity, Allocator allocator) | ||||
|         { | ||||
|             entityType = type; | ||||
|             entityBuffer = new (capacity, allocator); | ||||
|             entityIndexToDataIndex = new (capacity, allocator); | ||||
|         } | ||||
| 
 | ||||
|         public void Dispose() | ||||
|         { | ||||
|             entityBuffer.Dispose(); | ||||
|             entityIndexToDataIndex.Dispose(); | ||||
|         } | ||||
| 
 | ||||
|         public int Count => entityBuffer.Length; | ||||
| 
 | ||||
|         public Entity<TEnum> this[int index] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 var buf = entityBuffer[index]; | ||||
|                 return new(entityType, buf.index, buf.generation); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public int Add(Entity<TEnum> entity) | ||||
|         { | ||||
|             CheckEntityType(entity); | ||||
| 
 | ||||
|             var dataIndex = entityBuffer.Length; | ||||
|             var oldEntityCount = entityIndexToDataIndex.Length; | ||||
|             if (entity.index >= oldEntityCount) | ||||
|             { | ||||
|                 entityIndexToDataIndex.Resize(entity.index + 1, NativeArrayOptions.UninitializedMemory); | ||||
|                 for (int i = oldEntityCount, e = entity.index - 1; i < e; ++i) | ||||
|                 { | ||||
|                     entityIndexToDataIndex[i] = -1; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             entityIndexToDataIndex[entity.index] = dataIndex; | ||||
|             entityBuffer.Add(new Buffer{index = entity.index, generation = entity.generation }); | ||||
|             return dataIndex; | ||||
|         } | ||||
| 
 | ||||
|         public bool TryGetIndex(Entity<TEnum> entity, out int index) | ||||
|         { | ||||
|             CheckEntityType(entity); | ||||
| 
 | ||||
|             index = 0; | ||||
|             if (entity.index >= entityIndexToDataIndex.Length) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             index = entityIndexToDataIndex[entity.index]; | ||||
|             if (index < 0 || index >= entityBuffer.Length) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             var buf = entityBuffer[index]; | ||||
|             return buf.index == entity.index && buf.generation == entity.generation; | ||||
|         } | ||||
| 
 | ||||
|         public bool Remove(Entity<TEnum> entity, out int index, out bool removeFromBack) | ||||
|         { | ||||
|             CheckEntityType(entity); | ||||
| 
 | ||||
|             if (!TryGetIndex(entity, out index)) | ||||
|             { | ||||
|                 removeFromBack = false; | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             removeFromBack = RemoveInternal(entity, index); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         private bool RemoveInternal(Entity<TEnum> entity, int index) | ||||
|         { | ||||
|             entityIndexToDataIndex[entity.index] = -1; | ||||
| 
 | ||||
|             var backIndex = entityBuffer.Length - 1; | ||||
|             if (index == backIndex) | ||||
|             { | ||||
|                 entityBuffer.Resize(backIndex, NativeArrayOptions.UninitializedMemory); | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             var backBuf = entityBuffer[backIndex]; | ||||
|             entityBuffer.RemoveAtSwapBack(index); | ||||
|             entityIndexToDataIndex[backBuf.index] = index; | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         [Conditional("DEBUG")] | ||||
|         void CheckEntityType(Entity<TEnum> entity) | ||||
|         { | ||||
|             if (!entity.type.Equals(entityType)) | ||||
|                 throw new Exception($"Wrong entity type: {entity.type}"); | ||||
|         } | ||||
| 
 | ||||
|         public void Clear() | ||||
|         { | ||||
|             entityBuffer.Clear(); | ||||
|             entityIndexToDataIndex.Clear(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: c788a2d204aeb46b188a801ae66ab4ce | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,79 @@ | ||||
| using System; | ||||
| using Unity.Collections; | ||||
| using Unity.Mathematics; | ||||
| 
 | ||||
| namespace GameCore.TinyECS | ||||
| { | ||||
|     public struct EntityGenerator<TEnum> : IDisposable where TEnum : unmanaged | ||||
|     { | ||||
|         private NativeList<int> indexToGeneration; | ||||
|         private int beginSearchIndex; | ||||
|         private int freeCount; | ||||
|         private TEnum entityType; | ||||
| 
 | ||||
|         public EntityGenerator(TEnum type, int capacity, Allocator allocator) | ||||
|         { | ||||
|             indexToGeneration = new(capacity, allocator) { 0 }; | ||||
|             beginSearchIndex = 1; | ||||
|             freeCount = 0; | ||||
|             entityType = type; | ||||
|         } | ||||
| 
 | ||||
|         public void Dispose() | ||||
|         { | ||||
|             indexToGeneration.Dispose(); | ||||
|         } | ||||
| 
 | ||||
|         public Entity<TEnum> Create() | ||||
|         { | ||||
|             if (freeCount == 0) | ||||
|             { | ||||
|                 var index = indexToGeneration.Length; | ||||
|                 indexToGeneration.Add(1); | ||||
|                 return new(entityType, index, 1); | ||||
|             } | ||||
| 
 | ||||
|             var count = indexToGeneration.Length; | ||||
|             for (var i = beginSearchIndex; i < count; ++i) | ||||
|             { | ||||
|                 var oldGeneration = indexToGeneration[i]; | ||||
|                 if (oldGeneration >= 0) | ||||
|                     continue; | ||||
|                 beginSearchIndex = i + 1; | ||||
|                 freeCount--; | ||||
|                 var generation = 1 - oldGeneration; | ||||
|                 indexToGeneration[i] = generation; | ||||
| 
 | ||||
|                 return new(entityType, i, generation); | ||||
|             } | ||||
| 
 | ||||
|             throw new Exception("Impossible"); | ||||
|         } | ||||
| 
 | ||||
|         public void Delete(Entity<TEnum> entity) | ||||
|         { | ||||
|             var generation = indexToGeneration[entity.index]; | ||||
|             if (generation != entity.generation) | ||||
|             { | ||||
|                 throw new Exception($"Invalid entity: {entity}"); | ||||
|             } | ||||
| 
 | ||||
|             indexToGeneration[entity.index] = -generation; | ||||
|             freeCount++; | ||||
|             beginSearchIndex = math.min(beginSearchIndex, entity.index); | ||||
|         } | ||||
| 
 | ||||
|         public bool IsValid(Entity<TEnum> entity) | ||||
|         { | ||||
|             var generation = indexToGeneration[entity.index]; | ||||
|             return generation == entity.generation; | ||||
|         } | ||||
| 
 | ||||
|         public void Clear() | ||||
|         { | ||||
|             indexToGeneration.Resize(1, NativeArrayOptions.UninitializedMemory); | ||||
|             beginSearchIndex = 1; | ||||
|             freeCount = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: d1bf40e20fa3e4ea69d9a9896a297fe1 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,33 @@ | ||||
| { | ||||
|     "name": "TinyECS.Runtime", | ||||
|     "rootNamespace": "", | ||||
|     "references": [ | ||||
|         "Unity.Collections", | ||||
|         "Unity.Mathematics" | ||||
|     ], | ||||
|     "includePlatforms": [ | ||||
|         "Android", | ||||
|         "Editor", | ||||
|         "iOS", | ||||
|         "LinuxStandalone64", | ||||
|         "Lumin", | ||||
|         "macOSStandalone", | ||||
|         "PS4", | ||||
|         "Stadia", | ||||
|         "Switch", | ||||
|         "tvOS", | ||||
|         "WSA", | ||||
|         "WebGL", | ||||
|         "WindowsStandalone32", | ||||
|         "WindowsStandalone64", | ||||
|         "XboxOne" | ||||
|     ], | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": false, | ||||
|     "overrideReferences": false, | ||||
|     "precompiledReferences": [], | ||||
|     "autoReferenced": true, | ||||
|     "defineConstraints": [], | ||||
|     "versionDefines": [], | ||||
|     "noEngineReferences": false | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: c2bd585c8e44341079d076e23cd9d3eb | ||||
| AssemblyDefinitionImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @ -0,0 +1,14 @@ | ||||
| { | ||||
|     "name": "com.nimin.tinyecs", | ||||
|     "displayName": "Tiny ECS", | ||||
|     "version": "0.1.0", | ||||
|     "unity": "2021.3", | ||||
|     "description": "Data oriented ECS famework", | ||||
|     "dependencies": { | ||||
|       "com.unity.collections": "1.2.4", | ||||
|       "com.unity.burst": "1.8.1" | ||||
|     }, | ||||
|     "keywords": [ | ||||
|       "ECS", "JobSystem" | ||||
|     ] | ||||
|   } | ||||
| @ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: afdf6be686c684f4c8190a688038f1ca | ||||
| PackageManifestImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
					Loading…
					
					
				
		Reference in new issue