com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
- package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
- package/Docs/INDEX.md +11 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +17 -14
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
- package/Docs/RELATIONAL_COMPONENTS.md +6 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
- package/Editor/Sprites/AnimationCopier.cs +1 -1
- package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
- package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
- package/Editor/Sprites/TextureResizerWizard.cs +4 -3
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
- package/README.md +33 -18
- package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
- package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
- package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
- package/Runtime/Core/CodeGen.meta +8 -0
- package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
- package/Runtime/Core/Extension/IListExtensions.cs +720 -12
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
- package/Runtime/Core/Helper/Objects.cs +1 -1
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
- package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
- package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
- package/Runtime/Core/Random/IllusionFlow.cs +3 -3
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
- package/Runtime/Core/Random/PcgRandom.cs +6 -6
- package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
- package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
- package/Runtime/Core/Random/RomuDuo.cs +3 -3
- package/Runtime/Core/Random/SplitMix64.cs +3 -3
- package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
- package/Runtime/Core/Random/StormDropRandom.cs +271 -0
- package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
- package/Runtime/Core/Random/UnityRandom.cs +3 -3
- package/Runtime/Core/Random/WyRandom.cs +6 -4
- package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
- package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
- package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
- package/Runtime/Tags/CosmeticEffectData.cs +1 -1
- package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
- package/Tests/Editor/Helper/HelpersTests.cs +2 -2
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
- package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
- package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
- package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/Tests/Editor/Tags.meta +8 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
- package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
- package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
- package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
- package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
- package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
- package/package.json +1 -1
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
|
@@ -3,11 +3,17 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
3
3
|
using System;
|
|
4
4
|
using System.Collections;
|
|
5
5
|
using System.Collections.Generic;
|
|
6
|
+
using System.Linq;
|
|
7
|
+
using System.Linq.Expressions;
|
|
8
|
+
using System.Reflection;
|
|
6
9
|
using Extension;
|
|
7
10
|
using UnityEngine;
|
|
8
11
|
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
9
12
|
using WallstopStudios.UnityHelpers.Utils;
|
|
10
13
|
using static RelationalComponentProcessor;
|
|
14
|
+
#if UNITY_EDITOR && UNITY_2020_2_OR_NEWER
|
|
15
|
+
using Unity.Profiling;
|
|
16
|
+
#endif
|
|
11
17
|
|
|
12
18
|
/// <summary>
|
|
13
19
|
/// Automatically assigns child components (components down the transform hierarchy) to the decorated field.
|
|
@@ -82,6 +88,15 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
82
88
|
FieldMetadata<ChildComponentAttribute>[]
|
|
83
89
|
> FieldsByType = new();
|
|
84
90
|
|
|
91
|
+
#if UNITY_EDITOR && UNITY_2020_2_OR_NEWER
|
|
92
|
+
private static readonly ProfilerMarker ChildFastPathMarker = new ProfilerMarker(
|
|
93
|
+
"RelationalComponents.Child.FastPath"
|
|
94
|
+
);
|
|
95
|
+
private static readonly ProfilerMarker ChildFallbackMarker = new ProfilerMarker(
|
|
96
|
+
"RelationalComponents.Child.Fallback"
|
|
97
|
+
);
|
|
98
|
+
#endif
|
|
99
|
+
|
|
85
100
|
/// <summary>
|
|
86
101
|
/// Assigns fields on <paramref name="component"/> marked with <see cref="ChildComponentAttribute"/>.
|
|
87
102
|
/// </summary>
|
|
@@ -100,11 +115,22 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
100
115
|
/// </example>
|
|
101
116
|
public static void AssignChildComponents(this Component component)
|
|
102
117
|
{
|
|
103
|
-
Type componentType = component.GetType();
|
|
104
118
|
FieldMetadata<ChildComponentAttribute>[] fields = FieldsByType.GetOrAdd(
|
|
105
|
-
|
|
119
|
+
component.GetType(),
|
|
106
120
|
type => GetFieldMetadata<ChildComponentAttribute>(type)
|
|
107
121
|
);
|
|
122
|
+
AssignChildComponents(component, fields);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
internal static void AssignChildComponents(
|
|
126
|
+
Component component,
|
|
127
|
+
FieldMetadata<ChildComponentAttribute>[] fields
|
|
128
|
+
)
|
|
129
|
+
{
|
|
130
|
+
if (component == null || fields == null || fields.Length == 0)
|
|
131
|
+
{
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
108
134
|
|
|
109
135
|
foreach (FieldMetadata<ChildComponentAttribute> metadata in fields)
|
|
110
136
|
{
|
|
@@ -113,159 +139,575 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
113
139
|
continue;
|
|
114
140
|
}
|
|
115
141
|
|
|
116
|
-
bool foundChild;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
using PooledResource<List<Transform>> childBufferResource =
|
|
120
|
-
Buffers<Transform>.List.Get();
|
|
121
|
-
List<Transform> childBuffer = childBufferResource.resource;
|
|
122
|
-
|
|
123
|
-
switch (metadata.kind)
|
|
142
|
+
bool foundChild = false;
|
|
143
|
+
if (metadata.kind == FieldKind.Single)
|
|
124
144
|
{
|
|
125
|
-
|
|
145
|
+
if (TryAssignChildSingleFast(component, metadata, out Component childComponent))
|
|
126
146
|
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
metadata.SetValue(component, childComponent);
|
|
148
|
+
foundChild = true;
|
|
149
|
+
}
|
|
150
|
+
else
|
|
151
|
+
{
|
|
152
|
+
FilterParameters filters = metadata.Filters;
|
|
153
|
+
using PooledResource<List<Transform>> childBufferResource =
|
|
154
|
+
Buffers<Transform>.List.Get(out List<Transform> childBuffer);
|
|
155
|
+
if (
|
|
156
|
+
TryAssignChildSingleFallback(
|
|
157
|
+
component,
|
|
158
|
+
metadata,
|
|
159
|
+
filters,
|
|
160
|
+
childBuffer,
|
|
161
|
+
out childComponent
|
|
162
|
+
)
|
|
163
|
+
)
|
|
143
164
|
{
|
|
144
|
-
|
|
165
|
+
metadata.SetValue(component, childComponent);
|
|
166
|
+
foundChild = true;
|
|
145
167
|
}
|
|
146
|
-
|
|
147
|
-
metadata.setter(component, correctTypedArray);
|
|
148
|
-
foundChild = filteredCount > 0;
|
|
149
|
-
break;
|
|
150
168
|
}
|
|
151
|
-
|
|
169
|
+
}
|
|
170
|
+
else
|
|
171
|
+
{
|
|
172
|
+
FilterParameters filters = metadata.Filters;
|
|
173
|
+
if (
|
|
174
|
+
TryAssignChildCollectionFast(
|
|
175
|
+
component,
|
|
176
|
+
metadata,
|
|
177
|
+
filters,
|
|
178
|
+
out bool assignedAny
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
{
|
|
182
|
+
foundChild = assignedAny;
|
|
183
|
+
}
|
|
184
|
+
else
|
|
152
185
|
{
|
|
153
|
-
using PooledResource<List<
|
|
154
|
-
Buffers<
|
|
155
|
-
|
|
186
|
+
using PooledResource<List<Transform>> childBufferResource =
|
|
187
|
+
Buffers<Transform>.List.Get(out List<Transform> childBuffer);
|
|
188
|
+
|
|
189
|
+
switch (metadata.kind)
|
|
190
|
+
{
|
|
191
|
+
case FieldKind.Array:
|
|
192
|
+
{
|
|
193
|
+
using PooledResource<List<Component>> cacheResource =
|
|
194
|
+
Buffers<Component>.List.Get(out List<Component> cache);
|
|
195
|
+
cache.Clear();
|
|
196
|
+
int filteredCount = EnumerateFilteredChildComponents(
|
|
197
|
+
component,
|
|
198
|
+
metadata,
|
|
199
|
+
filters,
|
|
200
|
+
childBuffer,
|
|
201
|
+
candidate =>
|
|
202
|
+
{
|
|
203
|
+
cache.Add(candidate);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
);
|
|
156
207
|
|
|
157
|
-
|
|
208
|
+
Array correctTypedArray = metadata.arrayCreator(filteredCount);
|
|
209
|
+
for (int i = 0; i < filteredCount; ++i)
|
|
210
|
+
{
|
|
211
|
+
correctTypedArray.SetValue(cache[i], i);
|
|
212
|
+
}
|
|
158
213
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
214
|
+
metadata.SetValue(component, correctTypedArray);
|
|
215
|
+
foundChild = filteredCount > 0;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case FieldKind.List:
|
|
219
|
+
{
|
|
220
|
+
object existing = metadata.GetValue(component);
|
|
221
|
+
IList list = existing as IList;
|
|
222
|
+
if (list == null)
|
|
223
|
+
{
|
|
224
|
+
int initialCapacity =
|
|
225
|
+
metadata.attribute.MaxCount > 0
|
|
226
|
+
? metadata.attribute.MaxCount
|
|
227
|
+
: 0;
|
|
228
|
+
list = metadata.listCreator(initialCapacity);
|
|
229
|
+
metadata.SetValue(component, list);
|
|
230
|
+
}
|
|
231
|
+
else
|
|
232
|
+
{
|
|
233
|
+
list.Clear();
|
|
234
|
+
}
|
|
166
235
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
236
|
+
int added = EnumerateFilteredChildComponents(
|
|
237
|
+
component,
|
|
238
|
+
metadata,
|
|
239
|
+
filters,
|
|
240
|
+
childBuffer,
|
|
241
|
+
candidate =>
|
|
242
|
+
{
|
|
243
|
+
list.Add(candidate);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
foundChild = added > 0;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case FieldKind.HashSet:
|
|
252
|
+
{
|
|
253
|
+
object instance = metadata.GetValue(component);
|
|
254
|
+
if (instance != null && metadata.hashSetClearer != null)
|
|
255
|
+
{
|
|
256
|
+
metadata.hashSetClearer(instance);
|
|
257
|
+
}
|
|
258
|
+
else
|
|
259
|
+
{
|
|
260
|
+
int initialCapacity =
|
|
261
|
+
metadata.attribute.MaxCount > 0
|
|
262
|
+
? metadata.attribute.MaxCount
|
|
263
|
+
: 0;
|
|
264
|
+
instance = metadata.hashSetCreator(initialCapacity);
|
|
265
|
+
metadata.SetValue(component, instance);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
int added = EnumerateFilteredChildComponents(
|
|
269
|
+
component,
|
|
270
|
+
metadata,
|
|
271
|
+
filters,
|
|
272
|
+
childBuffer,
|
|
273
|
+
candidate =>
|
|
274
|
+
{
|
|
275
|
+
metadata.hashSetAdder(instance, candidate);
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
foundChild = added > 0;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
default:
|
|
284
|
+
{
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
171
287
|
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
172
290
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
291
|
+
if (!foundChild)
|
|
292
|
+
{
|
|
293
|
+
LogMissingComponentError(component, metadata, "child");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
internal static FieldMetadata<ChildComponentAttribute>[] GetOrCreateFields(Type type)
|
|
299
|
+
{
|
|
300
|
+
return FieldsByType.GetOrAdd(type, t => GetFieldMetadata<ChildComponentAttribute>(t));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private static bool TryAssignChildSingleFast(
|
|
304
|
+
Component component,
|
|
305
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
306
|
+
out Component childComponent
|
|
307
|
+
)
|
|
308
|
+
{
|
|
309
|
+
childComponent = null;
|
|
310
|
+
ChildComponentAttribute attribute = metadata.attribute;
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
metadata.isInterface
|
|
314
|
+
|| attribute.MaxDepth != 0
|
|
315
|
+
|| attribute.TagFilter != null
|
|
316
|
+
|| attribute.NameFilter != null
|
|
317
|
+
)
|
|
318
|
+
{
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
Component[] results = component.GetComponentsInChildren(
|
|
323
|
+
metadata.elementType,
|
|
324
|
+
attribute.IncludeInactive
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
if (results == null || results.Length == 0)
|
|
328
|
+
{
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
Transform componentTransform = component.transform;
|
|
333
|
+
for (int i = 0; i < results.Length; ++i)
|
|
334
|
+
{
|
|
335
|
+
Component candidate = results[i];
|
|
336
|
+
if (candidate == null)
|
|
337
|
+
{
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (attribute.OnlyDescendants && candidate.transform == componentTransform)
|
|
342
|
+
{
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
childComponent = candidate;
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private static bool TryAssignChildCollectionFast(
|
|
354
|
+
Component component,
|
|
355
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
356
|
+
FilterParameters filters,
|
|
357
|
+
out bool assignedAny
|
|
358
|
+
)
|
|
359
|
+
{
|
|
360
|
+
assignedAny = false;
|
|
361
|
+
ChildComponentAttribute attribute = metadata.attribute;
|
|
362
|
+
if (metadata.isInterface || filters.RequiresPostProcessing || attribute.MaxDepth > 0)
|
|
363
|
+
{
|
|
364
|
+
#if UNITY_EDITOR && UNITY_2020_2_OR_NEWER
|
|
365
|
+
ChildFallbackMarker.Begin();
|
|
366
|
+
ChildFallbackMarker.End();
|
|
367
|
+
#endif
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
#if UNITY_EDITOR && UNITY_2020_2_OR_NEWER
|
|
372
|
+
using (ChildFastPathMarker.Auto())
|
|
373
|
+
#endif
|
|
374
|
+
{
|
|
375
|
+
Array children = ChildComponentFastInvoker.GetArray(
|
|
376
|
+
component,
|
|
377
|
+
metadata.elementType,
|
|
378
|
+
attribute.IncludeInactive
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
Array filtered = FilterChildArray(component, metadata, children);
|
|
382
|
+
Array ordered = EnsureBreadthFirstOrder(component, metadata, filtered);
|
|
383
|
+
assignedAny = AssignChildComponentsFromArray(component, metadata, ordered);
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private static Array FilterChildArray(
|
|
389
|
+
Component component,
|
|
390
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
391
|
+
Array source
|
|
392
|
+
)
|
|
393
|
+
{
|
|
394
|
+
Type elementType = metadata.elementType;
|
|
395
|
+
if (source == null || source.Length == 0)
|
|
396
|
+
{
|
|
397
|
+
return Array.CreateInstance(elementType, 0);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
ChildComponentAttribute attribute = metadata.attribute;
|
|
401
|
+
bool onlyDescendants = attribute.OnlyDescendants;
|
|
402
|
+
Transform self = component.transform;
|
|
403
|
+
|
|
404
|
+
int maxCount = attribute.MaxCount;
|
|
405
|
+
if (!onlyDescendants && maxCount <= 0)
|
|
406
|
+
{
|
|
407
|
+
return source;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
int limit = maxCount > 0 ? Math.Min(maxCount, source.Length) : source.Length;
|
|
411
|
+
Array staged = Array.CreateInstance(elementType, limit);
|
|
412
|
+
int writeIndex = 0;
|
|
413
|
+
|
|
414
|
+
for (int i = 0; i < source.Length && writeIndex < limit; ++i)
|
|
415
|
+
{
|
|
416
|
+
Component candidate = source.GetValue(i) as Component;
|
|
417
|
+
if (candidate == null)
|
|
418
|
+
{
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (onlyDescendants && candidate.transform == self)
|
|
423
|
+
{
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
staged.SetValue(candidate, writeIndex++);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (writeIndex == staged.Length)
|
|
431
|
+
{
|
|
432
|
+
return staged;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
Array result = Array.CreateInstance(elementType, writeIndex);
|
|
436
|
+
if (writeIndex > 0)
|
|
437
|
+
{
|
|
438
|
+
Array.Copy(staged, 0, result, 0, writeIndex);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private static Array EnsureBreadthFirstOrder(
|
|
445
|
+
Component component,
|
|
446
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
447
|
+
Array source
|
|
448
|
+
)
|
|
449
|
+
{
|
|
450
|
+
Type elementType = metadata.elementType;
|
|
451
|
+
if (source == null)
|
|
452
|
+
{
|
|
453
|
+
return Array.CreateInstance(elementType, 0);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
int length = source.Length;
|
|
457
|
+
if (length <= 1)
|
|
458
|
+
{
|
|
459
|
+
return source;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
ChildComponentAttribute attribute = metadata.attribute;
|
|
463
|
+
using PooledResource<List<Transform>> traversalResource = Buffers<Transform>.List.Get(
|
|
464
|
+
out List<Transform> traversal
|
|
465
|
+
);
|
|
466
|
+
component.IterateOverAllChildrenRecursivelyBreadthFirst(
|
|
467
|
+
traversal,
|
|
468
|
+
includeSelf: !attribute.OnlyDescendants,
|
|
469
|
+
attribute.MaxDepth
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
using PooledResource<Dictionary<Transform, List<Component>>> groupedResource =
|
|
473
|
+
DictionaryBuffer<Transform, List<Component>>.Dictionary.Get(
|
|
474
|
+
out Dictionary<Transform, List<Component>> grouped
|
|
475
|
+
);
|
|
476
|
+
using PooledResource<Dictionary<Transform, int>> positionsResource = DictionaryBuffer<
|
|
477
|
+
Transform,
|
|
478
|
+
int
|
|
479
|
+
>.Dictionary.Get(out Dictionary<Transform, int> positions);
|
|
480
|
+
|
|
481
|
+
for (int i = 0; i < length; ++i)
|
|
482
|
+
{
|
|
483
|
+
Component candidate = source.GetValue(i) as Component;
|
|
484
|
+
if (candidate == null)
|
|
485
|
+
{
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
Transform key = candidate.transform;
|
|
490
|
+
if (!grouped.TryGetValue(key, out List<Component> list))
|
|
491
|
+
{
|
|
492
|
+
list = new List<Component>();
|
|
493
|
+
grouped.Add(key, list);
|
|
494
|
+
positions.Add(key, 0);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
list.Add(candidate);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
Array ordered = Array.CreateInstance(elementType, length);
|
|
501
|
+
int writeIndex = 0;
|
|
502
|
+
|
|
503
|
+
for (int i = 0; i < traversal.Count && writeIndex < length; ++i)
|
|
504
|
+
{
|
|
505
|
+
Transform transform = traversal[i];
|
|
506
|
+
if (!grouped.TryGetValue(transform, out List<Component> list))
|
|
507
|
+
{
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
int position = positions[transform];
|
|
512
|
+
while (position < list.Count && writeIndex < length)
|
|
513
|
+
{
|
|
514
|
+
ordered.SetValue(list[position], writeIndex++);
|
|
515
|
+
position++;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (position >= list.Count)
|
|
519
|
+
{
|
|
520
|
+
grouped.Remove(transform);
|
|
521
|
+
positions.Remove(transform);
|
|
522
|
+
}
|
|
523
|
+
else
|
|
524
|
+
{
|
|
525
|
+
positions[transform] = position;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (writeIndex < length && grouped.Count > 0)
|
|
530
|
+
{
|
|
531
|
+
foreach (KeyValuePair<Transform, List<Component>> pair in grouped)
|
|
532
|
+
{
|
|
533
|
+
List<Component> list = pair.Value;
|
|
534
|
+
int position = positions[pair.Key];
|
|
535
|
+
while (position < list.Count && writeIndex < length)
|
|
536
|
+
{
|
|
537
|
+
ordered.SetValue(list[position], writeIndex++);
|
|
538
|
+
position++;
|
|
176
539
|
}
|
|
177
|
-
|
|
540
|
+
if (writeIndex >= length)
|
|
178
541
|
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
182
546
|
|
|
183
|
-
|
|
547
|
+
if (writeIndex >= length)
|
|
548
|
+
{
|
|
549
|
+
return ordered;
|
|
550
|
+
}
|
|
184
551
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
metadata.elementType,
|
|
190
|
-
metadata.isInterface
|
|
191
|
-
);
|
|
552
|
+
if (writeIndex == 0)
|
|
553
|
+
{
|
|
554
|
+
return Array.CreateInstance(elementType, 0);
|
|
555
|
+
}
|
|
192
556
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
557
|
+
Array trimmed = Array.CreateInstance(elementType, writeIndex);
|
|
558
|
+
Array.Copy(ordered, 0, trimmed, 0, writeIndex);
|
|
559
|
+
return trimmed;
|
|
560
|
+
}
|
|
198
561
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
562
|
+
private static bool AssignChildComponentsFromArray(
|
|
563
|
+
Component component,
|
|
564
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
565
|
+
Array componentsArray
|
|
566
|
+
)
|
|
567
|
+
{
|
|
568
|
+
if (componentsArray == null)
|
|
569
|
+
{
|
|
570
|
+
componentsArray = Array.CreateInstance(metadata.elementType, 0);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
int count = componentsArray.Length;
|
|
574
|
+
|
|
575
|
+
switch (metadata.kind)
|
|
576
|
+
{
|
|
577
|
+
case FieldKind.Array:
|
|
578
|
+
{
|
|
579
|
+
Array instance = metadata.arrayCreator(count);
|
|
580
|
+
for (int i = 0; i < count; ++i)
|
|
581
|
+
{
|
|
582
|
+
instance.SetValue(componentsArray.GetValue(i), i);
|
|
202
583
|
}
|
|
203
|
-
|
|
584
|
+
|
|
585
|
+
metadata.SetValue(component, instance);
|
|
586
|
+
return count > 0;
|
|
587
|
+
}
|
|
588
|
+
case FieldKind.List:
|
|
589
|
+
{
|
|
590
|
+
if (metadata.GetValue(component) is IList list)
|
|
204
591
|
{
|
|
205
|
-
|
|
206
|
-
|
|
592
|
+
list.Clear();
|
|
593
|
+
}
|
|
594
|
+
else
|
|
595
|
+
{
|
|
596
|
+
list = metadata.listCreator(count);
|
|
597
|
+
metadata.SetValue(component, list);
|
|
598
|
+
}
|
|
207
599
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
600
|
+
for (int i = 0; i < count; ++i)
|
|
601
|
+
{
|
|
602
|
+
list.Add(componentsArray.GetValue(i));
|
|
603
|
+
}
|
|
211
604
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
metadata.attribute.AllowInterfaces,
|
|
227
|
-
components,
|
|
228
|
-
out Component resolved
|
|
229
|
-
)
|
|
230
|
-
)
|
|
231
|
-
{
|
|
232
|
-
childComponent = resolved;
|
|
233
|
-
foundChild = true;
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
605
|
+
return count > 0;
|
|
606
|
+
}
|
|
607
|
+
case FieldKind.HashSet:
|
|
608
|
+
{
|
|
609
|
+
object hashSet = metadata.GetValue(component);
|
|
610
|
+
if (hashSet != null && metadata.hashSetClearer != null)
|
|
611
|
+
{
|
|
612
|
+
metadata.hashSetClearer(hashSet);
|
|
613
|
+
}
|
|
614
|
+
else
|
|
615
|
+
{
|
|
616
|
+
hashSet = metadata.hashSetCreator(count);
|
|
617
|
+
metadata.SetValue(component, hashSet);
|
|
618
|
+
}
|
|
237
619
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
break;
|
|
620
|
+
for (int i = 0; i < count; ++i)
|
|
621
|
+
{
|
|
622
|
+
metadata.hashSetAdder(hashSet, componentsArray.GetValue(i));
|
|
243
623
|
}
|
|
624
|
+
|
|
625
|
+
return count > 0;
|
|
626
|
+
}
|
|
627
|
+
default:
|
|
628
|
+
{
|
|
629
|
+
return false;
|
|
244
630
|
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
245
633
|
|
|
246
|
-
|
|
634
|
+
private static bool TryAssignChildSingleFallback(
|
|
635
|
+
Component component,
|
|
636
|
+
FieldMetadata<ChildComponentAttribute> metadata,
|
|
637
|
+
FilterParameters filters,
|
|
638
|
+
List<Transform> childBuffer,
|
|
639
|
+
out Component childComponent
|
|
640
|
+
)
|
|
641
|
+
{
|
|
642
|
+
bool needsScratch = metadata.isInterface || filters.RequiresPostProcessing;
|
|
643
|
+
List<Component> scratchList = null;
|
|
644
|
+
PooledResource<List<Component>> scratch = default;
|
|
645
|
+
if (needsScratch)
|
|
646
|
+
{
|
|
647
|
+
scratch = Buffers<Component>.List.Get(out scratchList);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
childComponent = null;
|
|
651
|
+
|
|
652
|
+
foreach (
|
|
653
|
+
Transform child in component.IterateOverAllChildrenRecursivelyBreadthFirst(
|
|
654
|
+
childBuffer,
|
|
655
|
+
includeSelf: !metadata.attribute.OnlyDescendants,
|
|
656
|
+
maxDepth: metadata.attribute.MaxDepth
|
|
657
|
+
)
|
|
658
|
+
)
|
|
659
|
+
{
|
|
660
|
+
if (
|
|
661
|
+
TryResolveSingleComponent(
|
|
662
|
+
child,
|
|
663
|
+
filters,
|
|
664
|
+
metadata.elementType,
|
|
665
|
+
metadata.isInterface,
|
|
666
|
+
metadata.attribute.AllowInterfaces,
|
|
667
|
+
scratchList,
|
|
668
|
+
out Component resolved
|
|
669
|
+
)
|
|
670
|
+
)
|
|
247
671
|
{
|
|
248
|
-
|
|
672
|
+
childComponent = resolved;
|
|
673
|
+
break;
|
|
249
674
|
}
|
|
250
675
|
}
|
|
676
|
+
|
|
677
|
+
if (needsScratch)
|
|
678
|
+
{
|
|
679
|
+
scratch.Dispose();
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return childComponent != null;
|
|
251
683
|
}
|
|
252
684
|
|
|
253
|
-
private static
|
|
685
|
+
private static int EnumerateFilteredChildComponents(
|
|
254
686
|
Component component,
|
|
255
687
|
FieldMetadata<ChildComponentAttribute> metadata,
|
|
688
|
+
FilterParameters filters,
|
|
256
689
|
List<Transform> childBuffer,
|
|
257
|
-
|
|
690
|
+
Func<Component, bool> onComponent
|
|
258
691
|
)
|
|
259
692
|
{
|
|
260
|
-
|
|
693
|
+
if (component == null)
|
|
694
|
+
{
|
|
695
|
+
return 0;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
ChildComponentAttribute attribute = metadata.attribute;
|
|
699
|
+
int maxAssignments = attribute.MaxCount > 0 ? attribute.MaxCount : int.MaxValue;
|
|
700
|
+
int added = 0;
|
|
701
|
+
|
|
261
702
|
using PooledResource<List<Component>> componentBuffer = Buffers<Component>.List.Get(
|
|
262
703
|
out List<Component> components
|
|
263
704
|
);
|
|
705
|
+
|
|
264
706
|
foreach (
|
|
265
707
|
Transform child in component.IterateOverAllChildrenRecursivelyBreadthFirst(
|
|
266
708
|
childBuffer,
|
|
267
|
-
includeSelf: !
|
|
268
|
-
maxDepth:
|
|
709
|
+
includeSelf: !attribute.OnlyDescendants,
|
|
710
|
+
maxDepth: attribute.MaxDepth
|
|
269
711
|
)
|
|
270
712
|
)
|
|
271
713
|
{
|
|
@@ -273,12 +715,83 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
273
715
|
child,
|
|
274
716
|
metadata.elementType,
|
|
275
717
|
metadata.isInterface,
|
|
276
|
-
|
|
718
|
+
attribute.AllowInterfaces,
|
|
277
719
|
components
|
|
278
720
|
);
|
|
279
|
-
|
|
721
|
+
|
|
722
|
+
for (int i = 0; i < components.Count; ++i)
|
|
723
|
+
{
|
|
724
|
+
Component candidate = components[i];
|
|
725
|
+
if (!PassesStateAndFilters(candidate, filters, filterDisabledComponents: true))
|
|
726
|
+
{
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (!onComponent(candidate))
|
|
731
|
+
{
|
|
732
|
+
return added;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
added++;
|
|
736
|
+
if (added >= maxAssignments)
|
|
737
|
+
{
|
|
738
|
+
return added;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return added;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
internal static class ChildComponentFastInvoker
|
|
748
|
+
{
|
|
749
|
+
private static readonly Dictionary<Type, Func<Component, bool, Array>> ArrayGetters = new();
|
|
750
|
+
|
|
751
|
+
private static readonly MethodInfo GetComponentsInChildrenGeneric = typeof(Component)
|
|
752
|
+
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
|
753
|
+
.First(method =>
|
|
754
|
+
method.Name == nameof(Component.GetComponentsInChildren)
|
|
755
|
+
&& method.IsGenericMethodDefinition
|
|
756
|
+
&& method.GetParameters().Length == 1
|
|
757
|
+
&& method.GetParameters()[0].ParameterType == typeof(bool)
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
internal static Array GetArray(Component component, Type elementType, bool includeInactive)
|
|
761
|
+
{
|
|
762
|
+
if (!ArrayGetters.TryGetValue(elementType, out Func<Component, bool, Array> getter))
|
|
763
|
+
{
|
|
764
|
+
getter = CreateArrayGetter(elementType);
|
|
765
|
+
ArrayGetters[elementType] = getter;
|
|
280
766
|
}
|
|
281
|
-
|
|
767
|
+
|
|
768
|
+
return getter(component, includeInactive);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
private static Func<Component, bool, Array> CreateArrayGetter(Type elementType)
|
|
772
|
+
{
|
|
773
|
+
MethodInfo closedMethod = GetComponentsInChildrenGeneric.MakeGenericMethod(elementType);
|
|
774
|
+
ParameterExpression componentParameter = Expression.Parameter(
|
|
775
|
+
typeof(Component),
|
|
776
|
+
"component"
|
|
777
|
+
);
|
|
778
|
+
ParameterExpression includeInactiveParameter = Expression.Parameter(
|
|
779
|
+
typeof(bool),
|
|
780
|
+
"includeInactive"
|
|
781
|
+
);
|
|
782
|
+
MethodCallExpression invoke = Expression.Call(
|
|
783
|
+
componentParameter,
|
|
784
|
+
closedMethod,
|
|
785
|
+
includeInactiveParameter
|
|
786
|
+
);
|
|
787
|
+
UnaryExpression convert = Expression.Convert(invoke, typeof(Array));
|
|
788
|
+
return Expression
|
|
789
|
+
.Lambda<Func<Component, bool, Array>>(
|
|
790
|
+
convert,
|
|
791
|
+
componentParameter,
|
|
792
|
+
includeInactiveParameter
|
|
793
|
+
)
|
|
794
|
+
.Compile();
|
|
282
795
|
}
|
|
283
796
|
}
|
|
284
797
|
}
|