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