整理EntityDatabase代码
This commit is contained in:
1 parent
6c8108e3ea
commit
d9b66edd89
18 files changed
+220
-57
No files matched your search
@@ -0,0 +1,19 @@
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace ECSTest.Server
|
||||
{
|
||||
public struct CannonData
|
||||
{
|
||||
public float2 position;
|
||||
public float2 velocity;
|
||||
public float direction;
|
||||
public int cooldownFrameId;
|
||||
public int health;
|
||||
public int deathFrameId;
|
||||
}
|
||||
|
||||
public struct BulletData
|
||||
{
|
||||
public float2 position;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e44ed253ab647b3910794e124e6402e
|
||||
timeCreated: 1766030729
|
||||
@@ -1,13 +1,64 @@
|
||||
using GameCore.TinyECS;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Random = Unity.Mathematics.Random;
|
||||
|
||||
namespace ECSTest.Server
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
public struct TestWorldJob : IJob
|
||||
{
|
||||
public World world;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
// var componentId = ComponentId<BulletData>.Get();
|
||||
// Debug.LogError($"XXXX bulletData id: {componentId}");
|
||||
// var componentId2 = ComponentId<CannonData>.Get();
|
||||
// Debug.LogError($"xxxx2222 CannonData id: {componentId2}");
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct SpawnCannonJob : IJob
|
||||
{
|
||||
public World world;
|
||||
public NativeList<bool> deferList;
|
||||
|
||||
public uint seed;
|
||||
public int maxCount;
|
||||
public int spawnCountPerFrame;
|
||||
public Rect rect;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
ref var database = ref world.cannonDatabase;
|
||||
if (database.Count >= maxCount)
|
||||
return;
|
||||
|
||||
var random = new Random(seed);
|
||||
for (int i = 0; i < spawnCountPerFrame; i++)
|
||||
{
|
||||
var pos = random.NextFloat2(rect.min, rect.max);
|
||||
var dir = random.NextFloat(math.PI2);
|
||||
math.sincos(dir, out var s, out var c);
|
||||
var velocity = new float2(c, s);
|
||||
|
||||
var entity = database.AddEntity();
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[BurstCompile]
|
||||
public struct SpawnBulletJob : IJob
|
||||
{
|
||||
public WorldManager worldManager;
|
||||
public World World;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using GameCore.Network;
|
||||
using GameCore.TinyECS;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Networking.Transport;
|
||||
@@ -21,26 +22,47 @@ namespace ECSTest.Server
|
||||
private NativeList<NetworkConnection> connectionList;
|
||||
private NativeList<int> disconnectIndexList;
|
||||
|
||||
private World world;
|
||||
private JobHandle gameLogicJobHandle;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
driver = driverAsset.CreateServerDriver(address, out unreliablePipeline, out reliablePipeline);
|
||||
|
||||
connectionList = new NativeList<NetworkConnection>(16, Allocator.Persistent);
|
||||
disconnectIndexList = new NativeList<int>(16, Allocator.Persistent);
|
||||
|
||||
var cannonMeta = EntityDataBlockMeta.Create<CannonData>();
|
||||
world.cannonDatabase =
|
||||
new EntityDatabase<EntityType, EntityDataBlockChain>(EntityType.Cannon,
|
||||
new EntityDataBlockChain(cannonMeta));
|
||||
|
||||
var bulletMeta = EntityDataBlockMeta.Create<BulletData>();
|
||||
world.bulletDatabase =
|
||||
new EntityDatabase<EntityType, EntityDataBlockChain>(EntityType.Bullet,
|
||||
new EntityDataBlockChain(bulletMeta));
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
networkJobHandle.Complete();
|
||||
gameLogicJobHandle.Complete();
|
||||
|
||||
driver.Dispose();
|
||||
|
||||
connectionList.Dispose();
|
||||
disconnectIndexList.Dispose();
|
||||
|
||||
world.Dispose();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
var componentId = ComponentId<BulletData>.Get();
|
||||
Debug.LogError($"AAAA bulletData id: {componentId}");
|
||||
var componentId2 = ComponentId<CannonData>.Get();
|
||||
Debug.LogError($"AAAA2222 CannonData id: {componentId2}");
|
||||
|
||||
networkJobHandle.Complete();
|
||||
|
||||
var concurrentDriver = driver.ToConcurrent();
|
||||
@@ -60,6 +82,15 @@ namespace ECSTest.Server
|
||||
disconnectIndexList = disconnectIndexList.AsParallelWriter(),
|
||||
}.Schedule(connectionList, 16, networkJobHandle);
|
||||
|
||||
if (gameLogicJobHandle.IsCompleted)
|
||||
{
|
||||
gameLogicJobHandle.Complete();
|
||||
gameLogicJobHandle = new TestWorldJob()
|
||||
{
|
||||
world = world,
|
||||
|
||||
}.Schedule(networkJobHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using GameCore.TinyECS;
|
||||
|
||||
namespace ECSTest.Server
|
||||
{
|
||||
public struct World : IDisposable
|
||||
{
|
||||
public EntityDatabase<EntityType, EntityDataBlockChain> cannonDatabase;
|
||||
public EntityDatabase<EntityType, EntityDataBlockChain> bulletDatabase;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
cannonDatabase.Dispose();
|
||||
bulletDatabase.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -1,9 +0,0 @@
|
||||
using GameCore.TinyECS;
|
||||
|
||||
namespace ECSTest.Server
|
||||
{
|
||||
public struct WorldManager
|
||||
{
|
||||
public EntityDatabase<EntityType, EntityDataBlockChain> bulletDatabase;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace ECSTest
|
||||
using var blockChain = new EntityDataBlockChain(meta, 1000);
|
||||
|
||||
blockChain.Add();
|
||||
ref var stateData = ref blockChain.GetData<StateData>(0);
|
||||
ref var stateData = ref blockChain.GetDataRef<StateData>(0);
|
||||
stateData.hp = 1000;
|
||||
|
||||
var view = blockChain.AsBlockView(0);
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public struct ComponentId
|
||||
internal static class ComponentIdHelper
|
||||
{
|
||||
struct Inner<T> where T : unmanaged
|
||||
public static int currentId;
|
||||
}
|
||||
|
||||
public struct ComponentId<T>
|
||||
{
|
||||
public int id;
|
||||
private Inner(int i) => id = i;
|
||||
public static Inner<T> Instance = default;
|
||||
public static readonly SharedStatic<ComponentId<T>> Instance = SharedStatic<ComponentId<T>>.GetOrCreate<ComponentId<T>>();
|
||||
|
||||
[BurstDiscard]
|
||||
static void EnsureInitialized()
|
||||
{
|
||||
if (Instance.Data.id == 0)
|
||||
{
|
||||
Instance.Data.id = ++ComponentIdHelper.currentId;
|
||||
}
|
||||
}
|
||||
|
||||
static int currentId;
|
||||
|
||||
public static int Get<T>() where T : unmanaged
|
||||
public static int Get()
|
||||
{
|
||||
if (Inner<T>.Instance.id == 0)
|
||||
{
|
||||
Inner<T>.Instance.id = ++currentId;
|
||||
}
|
||||
return Inner<T>.Instance.id;
|
||||
EnsureInitialized();
|
||||
return Instance.Data.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using GameCore.LowLevel;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public readonly struct Entity<TEnum> : IEquatable<Entity<TEnum>> where TEnum : unmanaged
|
||||
public readonly struct Entity<TEnum> : IEquatable<Entity<TEnum>> where TEnum : unmanaged, IConvertible
|
||||
{
|
||||
public readonly TEnum type;
|
||||
public readonly int index;
|
||||
@@ -19,7 +20,7 @@ namespace GameCore.TinyECS
|
||||
public override string ToString() => $"Entity<{type}>({index},{generation})";
|
||||
public bool Equals(Entity<TEnum> other) => type.Equals(other.type) && index == other.index && generation == other.generation;
|
||||
public override bool Equals(object obj) => obj is Entity<TEnum> other && Equals(other);
|
||||
public override int GetHashCode() => LowLevelUtility.HashCombine(type.GetHashCode(), index, generation);
|
||||
public override int GetHashCode() => LowLevelUtility.HashCombine(UnsafeUtility.EnumToInt(type), index, generation);
|
||||
public static bool operator ==(Entity<TEnum> lhs, Entity<TEnum> rhs) => lhs.Equals(rhs);
|
||||
public static bool operator !=(Entity<TEnum> lhs, Entity<TEnum> rhs) => !(lhs == rhs);
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using System.Diagnostics;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public struct EntityCollection<TEnum> : IDisposable where TEnum : unmanaged
|
||||
public struct EntityCollection<TEnum> : IDisposable
|
||||
where TEnum : unmanaged, IConvertible
|
||||
{
|
||||
private struct Buffer
|
||||
{
|
||||
@@ -60,6 +62,25 @@ namespace GameCore.TinyECS
|
||||
return dataIndex;
|
||||
}
|
||||
|
||||
public int GetIndex(Entity<TEnum> entity)
|
||||
{
|
||||
CheckEntityType(entity);
|
||||
|
||||
if (entity.index >= entityIndexToDataIndex.Length)
|
||||
{
|
||||
throw new Exception($"Invalid entity: {entity}");
|
||||
}
|
||||
|
||||
var index = entityIndexToDataIndex[entity.index];
|
||||
var buf = entityBuffer[index];
|
||||
if (buf.index != entity.index || buf.generation != entity.generation)
|
||||
{
|
||||
throw new Exception($"Invalid entity: {entity}");
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public bool TryGetIndex(Entity<TEnum> entity, out int index)
|
||||
{
|
||||
CheckEntityType(entity);
|
||||
@@ -115,7 +136,7 @@ namespace GameCore.TinyECS
|
||||
[Conditional("DEBUG")]
|
||||
void CheckEntityType(Entity<TEnum> entity)
|
||||
{
|
||||
if (!entity.type.Equals(entityType))
|
||||
if (!UnsafeUtility.EnumEquals(entityType, entity.type))
|
||||
throw new Exception($"Wrong entity type: {entity.type}");
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace GameCore.TinyECS
|
||||
public bool IsFull => elementCount == MaxElementCount;
|
||||
public bool IsEmpty => elementCount == 0;
|
||||
|
||||
public EntityDataBlockChain(EntityDataBlockMeta blockMeta, int maxElementCountPerBlock)
|
||||
public EntityDataBlockChain(EntityDataBlockMeta blockMeta, int maxElementCountPerBlock = 1024)
|
||||
{
|
||||
this.blockMeta = blockMeta;
|
||||
blockPtrList = default;
|
||||
@@ -72,7 +72,7 @@ namespace GameCore.TinyECS
|
||||
|
||||
void* ComponentPtrAt<T>(int elementIndex) where T : unmanaged
|
||||
{
|
||||
var componentId = ComponentId.Get<T>();
|
||||
var componentId = ComponentId<T>.Get();
|
||||
var componentIndex = blockMeta.componentIdToIndex[componentId];
|
||||
if (componentIndex > 0)
|
||||
{
|
||||
@@ -133,7 +133,16 @@ namespace GameCore.TinyECS
|
||||
elementCount--;
|
||||
}
|
||||
|
||||
public ref T GetData<T>(int index) where T : unmanaged
|
||||
public T GetData<T>(int index) where T : unmanaged
|
||||
{
|
||||
CheckElementIndex(index);
|
||||
var pComponent = ComponentPtrAt<T>(index);
|
||||
if (pComponent == null)
|
||||
throw new Exception($"Invalid component type: {typeof(T)}");
|
||||
return UnsafeUtility.AsRef<T>(pComponent);
|
||||
}
|
||||
|
||||
public ref T GetDataRef<T>(int index) where T : unmanaged
|
||||
{
|
||||
CheckElementIndex(index);
|
||||
var pComponent = ComponentPtrAt<T>(index);
|
||||
@@ -150,7 +159,7 @@ namespace GameCore.TinyECS
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe readonly struct EntityDataBlockView
|
||||
public readonly unsafe struct EntityDataBlockView
|
||||
{
|
||||
private readonly void* ptr;
|
||||
public readonly EntityDataBlockMeta meta;
|
||||
@@ -172,7 +181,7 @@ namespace GameCore.TinyECS
|
||||
|
||||
void* ComponentPtrAt<T>(int elementIndex) where T : unmanaged
|
||||
{
|
||||
var componentId = ComponentId.Get<T>();
|
||||
var componentId = ComponentId<T>.Get();
|
||||
var componentIndex = meta.componentIdToIndex[componentId];
|
||||
if (componentIndex > 0)
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace GameCore.TinyECS
|
||||
meta.componentIdToIndex.Length = meta.componentIdToIndex.Capacity;
|
||||
meta.componentIdToOffset.Length = meta.componentIdToOffset.Capacity;
|
||||
|
||||
var id1 = ComponentId.Get<T1>();
|
||||
var id1 = ComponentId<T1>.Get();
|
||||
meta.componentIdToIndex[id1] = 1;
|
||||
meta.componentIdToOffset[id1] = 0;
|
||||
meta.elementSize = UnsafeUtility.SizeOf<T1>();
|
||||
@@ -31,8 +31,8 @@ namespace GameCore.TinyECS
|
||||
meta.componentIdToIndex.Length = meta.componentIdToIndex.Capacity;
|
||||
meta.componentIdToOffset.Length = meta.componentIdToOffset.Capacity;
|
||||
|
||||
var id1 = ComponentId.Get<T1>();
|
||||
var id2 = ComponentId.Get<T2>();
|
||||
var id1 = ComponentId<T1>.Get();
|
||||
var id2 = ComponentId<T2>.Get();
|
||||
meta.componentIdToIndex[id1] = 1;
|
||||
meta.componentIdToIndex[id2] = 2;
|
||||
meta.componentIdToOffset[id1] = (short)LowLevelUtility.OffsetOf<(T1, T2), T1>();
|
||||
|
||||
@@ -6,5 +6,8 @@ namespace GameCore.TinyECS
|
||||
{
|
||||
void Add();
|
||||
void Remove(int index);
|
||||
|
||||
T GetData<T>(int index) where T : unmanaged;
|
||||
ref T GetDataRef<T>(int index) where T : unmanaged;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,23 @@ using Unity.Mathematics;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public struct EntityDatabase<TEnum, TData> : IDisposable where TEnum : unmanaged where TData : unmanaged, IEntityDataStruct
|
||||
public struct EntityDatabase<TEnum, TData> : IDisposable
|
||||
where TEnum : unmanaged, IConvertible
|
||||
where TData : unmanaged, IEntityDataStruct
|
||||
{
|
||||
public TData dataStruct;
|
||||
public EntityGenerator<TEnum> generator;
|
||||
public EntityCollection<TEnum> collection;
|
||||
|
||||
public int Count => collection.Count;
|
||||
|
||||
public EntityDatabase(TEnum type, TData dataStruct, int capacity = 16)
|
||||
{
|
||||
generator = new (type, capacity, Allocator.Persistent);
|
||||
collection = new (type, capacity, Allocator.Persistent);
|
||||
this.dataStruct = dataStruct;
|
||||
}
|
||||
|
||||
public Entity<TEnum> AddEntity()
|
||||
{
|
||||
var entity = generator.Create();
|
||||
@@ -25,6 +36,18 @@ namespace GameCore.TinyECS
|
||||
dataStruct.Remove(index);
|
||||
}
|
||||
|
||||
public T GetData<T>(Entity<TEnum> entity) where T : unmanaged
|
||||
{
|
||||
var index = collection.GetIndex(entity);
|
||||
return dataStruct.GetData<T>(index);
|
||||
}
|
||||
|
||||
public ref T GetDataRef<T>(Entity<TEnum> entity) where T : unmanaged
|
||||
{
|
||||
var index = collection.GetIndex(entity);
|
||||
return ref dataStruct.GetDataRef<T>(index);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
generator.Dispose();
|
||||
|
||||
@@ -4,7 +4,8 @@ using Unity.Mathematics;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public struct EntityGenerator<TEnum> : IDisposable where TEnum : unmanaged
|
||||
public struct EntityGenerator<TEnum> : IDisposable
|
||||
where TEnum : unmanaged, IConvertible
|
||||
{
|
||||
private NativeList<int> indexToGeneration;
|
||||
private int beginSearchIndex;
|
||||
|
||||
@@ -4,25 +4,10 @@
|
||||
"references": [
|
||||
"Unity.Collections",
|
||||
"Unity.Mathematics",
|
||||
"nimin.lowlevel"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Android",
|
||||
"Editor",
|
||||
"iOS",
|
||||
"LinuxStandalone64",
|
||||
"Lumin",
|
||||
"macOSStandalone",
|
||||
"PS4",
|
||||
"Stadia",
|
||||
"Switch",
|
||||
"tvOS",
|
||||
"WSA",
|
||||
"WebGL",
|
||||
"WindowsStandalone32",
|
||||
"WindowsStandalone64",
|
||||
"XboxOne"
|
||||
"nimin.lowlevel",
|
||||
"Unity.Burst"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystem_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3ea01e77be99618395f448b16bd7cc2bd4248bd9e5d8474964b2d97f39c5d74_003FSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAllocator_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003Fa6_003F9ff064e4_003FAllocator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJobsUtility_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003F4c_003Fe16da97b_003FJobsUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystem_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3ea01e77be99618395f448b16bd7cc2bd4248bd9e5d8474964b2d97f39c5d74_003FSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnsafeUtility_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003F8b_003F35bd5f7e_003FUnsafeUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
Reference in new issue
Block a user