-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathComponentSet.cs
167 lines (134 loc) · 6.05 KB
/
ComponentSet.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace MelonECS
{
public interface IComponentSet
{
bool Has(in Entity entity);
void Remove(in Entity entity);
bool TryRemove(in Entity entity);
void EndOfFrameCleanup();
void FlushAddsAndRemoves();
IComponent GetGeneric(in Entity entity);
int Count { get; }
}
public class ComponentSet<TComponent> : IComponentSet where TComponent : struct, IComponent
{
private const int INVALID_INDEX = -1;
public int Count { get; private set; }
private int[] indices;
private Entity[] entities;
private TComponent[] components;
private Entity[] changed;
private int changedCount;
// private readonly List<(Entity, TComponent)> addedComponents = new List<(Entity, TComponent)>();
// private readonly List<Entity> removedComponents = new List<Entity>();
private readonly StructList<Entity> addedEntities = new StructList<Entity>(4);
private readonly StructList<TComponent> addedComponents = new StructList<TComponent>(4);
private readonly StructList<Entity> removedEntities = new StructList<Entity>(4);
private readonly StructList<TComponent> removedComponents = new StructList<TComponent>(4);
private bool isDirty = false;
public ComponentSet(int entityCapacity, int componentCapacity)
{
indices = new int[entityCapacity];
for (int i = 0; i < entityCapacity; i++)
indices[i] = INVALID_INDEX;
entities = new Entity[componentCapacity];
components = new TComponent[componentCapacity];
changed = new Entity[componentCapacity];
}
public void Add(Entity entity, TComponent component)
{
if (entity.Index >= indices.Length)
{
// If somehow an Entity's index is extremely large allocate just enough space, otherwise double what we have
var temp = new int[Math.Max(entity.Index + 1, indices.Length * 2)];
Array.Copy(indices, temp, indices.Length);
for (int i = Count; i < temp.Length; i++)
temp[i] = INVALID_INDEX;
indices = temp;
}
if (Has(entity))
{
throw new Exception($"Failed to add component. {entity} already has component {typeof(TComponent).Name}");
}
addedEntities.Add(entity);
addedComponents.Add(component);
isDirty = true;
}
public void Remove(in Entity entity)
{
if (!Has(entity))
{
throw new Exception($"Failed to remove component. {entity} does not have component {typeof(TComponent).Name}");
}
removedEntities.Add(entity);
removedComponents.Add(Get(entity));
isDirty = true;
}
public bool TryRemove(in Entity entity)
{
if (!Has(entity))
return false;
Remove(entity);
return true;
}
public void NotifyChange(in Entity entity)
{
ArrayUtil.EnsureLength(ref changed, changedCount + 1);
changed[changedCount] = entity;
changedCount++;
}
public void EndOfFrameCleanup()
{
changedCount = 0;
addedEntities.Clear();
addedComponents.Clear();
removedEntities.Clear();
removedComponents.Clear();
}
public void FlushAddsAndRemoves()
{
if (!isDirty)
return;
// Process adds and removes
foreach (ref Entity entity in removedEntities.ArrayRef)
{
if (indices[entity.Index] == INVALID_INDEX)
continue;
// Swap with end of list rather than shifting everything
int newIndex = indices[entity.Index];
indices[entities[Count - 1].Index] = newIndex;
entities[newIndex] = entities[Count - 1];
components[newIndex] = components[Count - 1];
indices[entity.Index] = INVALID_INDEX;
Count--;
}
ArrayUtil.EnsureLength(ref entities, Count + addedEntities.Count + 1);
ArrayUtil.EnsureLength(ref components, Count + addedComponents.Count + 1);
for (int i = 0; i < addedEntities.ArrayRef.Length; i++)
{
Entity entity = addedEntities.ArrayRef[i];
if (indices[entity.Index] != INVALID_INDEX)
continue;
indices[entity.Index] = Count;
entities[Count] = entity;
components[Count] = addedComponents.ArrayRef[i];
Count++;
}
}
public ArrayRef<Entity> AllEntities() => new ArrayRef<Entity>(entities, 0, Count);
public ArrayRef<TComponent> AllComponents() => new ArrayRef<TComponent>(components, 0, Count);
public ArrayRef<Entity> AllChanged() => new ArrayRef<Entity>(changed, 0, changedCount);
public ArrayRef<Entity> AllAddedEntities() => addedEntities.ArrayRef;
public ref TComponent GetAddedComponent(in Entity entity) => ref addedComponents.ArrayRef[addedEntities.IndexOf(entity)];
public ArrayRef<Entity> AllRemovedEntities() => removedEntities.ArrayRef;
public ref TComponent GetRemovedComponent(in Entity entity) => ref removedComponents.ArrayRef[removedEntities.IndexOf(entity)];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(in Entity entity) => indices[entity.Index] != INVALID_INDEX;
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TComponent Get(in Entity entity) => ref components[indices[entity.Index]];
public IComponent GetGeneric(in Entity entity) => components[indices[entity.Index]];
}
}