diff --git a/Assets/Scripts/EntityData.cs b/Assets/Scripts/EntityData.cs new file mode 100644 index 0000000..da5f092 --- /dev/null +++ b/Assets/Scripts/EntityData.cs @@ -0,0 +1,7 @@ +using GameCore.TinyECS; + +namespace GameCore.Test +{ + using Entity = Entity; + +} \ No newline at end of file diff --git a/Assets/Scripts/EntityData.cs.meta b/Assets/Scripts/EntityData.cs.meta new file mode 100644 index 0000000..a3a6873 --- /dev/null +++ b/Assets/Scripts/EntityData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72e85a26219664e7f843536ec14a06bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/EntityType.cs b/Assets/Scripts/EntityType.cs new file mode 100644 index 0000000..0b35401 --- /dev/null +++ b/Assets/Scripts/EntityType.cs @@ -0,0 +1,9 @@ +namespace GameCore.Test +{ + public enum EntityType + { + Unit, + Monster, + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EntityType.cs.meta b/Assets/Scripts/EntityType.cs.meta new file mode 100644 index 0000000..8ce5980 --- /dev/null +++ b/Assets/Scripts/EntityType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fbbe7882877348f8a41a85ea6281b10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/TestJob.cs b/Assets/Scripts/TestJob.cs new file mode 100644 index 0000000..dfcca1c --- /dev/null +++ b/Assets/Scripts/TestJob.cs @@ -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) + { + + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/TestJob.cs.meta b/Assets/Scripts/TestJob.cs.meta new file mode 100644 index 0000000..e492340 --- /dev/null +++ b/Assets/Scripts/TestJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68aafcef1265449e6b6b2dd81d7173a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/Runtime.meta b/LocalPackages/com.nimin.tinyecs/Runtime.meta new file mode 100644 index 0000000..7b6c7fa --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4ebb82ca8005849b48d82e7b2799da17 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs b/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs new file mode 100644 index 0000000..4fa0fe5 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs @@ -0,0 +1,48 @@ +using System; + +namespace GameCore.TinyECS +{ + public readonly struct Entity : IEquatable> 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 other) + { + return type.Equals(other.type) && index == other.index && generation == other.generation; + } + + public override bool Equals(object obj) + { + return obj is Entity other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(type, index, generation); + } + + public static bool operator ==(Entity lhs, Entity rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(Entity lhs, Entity rhs) + { + return !(lhs == rhs); + } + } +} \ No newline at end of file diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs.meta b/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs.meta new file mode 100644 index 0000000..0c43cc8 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/Entity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad57411486945444c95e8a0aec2cf8b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs b/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs new file mode 100644 index 0000000..fce1930 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs @@ -0,0 +1,129 @@ +using System; +using Unity.Collections; +using System.Diagnostics; + +namespace GameCore.TinyECS +{ + public struct EntityCollection : IDisposable where TEnum : unmanaged + { + private struct Buffer + { + public int index; + public int generation; + } + + private NativeList entityBuffer; + private NativeList 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 this[int index] + { + get + { + var buf = entityBuffer[index]; + return new(entityType, buf.index, buf.generation); + } + } + + public int Add(Entity 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 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 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 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 entity) + { + if (!entity.type.Equals(entityType)) + throw new Exception($"Wrong entity type: {entity.type}"); + } + + public void Clear() + { + entityBuffer.Clear(); + entityIndexToDataIndex.Clear(); + } + + } +} diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs.meta b/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs.meta new file mode 100644 index 0000000..9d76902 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c788a2d204aeb46b188a801ae66ab4ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs b/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs new file mode 100644 index 0000000..a86d058 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs @@ -0,0 +1,79 @@ +using System; +using Unity.Collections; +using Unity.Mathematics; + +namespace GameCore.TinyECS +{ + public struct EntityGenerator : IDisposable where TEnum : unmanaged + { + private NativeList 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 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 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 entity) + { + var generation = indexToGeneration[entity.index]; + return generation == entity.generation; + } + + public void Clear() + { + indexToGeneration.Resize(1, NativeArrayOptions.UninitializedMemory); + beginSearchIndex = 1; + freeCount = 0; + } + } +} diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs.meta b/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs.meta new file mode 100644 index 0000000..8360b4a --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1bf40e20fa3e4ea69d9a9896a297fe1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef b/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef new file mode 100644 index 0000000..707d11e --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef @@ -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 +} \ No newline at end of file diff --git a/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef.meta b/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef.meta new file mode 100644 index 0000000..168c675 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/Runtime/TinyECS.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c2bd585c8e44341079d076e23cd9d3eb +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LocalPackages/com.nimin.tinyecs/package.json b/LocalPackages/com.nimin.tinyecs/package.json new file mode 100644 index 0000000..c12a72a --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/package.json @@ -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" + ] + } diff --git a/LocalPackages/com.nimin.tinyecs/package.json.meta b/LocalPackages/com.nimin.tinyecs/package.json.meta new file mode 100644 index 0000000..47d3fe9 --- /dev/null +++ b/LocalPackages/com.nimin.tinyecs/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: afdf6be686c684f4c8190a688038f1ca +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json index c1b21e4..78fc72d 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -40,6 +40,7 @@ "com.unity.modules.video": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", - "com.unity.modules.xr": "1.0.0" + "com.unity.modules.xr": "1.0.0", + "com.nimin.tinyecs": "file:../LocalPackages/com.nimin.tinyecs" } } diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 121178e..ebd005f 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,5 +1,23 @@ { "dependencies": { + "com.nimin.tinyecs": { + "version": "file:../LocalPackages/com.nimin.tinyecs", + "depth": 0, + "source": "local", + "dependencies": { + "com.unity.collections": "1.2.4", + "com.unity.burst": "1.8.1" + } + }, + "com.unity.burst": { + "version": "1.8.1", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1" + }, + "url": "https://packages.unity.cn" + }, "com.unity.collab-proxy": { "version": "1.17.1", "depth": 0, @@ -9,6 +27,16 @@ }, "url": "https://packages.unity.cn" }, + "com.unity.collections": { + "version": "1.2.4", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.6.6", + "com.unity.test-framework": "1.1.31" + }, + "url": "https://packages.unity.cn" + }, "com.unity.editorcoroutines": { "version": "1.0.0", "depth": 1, @@ -62,6 +90,13 @@ "dependencies": {}, "url": "https://packages.unity.cn" }, + "com.unity.mathematics": { + "version": "1.2.6", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, "com.unity.nuget.newtonsoft-json": { "version": "3.0.2", "depth": 2, diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index f978d0d..aff4620 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -280,7 +280,196 @@ PlayerSettings: AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 m_BuildTargetIcons: [] - m_BuildTargetPlatformIcons: [] + m_BuildTargetPlatformIcons: + - m_BuildTarget: iPhone + m_Icons: + - m_Textures: [] + m_Width: 180 + m_Height: 180 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 87 + m_Height: 87 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 60 + m_Height: 60 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 20 + m_Height: 20 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 1024 + m_Height: 1024 + m_Kind: 4 + m_SubKind: App Store + - m_BuildTarget: Android + m_Icons: + - m_Textures: [] + m_Width: 432 + m_Height: 432 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 324 + m_Height: 324 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 216 + m_Height: 216 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 162 + m_Height: 162 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 108 + m_Height: 108 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 81 + m_Height: 81 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 0 + m_SubKind: m_BuildTargetBatching: - m_BuildTarget: Standalone m_StaticBatching: 1