ecs framework
This commit is contained in:
1 parent
6d59d7f23f
commit
3a1e97df6f
20 files changed
+653
-2
No files matched your search
@@ -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:
|
||||
Reference in new issue
Block a user