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