com.wallstop-studios.unity-helpers 2.0.4 β 2.1.1
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/Docs/DATA_STRUCTURES.md +7 -7
- package/Docs/EFFECTS_SYSTEM.md +836 -8
- package/Docs/EFFECTS_SYSTEM_TUTORIAL.md +77 -18
- package/Docs/HULLS.md +2 -2
- package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md.meta +7 -0
- package/Docs/INDEX.md +10 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +18 -15
- package/Docs/REFLECTION_HELPERS.md +1 -1
- package/Docs/RELATIONAL_COMPONENTS.md +51 -6
- package/Docs/SERIALIZATION.md +1 -1
- package/Docs/SINGLETONS.md +2 -2
- package/Docs/SPATIAL_TREES_2D_GUIDE.md +3 -3
- package/Docs/SPATIAL_TREES_3D_GUIDE.md +3 -3
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_SEMANTICS.md +7 -7
- package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
- package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +131 -41
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +175 -18
- package/README.md +42 -18
- package/Runtime/Core/Extension/IListExtensions.cs +720 -12
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +285 -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/Attribute.cs +144 -24
- package/Runtime/Tags/AttributeEffect.cs +119 -16
- package/Runtime/Tags/AttributeMetadataCache.cs +312 -3
- package/Runtime/Tags/AttributeModification.cs +59 -29
- package/Runtime/Tags/AttributesComponent.cs +20 -0
- package/Runtime/Tags/EffectBehavior.cs +171 -0
- package/Runtime/Tags/EffectBehavior.cs.meta +4 -0
- package/Runtime/Tags/EffectHandle.cs +5 -0
- package/Runtime/Tags/EffectHandler.cs +385 -39
- package/Runtime/Tags/EffectStackKey.cs +79 -0
- package/Runtime/Tags/EffectStackKey.cs.meta +4 -0
- package/Runtime/Tags/PeriodicEffectDefinition.cs +102 -0
- package/Runtime/Tags/PeriodicEffectDefinition.cs.meta +4 -0
- package/Runtime/Tags/PeriodicEffectRuntimeState.cs +40 -0
- package/Runtime/Tags/PeriodicEffectRuntimeState.cs.meta +4 -0
- package/Samples~/DI - Zenject/README.md +0 -2
- package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs +285 -0
- package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs.meta +11 -0
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +2 -2
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/{node_modules.meta β Tests/Editor/Tags.meta} +1 -1
- package/Tests/Editor/Utils/ScriptableObjectSingletonTests.cs +41 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +3 -3
- 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/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/JsonSerializationTest.cs +4 -3
- 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/AttributeEffectTests.cs +135 -0
- package/Tests/Runtime/Tags/AttributeEffectTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/AttributeModificationTests.cs +137 -0
- package/Tests/Runtime/Tags/AttributeTests.cs +192 -0
- package/Tests/Runtime/Tags/AttributeTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs +184 -0
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/EffectHandlerTests.cs +618 -0
- package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs +89 -0
- package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs.meta +4 -0
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +92 -0
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs.meta +3 -0
- package/package.json +1 -1
- package/scripts/lint-doc-links.ps1 +156 -11
- package/Tests/Runtime/Tags/AttributeDataTests.cs +0 -312
- /package/Tests/Runtime/Tags/{AttributeDataTests.cs.meta β AttributeModificationTests.cs.meta} +0 -0
|
@@ -42,63 +42,153 @@ namespace WallstopStudios.UnityHelpers.Editor.CustomDrawers
|
|
|
42
42
|
SerializedProperty conditionProperty = property.serializedObject.FindProperty(
|
|
43
43
|
showIf.conditionField
|
|
44
44
|
);
|
|
45
|
-
if (conditionProperty
|
|
45
|
+
if (conditionProperty != null)
|
|
46
46
|
{
|
|
47
|
-
if (conditionProperty
|
|
47
|
+
if (TryEvaluateCondition(conditionProperty, showIf, out bool serializedResult))
|
|
48
48
|
{
|
|
49
|
-
return
|
|
49
|
+
return serializedResult;
|
|
50
50
|
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
54
|
+
object enclosingObject = property.GetEnclosingObject(out _);
|
|
55
|
+
if (enclosingObject == null)
|
|
56
|
+
{
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
60
|
+
Type type = enclosingObject.GetType();
|
|
61
|
+
Dictionary<string, Func<object, object>> cachedFields = CachedFields.GetOrAdd(type);
|
|
62
|
+
if (!cachedFields.TryGetValue(showIf.conditionField, out Func<object, object> accessor))
|
|
63
|
+
{
|
|
64
|
+
FieldInfo field = type.GetField(
|
|
65
|
+
showIf.conditionField,
|
|
66
|
+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
|
|
67
|
+
);
|
|
68
|
+
if (field == null)
|
|
67
69
|
{
|
|
68
|
-
|
|
69
|
-
showIf.conditionField
|
|
70
|
-
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
|
|
70
|
+
Debug.LogError(
|
|
71
|
+
$"Failed to find conditional field {showIf.conditionField} on {type.Name}!"
|
|
71
72
|
);
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
Debug.LogError(
|
|
75
|
-
$"Failed to find conditional field {showIf.conditionField} on {type.Name}!"
|
|
76
|
-
);
|
|
77
|
-
accessor = _ => null;
|
|
78
|
-
}
|
|
79
|
-
else
|
|
80
|
-
{
|
|
81
|
-
accessor = ReflectionHelpers.GetFieldGetter(field);
|
|
82
|
-
}
|
|
83
|
-
cachedFields[showIf.conditionField] = accessor;
|
|
73
|
+
accessor = _ => null;
|
|
84
74
|
}
|
|
85
|
-
|
|
86
|
-
if (fieldValue is bool maybeCondition)
|
|
75
|
+
else
|
|
87
76
|
{
|
|
88
|
-
|
|
77
|
+
accessor = ReflectionHelpers.GetFieldGetter(field);
|
|
89
78
|
}
|
|
79
|
+
cachedFields[showIf.conditionField] = accessor;
|
|
80
|
+
}
|
|
81
|
+
object fieldValue = accessor(enclosingObject);
|
|
82
|
+
return !TryEvaluateCondition(fieldValue, showIf, out bool reflectedResult)
|
|
83
|
+
? true
|
|
84
|
+
: reflectedResult;
|
|
85
|
+
}
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
private static bool TryEvaluateCondition(
|
|
88
|
+
SerializedProperty conditionProperty,
|
|
89
|
+
WShowIfAttribute showIf,
|
|
90
|
+
out bool shouldShow
|
|
91
|
+
)
|
|
92
|
+
{
|
|
93
|
+
if (conditionProperty == null)
|
|
94
|
+
{
|
|
95
|
+
shouldShow = true;
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (conditionProperty.propertyType == SerializedPropertyType.Boolean)
|
|
100
|
+
{
|
|
101
|
+
bool condition = conditionProperty.boolValue;
|
|
102
|
+
shouldShow = showIf.inverse ? !condition : condition;
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
object conditionValue = conditionProperty.GetTargetObjectWithField(out _);
|
|
107
|
+
return TryEvaluateCondition(conditionValue, showIf, out shouldShow);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private static bool TryEvaluateCondition(
|
|
111
|
+
object conditionValue,
|
|
112
|
+
WShowIfAttribute showIf,
|
|
113
|
+
out bool shouldShow
|
|
114
|
+
)
|
|
115
|
+
{
|
|
116
|
+
if (conditionValue is bool boolean)
|
|
117
|
+
{
|
|
118
|
+
shouldShow = showIf.inverse ? !boolean : boolean;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
object[] expectedValues = showIf.expectedValues;
|
|
123
|
+
if (expectedValues == null || expectedValues.Length == 0)
|
|
124
|
+
{
|
|
125
|
+
shouldShow = true;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
bool match = false;
|
|
130
|
+
for (int i = 0; i < expectedValues.Length; ++i)
|
|
131
|
+
{
|
|
132
|
+
if (ValuesEqual(conditionValue, expectedValues[i]))
|
|
93
133
|
{
|
|
94
|
-
|
|
134
|
+
match = true;
|
|
135
|
+
break;
|
|
95
136
|
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
shouldShow = showIf.inverse ? !match : match;
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private static bool ValuesEqual(object actual, object expected)
|
|
144
|
+
{
|
|
145
|
+
if (ReferenceEquals(actual, expected))
|
|
146
|
+
{
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (actual == null || expected == null)
|
|
151
|
+
{
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (actual.Equals(expected))
|
|
156
|
+
{
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Type actualType = actual.GetType();
|
|
161
|
+
Type expectedType = expected.GetType();
|
|
162
|
+
|
|
163
|
+
try
|
|
164
|
+
{
|
|
165
|
+
if (actualType.IsEnum || expectedType.IsEnum)
|
|
166
|
+
{
|
|
167
|
+
long actualValue = Convert.ToInt64(actual);
|
|
168
|
+
long expectedValue = Convert.ToInt64(expected);
|
|
169
|
+
return actualValue == expectedValue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch
|
|
173
|
+
{
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
96
176
|
|
|
97
|
-
|
|
177
|
+
if (actual is IConvertible && expected is IConvertible)
|
|
178
|
+
{
|
|
179
|
+
try
|
|
180
|
+
{
|
|
181
|
+
double actualValue = Convert.ToDouble(actual);
|
|
182
|
+
double expectedValue = Convert.ToDouble(expected);
|
|
183
|
+
return Math.Abs(actualValue - expectedValue) < double.Epsilon;
|
|
184
|
+
}
|
|
185
|
+
catch
|
|
186
|
+
{
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
98
189
|
}
|
|
99
190
|
|
|
100
|
-
|
|
101
|
-
return showIf.inverse ? !condition : condition;
|
|
191
|
+
return false;
|
|
102
192
|
}
|
|
103
193
|
}
|
|
104
194
|
#endif
|
|
@@ -18,6 +18,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
18
18
|
|
|
19
19
|
// Prevents re-entrant execution during domain reloads/asset refreshes
|
|
20
20
|
private static bool _isEnsuring;
|
|
21
|
+
private static int _assetEditingScopeDepth;
|
|
21
22
|
|
|
22
23
|
// Controls whether informational logs are emitted. Warnings still always log.
|
|
23
24
|
internal static bool VerboseLogging { get; set; }
|
|
@@ -41,6 +42,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
41
42
|
|
|
42
43
|
_isEnsuring = true;
|
|
43
44
|
AssetDatabase.StartAssetEditing();
|
|
45
|
+
_assetEditingScopeDepth++;
|
|
44
46
|
bool anyChanges = false;
|
|
45
47
|
try
|
|
46
48
|
{
|
|
@@ -94,10 +96,24 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
string resolvedResourcesRoot = EnsureAndResolveFolderPath(ResourcesRoot);
|
|
99
|
+
if (string.IsNullOrWhiteSpace(resolvedResourcesRoot))
|
|
100
|
+
{
|
|
101
|
+
Debug.LogError(
|
|
102
|
+
"ScriptableObjectSingletonCreator: Unable to resolve required Resources root folder. Aborting singleton auto-creation."
|
|
103
|
+
);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
97
106
|
|
|
98
107
|
string resourcesSubFolder = GetResourcesSubFolder(derivedType);
|
|
99
108
|
string targetFolderRequested = CombinePaths(ResourcesRoot, resourcesSubFolder);
|
|
100
109
|
string targetFolder = EnsureAndResolveFolderPath(targetFolderRequested);
|
|
110
|
+
if (string.IsNullOrWhiteSpace(targetFolder))
|
|
111
|
+
{
|
|
112
|
+
Debug.LogError(
|
|
113
|
+
$"ScriptableObjectSingletonCreator: Unable to ensure folder '{targetFolderRequested}' for singleton {derivedType.FullName}. Skipping asset creation."
|
|
114
|
+
);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
101
117
|
|
|
102
118
|
string targetAssetPath = CombinePaths(
|
|
103
119
|
targetFolder,
|
|
@@ -156,6 +172,10 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
156
172
|
finally
|
|
157
173
|
{
|
|
158
174
|
AssetDatabase.StopAssetEditing();
|
|
175
|
+
if (_assetEditingScopeDepth > 0)
|
|
176
|
+
{
|
|
177
|
+
_assetEditingScopeDepth--;
|
|
178
|
+
}
|
|
159
179
|
_isEnsuring = false;
|
|
160
180
|
|
|
161
181
|
if (anyChanges)
|
|
@@ -448,31 +468,119 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
448
468
|
{
|
|
449
469
|
string desiredName = parts[i];
|
|
450
470
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
string
|
|
454
|
-
if (subFolders != null)
|
|
471
|
+
string matchedExisting = FindMatchingSubfolder(current, desiredName);
|
|
472
|
+
|
|
473
|
+
if (string.IsNullOrEmpty(matchedExisting))
|
|
455
474
|
{
|
|
456
|
-
|
|
475
|
+
string intendedPath = current + "/" + desiredName;
|
|
476
|
+
string createdGuid = AssetDatabase.CreateFolder(current, desiredName);
|
|
477
|
+
string createdPath = string.Empty;
|
|
478
|
+
if (!string.IsNullOrEmpty(createdGuid))
|
|
479
|
+
{
|
|
480
|
+
createdPath = NormalizePath(AssetDatabase.GUIDToAssetPath(createdGuid));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
string actualPath = FindMatchingSubfolder(current, desiredName);
|
|
484
|
+
if (string.IsNullOrEmpty(actualPath))
|
|
485
|
+
{
|
|
486
|
+
actualPath = createdPath;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
bool intendedValid = AssetDatabase.IsValidFolder(intendedPath);
|
|
490
|
+
bool actualValid =
|
|
491
|
+
!string.IsNullOrEmpty(actualPath)
|
|
492
|
+
&& AssetDatabase.IsValidFolder(actualPath);
|
|
493
|
+
|
|
494
|
+
if (!intendedValid && !actualValid)
|
|
495
|
+
{
|
|
496
|
+
bool directoryExists =
|
|
497
|
+
Directory.Exists(intendedPath)
|
|
498
|
+
|| (!string.IsNullOrEmpty(actualPath) && Directory.Exists(actualPath));
|
|
499
|
+
if (directoryExists || !string.IsNullOrEmpty(createdGuid))
|
|
500
|
+
{
|
|
501
|
+
ForceAssetDatabaseSync();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
intendedValid = AssetDatabase.IsValidFolder(intendedPath);
|
|
505
|
+
if (!intendedValid)
|
|
506
|
+
{
|
|
507
|
+
actualPath = FindMatchingSubfolder(current, desiredName);
|
|
508
|
+
if (
|
|
509
|
+
string.IsNullOrEmpty(actualPath)
|
|
510
|
+
&& !string.IsNullOrEmpty(createdGuid)
|
|
511
|
+
)
|
|
512
|
+
{
|
|
513
|
+
actualPath = NormalizePath(
|
|
514
|
+
AssetDatabase.GUIDToAssetPath(createdGuid)
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
actualValid =
|
|
519
|
+
!string.IsNullOrEmpty(actualPath)
|
|
520
|
+
&& AssetDatabase.IsValidFolder(actualPath);
|
|
521
|
+
}
|
|
522
|
+
else
|
|
523
|
+
{
|
|
524
|
+
actualPath = intendedPath;
|
|
525
|
+
actualValid = true;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (intendedValid)
|
|
457
530
|
{
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
531
|
+
current = ResolveExistingFolderPath(intendedPath);
|
|
532
|
+
LogVerbose(
|
|
533
|
+
$"ScriptableObjectSingletonCreator: Created folder '{current}'."
|
|
534
|
+
);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (
|
|
539
|
+
actualValid
|
|
540
|
+
&& string.Equals(
|
|
541
|
+
actualPath,
|
|
542
|
+
intendedPath,
|
|
543
|
+
StringComparison.OrdinalIgnoreCase
|
|
463
544
|
)
|
|
545
|
+
)
|
|
546
|
+
{
|
|
547
|
+
string renameError = AssetDatabase.MoveAsset(actualPath, intendedPath);
|
|
548
|
+
if (string.IsNullOrEmpty(renameError))
|
|
549
|
+
{
|
|
550
|
+
LogVerbose(
|
|
551
|
+
$"ScriptableObjectSingletonCreator: Renamed folder '{actualPath}' to '{intendedPath}' to correct casing."
|
|
552
|
+
);
|
|
553
|
+
current = ResolveExistingFolderPath(intendedPath);
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
LogVerbose(
|
|
558
|
+
$"ScriptableObjectSingletonCreator: Reusing newly created folder '{actualPath}' when casing correction to '{intendedPath}' failed: {renameError}."
|
|
559
|
+
);
|
|
560
|
+
current = ResolveExistingFolderPath(actualPath);
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (actualValid && AssetDatabase.IsValidFolder(actualPath))
|
|
565
|
+
{
|
|
566
|
+
bool deleted = AssetDatabase.DeleteAsset(actualPath);
|
|
567
|
+
if (!deleted)
|
|
464
568
|
{
|
|
465
|
-
|
|
466
|
-
|
|
569
|
+
Debug.LogWarning(
|
|
570
|
+
$"ScriptableObjectSingletonCreator: Unexpected folder '{actualPath}' was created while attempting to create '{intendedPath}', but it could not be removed."
|
|
571
|
+
);
|
|
467
572
|
}
|
|
573
|
+
|
|
574
|
+
Debug.LogError(
|
|
575
|
+
$"ScriptableObjectSingletonCreator: Expected to create folder '{intendedPath}', but Unity created '{actualPath}'. Aborting to avoid duplicate folders."
|
|
576
|
+
);
|
|
577
|
+
return string.Empty;
|
|
468
578
|
}
|
|
469
|
-
}
|
|
470
579
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
LogVerbose($"ScriptableObjectSingletonCreator: Created folder '{current}'.");
|
|
580
|
+
Debug.LogError(
|
|
581
|
+
$"ScriptableObjectSingletonCreator: Failed to create folder '{intendedPath}'."
|
|
582
|
+
);
|
|
583
|
+
return string.Empty;
|
|
476
584
|
}
|
|
477
585
|
else
|
|
478
586
|
{
|
|
@@ -561,6 +669,33 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
561
669
|
return current;
|
|
562
670
|
}
|
|
563
671
|
|
|
672
|
+
private static string FindMatchingSubfolder(string parent, string desiredName)
|
|
673
|
+
{
|
|
674
|
+
if (string.IsNullOrWhiteSpace(parent) || string.IsNullOrWhiteSpace(desiredName))
|
|
675
|
+
{
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
string[] subFolders = AssetDatabase.GetSubFolders(parent);
|
|
680
|
+
if (subFolders == null || subFolders.Length == 0)
|
|
681
|
+
{
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
for (int i = 0; i < subFolders.Length; i++)
|
|
686
|
+
{
|
|
687
|
+
string sub = subFolders[i];
|
|
688
|
+
int lastSlash = sub.LastIndexOf('/', sub.Length - 1);
|
|
689
|
+
string terminal = lastSlash >= 0 ? sub.Substring(lastSlash + 1) : sub;
|
|
690
|
+
if (string.Equals(terminal, desiredName, StringComparison.OrdinalIgnoreCase))
|
|
691
|
+
{
|
|
692
|
+
return sub;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
|
|
564
699
|
private static string ResolveExistingFolderPath(string intended)
|
|
565
700
|
{
|
|
566
701
|
if (string.IsNullOrWhiteSpace(intended))
|
|
@@ -621,6 +756,28 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
621
756
|
return current;
|
|
622
757
|
}
|
|
623
758
|
|
|
759
|
+
private static void ForceAssetDatabaseSync()
|
|
760
|
+
{
|
|
761
|
+
if (_assetEditingScopeDepth > 0)
|
|
762
|
+
{
|
|
763
|
+
AssetDatabase.StopAssetEditing();
|
|
764
|
+
try
|
|
765
|
+
{
|
|
766
|
+
AssetDatabase.SaveAssets();
|
|
767
|
+
AssetDatabase.Refresh();
|
|
768
|
+
}
|
|
769
|
+
finally
|
|
770
|
+
{
|
|
771
|
+
AssetDatabase.StartAssetEditing();
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
else
|
|
775
|
+
{
|
|
776
|
+
AssetDatabase.SaveAssets();
|
|
777
|
+
AssetDatabase.Refresh();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
624
781
|
private static void LogVerbose(string message)
|
|
625
782
|
{
|
|
626
783
|
if (VerboseLogging)
|
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ void Awake() {
|
|
|
77
77
|
void Awake() => this.AssignRelationalComponents();
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
**Bonus:** Works with VContainer/Zenject for automatic DI + relational wiring!
|
|
80
|
+
**Bonus:** Works with VContainer/Zenject/Reflex for automatic DI + relational wiring!
|
|
81
81
|
|
|
82
82
|
[π Learn More](Docs/RELATIONAL_COMPONENTS.md) | [π― DI β VContainer](Samples~/DI%20-%20VContainer/README.md) | [π― DI β Zenject](Samples~/DI%20-%20Zenject/README.md) | [π― DI β Reflex](Samples~/DI%20-%20Reflex/README.md)
|
|
83
83
|
|
|
@@ -454,24 +454,27 @@ Already read the [Top 5 Time-Savers](#-top-5-time-savers)? Jump directly to the
|
|
|
454
454
|
|
|
455
455
|
### Random Number Generators
|
|
456
456
|
|
|
457
|
-
Unity Helpers includes **
|
|
457
|
+
Unity Helpers includes **15 high-quality random number generators**, all implementing a rich `IRandom` interface:
|
|
458
458
|
|
|
459
459
|
#### Available Generators
|
|
460
460
|
|
|
461
|
-
| Generator | Speed | Quality | Use Case
|
|
462
|
-
| ------------------------------- | --------- | --------- |
|
|
463
|
-
| **IllusionFlow** β | Fast
|
|
464
|
-
| **PcgRandom**
|
|
465
|
-
| **
|
|
466
|
-
| **
|
|
467
|
-
| **
|
|
468
|
-
| **
|
|
469
|
-
| **
|
|
470
|
-
| **
|
|
471
|
-
| **
|
|
472
|
-
| **
|
|
473
|
-
| **
|
|
474
|
-
| **
|
|
461
|
+
| Generator | Speed | Quality | Use Case |
|
|
462
|
+
| ------------------------------- | --------- | --------- | ------------------------------------------ |
|
|
463
|
+
| **IllusionFlow** β | Very Fast | Excellent | Default choice (via PRNG.Instance) |
|
|
464
|
+
| **PcgRandom** β | Very Fast | Excellent | Deterministic gameplay; explicit seeding |
|
|
465
|
+
| **FlurryBurstRandom** | Very Fast | Excellent | High-quality PCG/Xoshiro alternative |
|
|
466
|
+
| **RomuDuo** | Very Fast | Good | Maximum performance needed |
|
|
467
|
+
| **LinearCongruentialGenerator** | Fastest | Fair | Simple, fast generation |
|
|
468
|
+
| **XorShiftRandom** | Very Fast | Good | General purpose |
|
|
469
|
+
| **XoroShiroRandom** | Very Fast | Good | General purpose |
|
|
470
|
+
| **SplitMix64** | Very Fast | Good | Initialization, hashing |
|
|
471
|
+
| **StormDropRandom** | Fast | Excellent | Large-buffer streams for heavy simulations |
|
|
472
|
+
| **PhotonSpinRandom** | Moderate | Excellent | Bulk generation; long non-overlapping runs |
|
|
473
|
+
| **SquirrelRandom** | Moderate | Good | Hash-based generation |
|
|
474
|
+
| **WyRandom** | Moderate | Good | Hashing applications |
|
|
475
|
+
| **DotNetRandom** | Moderate | Good | .NET compatibility |
|
|
476
|
+
| **SystemRandom** | Slow | Good | Backward compatibility |
|
|
477
|
+
| **UnityRandom** | Very Slow | Good | Unity compatibility |
|
|
475
478
|
|
|
476
479
|
β **Recommended**: Use `PRNG.Instance` (currently IllusionFlow)
|
|
477
480
|
|
|
@@ -1018,7 +1021,7 @@ void ProcessLargeDataset(int size)
|
|
|
1018
1021
|
|
|
1019
1022
|
- When the define is present, optional assemblies under `Runtime/Integrations/*` compile automatically and expose helpers like `RelationalComponentsInstaller` (Zenject/Reflex) and `RegisterRelationalComponents()` (VContainer).
|
|
1020
1023
|
- If you use UPM, no manual defines are required β the package IDs above trigger symbols via `versionDefines` in the asmdefs.
|
|
1021
|
-
- For test scenarios without LifetimeScope (VContainer)
|
|
1024
|
+
- For test scenarios without LifetimeScope (VContainer), SceneContext (Zenject), or SceneScope (Reflex), see [DI Integrations: Testing and Edge Cases](Docs/RELATIONAL_COMPONENTS.md#di-integrations-testing-and-edge-cases) for stepβbyβstep patterns.
|
|
1022
1025
|
|
|
1023
1026
|
**Quick start:**
|
|
1024
1027
|
|
|
@@ -1046,6 +1049,12 @@ using Zenject;
|
|
|
1046
1049
|
using WallstopStudios.UnityHelpers.Integrations.Zenject;
|
|
1047
1050
|
|
|
1048
1051
|
var enemy = Container.InstantiateComponentWithRelations(enemyPrefab, parent);
|
|
1052
|
+
|
|
1053
|
+
// Reflex β prefab instantiation with DI + relations
|
|
1054
|
+
using Reflex.Core;
|
|
1055
|
+
using WallstopStudios.UnityHelpers.Integrations.Reflex;
|
|
1056
|
+
|
|
1057
|
+
var enemy = container.InstantiateComponentWithRelations(enemyPrefab, parent);
|
|
1049
1058
|
```
|
|
1050
1059
|
|
|
1051
1060
|
See the full guide with scenarios, troubleshooting, and testing patterns: [Relational Components Guide](Docs/RELATIONAL_COMPONENTS.md)
|
|
@@ -1064,10 +1073,17 @@ See the full guide with scenarios, troubleshooting, and testing patterns: [Relat
|
|
|
1064
1073
|
- `container.InjectGameObjectWithRelations(root, includeInactiveChildren)` β inject hierarchy + assign
|
|
1065
1074
|
- `container.InstantiateGameObjectWithRelations(prefab, parent)` β instantiate GO + inject + assign
|
|
1066
1075
|
|
|
1076
|
+
- Reflex:
|
|
1077
|
+
- `container.InjectWithRelations(component)` β inject + assign a single instance
|
|
1078
|
+
- `container.InstantiateComponentWithRelations(prefab, parent)` β instantiate + inject + assign
|
|
1079
|
+
- `container.InjectGameObjectWithRelations(root, includeInactiveChildren)` β inject hierarchy + assign
|
|
1080
|
+
- `container.InstantiateGameObjectWithRelations(prefab, parent)` β instantiate GO + inject + assign
|
|
1081
|
+
|
|
1067
1082
|
### Additive Scene Loads
|
|
1068
1083
|
|
|
1069
1084
|
- VContainer: `RegisterRelationalComponents(..., enableAdditiveSceneListener: true)` registers a listener that hydrates components in newly loaded scenes.
|
|
1070
|
-
- Zenject: `RelationalComponentsInstaller` exposes a toggle
|
|
1085
|
+
- Zenject: `RelationalComponentsInstaller` exposes a toggle "Listen For Additive Scenes" to register the same behavior.
|
|
1086
|
+
- Reflex: `RelationalComponentsInstaller` exposes a toggle "Listen For Additive Scenes" to register the same behavior.
|
|
1071
1087
|
- Only the newly loaded scene is processed; other loaded scenes are not reβscanned.
|
|
1072
1088
|
|
|
1073
1089
|
### Performance Options
|
|
@@ -1076,6 +1092,7 @@ See the full guide with scenarios, troubleshooting, and testing patterns: [Relat
|
|
|
1076
1092
|
- Single-pass scan (default) reduces `FindObjectsOfType` calls by scanning once and checking type ancestry.
|
|
1077
1093
|
- VContainer: `new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)`
|
|
1078
1094
|
- Zenject: `new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)`
|
|
1095
|
+
- Reflex: `new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)`
|
|
1079
1096
|
- Per-object paths (instantiate/inject helpers, pools) avoid global scans entirely for objects created via DI.
|
|
1080
1097
|
|
|
1081
1098
|
---
|
|
@@ -1110,6 +1127,12 @@ Unity Helpers is built with performance as a top priority:
|
|
|
1110
1127
|
- Safe for IL2CPP and AOT platforms
|
|
1111
1128
|
- [π Reflection Performance](Docs/REFLECTION_HELPERS.md)
|
|
1112
1129
|
|
|
1130
|
+
**List Sorting:**
|
|
1131
|
+
|
|
1132
|
+
- Multiple adaptive algorithms (`Ghost`, `Meteor`, `Power`, `Grail`, `Pattern-Defeating QuickSort`, `Insertion`) tuned for `IList<T>`
|
|
1133
|
+
- Deterministic datasets (sorted, nearly sorted, shuffled) across sizes from 100 to 1,000,000
|
|
1134
|
+
- [π IList Sorting Performance Benchmarks](Docs/ILIST_SORTING_PERFORMANCE.md)
|
|
1135
|
+
|
|
1113
1136
|
---
|
|
1114
1137
|
|
|
1115
1138
|
## Documentation Index
|
|
@@ -1143,6 +1166,7 @@ Unity Helpers is built with performance as a top priority:
|
|
|
1143
1166
|
|
|
1144
1167
|
- Random Performance β [Random Performance](Docs/RANDOM_PERFORMANCE.md)
|
|
1145
1168
|
- Reflection Helpers β [Reflection Helpers](Docs/REFLECTION_HELPERS.md)
|
|
1169
|
+
- IList Sorting Performance β [IList Sorting Performance](Docs/ILIST_SORTING_PERFORMANCE.md)
|
|
1146
1170
|
|
|
1147
1171
|
**Project Info:**
|
|
1148
1172
|
|