com.wallstop-studios.unity-helpers 2.0.0-rc43 → 2.0.0-rc45

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.
Files changed (31) hide show
  1. package/README.md +16 -0
  2. package/Runtime/Core/Helper/FormattingHelpers.cs +32 -0
  3. package/Runtime/Core/Helper/FormattingHelpers.cs.meta +3 -0
  4. package/Runtime/Core/Helper/Objects.cs +2 -2
  5. package/Runtime/Core/Helper/ReflectionHelpers.cs +156 -1
  6. package/Runtime/Core/Helper/SpriteHelpers.cs +7 -52
  7. package/Runtime/Core/Helper/WallMath.cs +5 -5
  8. package/Runtime/Core/Random/AbstractRandom.cs +6 -5
  9. package/Runtime/Core/Random/DotNetRandom.cs +2 -0
  10. package/Runtime/Core/Random/IRandom.cs +1 -0
  11. package/Runtime/Core/Random/LinearCongruentialGenerator.cs +49 -0
  12. package/Runtime/Core/Random/LinearCongruentialGenerator.cs.meta +3 -0
  13. package/Runtime/Core/Random/PcgRandom.cs +1 -1
  14. package/Runtime/Core/Random/RomuDuo.cs +1 -1
  15. package/Runtime/Core/Random/SplitMix64.cs +1 -1
  16. package/Runtime/Core/Random/SystemRandom.cs +1 -1
  17. package/Runtime/Core/Random/WyRandom.cs +1 -1
  18. package/Runtime/Core/Random/XorShiftRandom.cs +13 -8
  19. package/Runtime/Core/Random/XorShiroRandom.cs +2 -0
  20. package/Runtime/UI/LayeredImage.cs +2 -2
  21. package/Runtime/Utils/SpriteRendererMetadata.cs +104 -42
  22. package/Tests/Runtime/Helper/ArrayConverterTests.cs +3 -3
  23. package/Tests/Runtime/Helper/FormattingHelperTests.cs +129 -0
  24. package/Tests/Runtime/Helper/FormattingHelperTests.cs.meta +3 -0
  25. package/Tests/Runtime/Helper/ObjectHelperTests.cs +2 -2
  26. package/Tests/Runtime/Helper/ReflectionHelperTests.cs +356 -35
  27. package/Tests/Runtime/Helper/WallMathTests.cs +4 -4
  28. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +21 -3
  29. package/Tests/Runtime/Random/LinearCongruentialGeneratorTests.cs +12 -0
  30. package/Tests/Runtime/Random/LinearCongruentialGeneratorTests.cs.meta +3 -0
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -129,6 +129,22 @@ random.NextNoiseMap(width, height); // A configurable noise map generated using
129
129
  - XorShift
130
130
  - XorShiro
131
131
 
132
+ ## Performance (Number of Operations / Second)
133
+
134
+ | Random | NextBool | Next | NextUInt | NextFloat | NextDouble | NextUint - Range | NextInt - Range |
135
+ | ------ | -------- | ---- | -------- | --------- | ---------- | ---------------- | --------------- |
136
+ | PcgRandom | 34,260,478 | 34,303,866 | 35,148,246 | 24,818,438 | 30,034,632 |22,436,474 |20,801,797 |
137
+ | SystemRandom | 28,698,806 | 30,040,782 | 20,435,092 | 24,079,399 | 27,284,147 |20,735,769 |19,861,780 |
138
+ | SquirrelRandom | 33,285,784 | 33,439,909 | 34,611,893 | 23,885,731 | 28,958,725 |21,520,279 |20,311,372 |
139
+ | XorShiftRandom | 34,695,989 | 35,147,320 | 35,997,718 | 25,248,238 | 31,582,991 |22,679,928 |21,255,319 |
140
+ | DotNetRandom | 18,097,489 | 18,191,042 | 18,783,063 | 14,061,444 | 15,730,439 |13,284,050 |12,596,913 |
141
+ | WyRandom | 28,490,852 | 29,086,052 | 29,724,907 | 20,252,065 | 24,542,201 |18,474,790 |17,404,641 |
142
+ | SplitMix64 | 34,301,843 | 34,343,404 | 35,512,284 | 24,289,416 | 30,586,231 |22,470,330 |20,850,965 |
143
+ | RomuDuo | 32,969,889 | 33,413,212 | 34,339,227 | 23,755,059 | 29,483,136 |21,671,641 |20,253,479 |
144
+ | XorShiroRandom | 31,529,025 | 31,717,709 | 32,536,277 | 22,421,715 | 27,619,417 |20,843,993 |19,270,063 |
145
+ | UnityRandom | 24,925,268 | 24,830,594 | 26,429,283 | 17,864,528 | 21,206,384 |16,376,626 |15,528,972 |
146
+ | LinearCongruentialGenerator | 35,013,818 | 35,025,182 | 35,843,533 | 25,093,401 | 31,553,487 |22,579,798 |21,211,175 |
147
+
132
148
  # Spatial Trees
133
149
  There are three implemented 2D immutable spatial trees that can store generic objects, as long as there is some resolution function that can convert them into Vector2 spatial positions.
134
150
 
@@ -0,0 +1,32 @@
1
+ namespace UnityHelpers.Core.Helper
2
+ {
3
+ using System;
4
+
5
+ public static class FormattingHelpers
6
+ {
7
+ private static readonly string[] ByteSizes = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
8
+
9
+ public static string FormatBytes(long bytes)
10
+ {
11
+ bytes = Math.Max(0L, bytes);
12
+ double len = bytes;
13
+ int order = 0;
14
+
15
+ const int byteInChunk = 1024;
16
+ while (byteInChunk <= len)
17
+ {
18
+ len /= byteInChunk;
19
+ if (order < ByteSizes.Length - 1)
20
+ {
21
+ ++order;
22
+ }
23
+ else
24
+ {
25
+ throw new ArgumentException($"Too many bytes! Cannot parse {bytes}");
26
+ }
27
+ }
28
+
29
+ return $"{len:0.##} {ByteSizes[order]}";
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: a4bf649a0aef41b48c8afb261c4bd1d1
3
+ timeCreated: 1742614257
@@ -7,8 +7,8 @@
7
7
 
8
8
  public static class Objects
9
9
  {
10
- private const int HashBase = 839;
11
- private const int HashMultiplier = 4021;
10
+ private const int HashBase = 5556137;
11
+ private const int HashMultiplier = 95785853;
12
12
 
13
13
  public static T FromWeakReference<T>(WeakReference weakReference)
14
14
  where T : class
@@ -20,6 +20,7 @@
20
20
  public static Array CreateArray(Type type, int length)
21
21
  {
22
22
  return ArrayCreators
23
+ // ReSharper disable once ConvertClosureToMethodGroup
23
24
  .GetOrAdd(type, elementType => GetArrayCreator(elementType))
24
25
  .Invoke(length);
25
26
  }
@@ -28,6 +29,7 @@
28
29
  public static IList CreateList(Type elementType, int length)
29
30
  {
30
31
  return ListWithCapacityCreators
32
+ // ReSharper disable once ConvertClosureToMethodGroup
31
33
  .GetOrAdd(elementType, type => GetListWithCapacityCreator(type))
32
34
  .Invoke(length);
33
35
  }
@@ -35,6 +37,7 @@
35
37
  [MethodImpl(MethodImplOptions.AggressiveInlining)]
36
38
  public static IList CreateList(Type elementType)
37
39
  {
40
+ // ReSharper disable once ConvertClosureToMethodGroup
38
41
  return ListCreators.GetOrAdd(elementType, type => GetListCreator(type)).Invoke();
39
42
  }
40
43
 
@@ -44,7 +47,7 @@
44
47
  return field.GetValue;
45
48
  #else
46
49
  DynamicMethod dynamicMethod = new(
47
- $"Get{(field.DeclaringType?.Name ?? string.Empty)}{field.Name}",
50
+ $"Get{field.DeclaringType.Name}{field.Name}",
48
51
  typeof(object),
49
52
  new[] { typeof(object) },
50
53
  field.DeclaringType,
@@ -72,6 +75,41 @@
72
75
  #endif
73
76
  }
74
77
 
78
+ public static Func<object> GetStaticFieldGetter(FieldInfo field)
79
+ {
80
+ if (!field.IsStatic)
81
+ {
82
+ throw new ArgumentException(nameof(field));
83
+ }
84
+
85
+ #if WEB_GL
86
+ return () => field.GetValue(null);
87
+ #else
88
+ DynamicMethod dynamicMethod = new(
89
+ $"Get{field.DeclaringType.Name}{field.Name}",
90
+ typeof(object),
91
+ Type.EmptyTypes, // No parameters for static fields
92
+ field.DeclaringType,
93
+ true
94
+ );
95
+
96
+ ILGenerator il = dynamicMethod.GetILGenerator();
97
+
98
+ // Load the static field
99
+ il.Emit(OpCodes.Ldsfld, field);
100
+
101
+ // If the field's type is a value type, box it.
102
+ if (field.FieldType.IsValueType)
103
+ {
104
+ il.Emit(OpCodes.Box, field.FieldType);
105
+ }
106
+
107
+ il.Emit(OpCodes.Ret);
108
+
109
+ return (Func<object>)dynamicMethod.CreateDelegate(typeof(Func<object>));
110
+ #endif
111
+ }
112
+
75
113
  public static Func<TInstance, TValue> GetFieldGetter<TInstance, TValue>(FieldInfo field)
76
114
  {
77
115
  #if WEB_GL
@@ -142,6 +180,59 @@
142
180
  #endif
143
181
  }
144
182
 
183
+ public static Func<TValue> GetStaticFieldGetter<TValue>(FieldInfo field)
184
+ {
185
+ if (!field.IsStatic)
186
+ {
187
+ throw new ArgumentException(nameof(field));
188
+ }
189
+
190
+ #if WEB_GL
191
+ return Getter;
192
+ TValue Getter()
193
+ {
194
+ return (TValue)field.GetValue(null);
195
+ }
196
+ #else
197
+ DynamicMethod dynamicMethod = new(
198
+ $"GetGenericStatic{field.DeclaringType.Name}{field.Name}",
199
+ typeof(TValue),
200
+ Type.EmptyTypes, // no parameters needed for static fields
201
+ field.DeclaringType,
202
+ true
203
+ );
204
+
205
+ ILGenerator il = dynamicMethod.GetILGenerator();
206
+
207
+ // Load the static field.
208
+ il.Emit(OpCodes.Ldsfld, field);
209
+
210
+ // Handle conversion from the field type to TValue.
211
+ if (field.FieldType.IsValueType)
212
+ {
213
+ if (!typeof(TValue).IsValueType)
214
+ {
215
+ il.Emit(OpCodes.Box, field.FieldType);
216
+ }
217
+ }
218
+ else
219
+ {
220
+ if (typeof(TValue).IsValueType)
221
+ {
222
+ il.Emit(OpCodes.Unbox_Any, typeof(TValue));
223
+ }
224
+ else if (typeof(TValue) != field.FieldType)
225
+ {
226
+ il.Emit(OpCodes.Castclass, typeof(TValue));
227
+ }
228
+ }
229
+
230
+ il.Emit(OpCodes.Ret);
231
+
232
+ return (Func<TValue>)dynamicMethod.CreateDelegate(typeof(Func<TValue>));
233
+ #endif
234
+ }
235
+
145
236
  public static FieldSetter<TInstance, TValue> GetFieldSetter<TInstance, TValue>(
146
237
  FieldInfo field
147
238
  )
@@ -184,6 +275,36 @@
184
275
  #endif
185
276
  }
186
277
 
278
+ public static Action<TValue> GetStaticFieldSetter<TValue>(FieldInfo field)
279
+ {
280
+ if (!field.IsStatic)
281
+ {
282
+ throw new ArgumentException(nameof(field));
283
+ }
284
+ #if WEB_GL
285
+ return Setter;
286
+ void Setter(TValue newValue)
287
+ {
288
+ field.SetValue(null, newValue);
289
+ }
290
+ #else
291
+ DynamicMethod dynamicMethod = new(
292
+ $"SetFieldGenericStatic{field.DeclaringType.Name}{field.Name}",
293
+ typeof(void),
294
+ new[] { typeof(TValue) },
295
+ field.Module,
296
+ true
297
+ );
298
+
299
+ ILGenerator il = dynamicMethod.GetILGenerator();
300
+ il.Emit(OpCodes.Ldarg_0);
301
+ il.Emit(OpCodes.Stsfld, field);
302
+ il.Emit(OpCodes.Ret);
303
+
304
+ return (Action<TValue>)dynamicMethod.CreateDelegate(typeof(Action<TValue>));
305
+ #endif
306
+ }
307
+
187
308
  public static Action<object, object> GetFieldSetter(FieldInfo field)
188
309
  {
189
310
  #if WEB_GL
@@ -217,6 +338,40 @@
217
338
  #endif
218
339
  }
219
340
 
341
+ public static Action<object> GetStaticFieldSetter(FieldInfo field)
342
+ {
343
+ if (!field.IsStatic)
344
+ {
345
+ throw new ArgumentException(nameof(field));
346
+ }
347
+ #if WEB_GL
348
+ return value => field.SetValue(null, value);
349
+ #else
350
+ DynamicMethod dynamicMethod = new(
351
+ $"SetFieldStatic{field.DeclaringType.Name}{field.Name}",
352
+ null,
353
+ new[] { typeof(object) },
354
+ field.DeclaringType.Module,
355
+ true
356
+ );
357
+
358
+ ILGenerator il = dynamicMethod.GetILGenerator();
359
+
360
+ // Load the new value (argument 0)
361
+ il.Emit(OpCodes.Ldarg_0);
362
+ // Convert the object to the field's type (unbox or cast as needed)
363
+ il.Emit(
364
+ field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
365
+ field.FieldType
366
+ );
367
+ // Set the static field
368
+ il.Emit(OpCodes.Stsfld, field);
369
+ il.Emit(OpCodes.Ret);
370
+
371
+ return (Action<object>)dynamicMethod.CreateDelegate(typeof(Action<object>));
372
+ #endif
373
+ }
374
+
220
375
  public static Func<int, Array> GetArrayCreator(Type elementType)
221
376
  {
222
377
  #if WEB_GL
@@ -3,13 +3,12 @@
3
3
  using Extension;
4
4
  using UnityEditor;
5
5
  using UnityEngine;
6
- using Utils;
7
6
 
8
7
  public static class SpriteHelpers
9
8
  {
10
9
  public static void MakeReadable(this Texture2D texture)
11
10
  {
12
- if (texture.isReadable)
11
+ if (texture == null || texture.isReadable)
13
12
  {
14
13
  return;
15
14
  }
@@ -29,58 +28,14 @@
29
28
  return;
30
29
  }
31
30
 
32
- tImporter.isReadable = true;
33
- EditorUtility.SetDirty(tImporter);
34
- tImporter.SaveAndReimport();
35
- EditorUtility.SetDirty(texture);
36
- #endif
37
- }
38
-
39
- public static void SetSpritePivot(string fullSpritePath, Vector2 pivot)
40
- {
41
- #if UNITY_EDITOR
42
- SetSpritePivot(AssetImporter.GetAtPath(fullSpritePath) as TextureImporter, pivot);
43
- #endif
44
- }
45
-
46
- public static void SetSpritePivot(Sprite sprite, Vector2 pivot)
47
- {
48
- #if UNITY_EDITOR
49
- SetSpritePivot(
50
- AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sprite)) as TextureImporter,
51
- pivot
52
- );
53
- #endif
54
- }
55
-
56
- #if UNITY_EDITOR
57
- public static void SetSpritePivot(TextureImporter textureImporter, Vector2 pivot)
58
- {
59
- if (textureImporter == null)
31
+ if (!tImporter.isReadable)
60
32
  {
61
- return;
33
+ tImporter.isReadable = true;
34
+ EditorUtility.SetDirty(tImporter);
35
+ tImporter.SaveAndReimport();
36
+ EditorUtility.SetDirty(texture);
62
37
  }
63
-
64
- TextureImporterSettings textureImportSettings = new TextureImporterSettings();
65
- textureImporter.ReadTextureSettings(textureImportSettings);
66
- textureImportSettings.spriteAlignment = (int)SpriteAlignment.Custom;
67
- textureImportSettings.wrapMode = TextureWrapMode.Clamp;
68
- textureImportSettings.filterMode = FilterMode.Trilinear;
69
- textureImporter.SetTextureSettings(textureImportSettings);
70
-
71
- TextureImporterPlatformSettings importerSettings = new TextureImporterPlatformSettings
72
- {
73
- resizeAlgorithm = TextureResizeAlgorithm.Bilinear,
74
- maxTextureSize = SetTextureImportData.RegularTextureSize,
75
- textureCompression = TextureImporterCompression.Compressed,
76
- format = TextureImporterFormat.Automatic,
77
- };
78
-
79
- textureImporter.SetPlatformTextureSettings(importerSettings);
80
- textureImporter.isReadable = true;
81
- textureImporter.spritePivot = pivot;
82
- textureImporter.SaveAndReimport();
83
- }
84
38
  #endif
39
+ }
85
40
  }
86
41
  }
@@ -125,12 +125,12 @@
125
125
  if (0 < direction.x)
126
126
  {
127
127
  float t2 = (max.x - center.x) / direction.x;
128
- tMax = Math.Min(tMax, t2);
128
+ tMax = Mathf.Min(tMax, t2);
129
129
  }
130
130
  else
131
131
  {
132
132
  float t1 = (min.x - center.x) / direction.x;
133
- tMax = Math.Min(tMax, t1);
133
+ tMax = Mathf.Min(tMax, t1);
134
134
  }
135
135
  }
136
136
 
@@ -139,12 +139,12 @@
139
139
  if (direction.y > 0)
140
140
  {
141
141
  float t2 = (max.y - center.y) / direction.y;
142
- tMax = Math.Min(tMax, t2);
142
+ tMax = Mathf.Min(tMax, t2);
143
143
  }
144
144
  else
145
145
  {
146
146
  float t1 = (min.y - center.y) / direction.y;
147
- tMax = Math.Min(tMax, t1);
147
+ tMax = Mathf.Min(tMax, t1);
148
148
  }
149
149
  }
150
150
 
@@ -160,7 +160,7 @@
160
160
 
161
161
  public static bool Approximately(this float lhs, float rhs, float tolerance = 0.045f)
162
162
  {
163
- return Math.Abs(lhs - rhs) <= tolerance;
163
+ return Mathf.Abs(lhs - rhs) <= tolerance;
164
164
  }
165
165
  }
166
166
  }
@@ -21,6 +21,8 @@
21
21
 
22
22
  public abstract RandomState InternalState { get; }
23
23
 
24
+ private readonly byte[] _guidBytes = new byte[16];
25
+
24
26
  public virtual int Next()
25
27
  {
26
28
  // Mask out the MSB to ensure the value is within [0, int.MaxValue]
@@ -410,24 +412,23 @@
410
412
  where T : struct, Enum
411
413
  {
412
414
  Type enumType = typeof(T);
413
- T[] enumValues = (T[])EnumTypeCache.GetOrAdd(enumType, Enum.GetValues);
415
+ T[] enumValues = (T[])EnumTypeCache.GetOrAdd(enumType, type => Enum.GetValues(type));
414
416
 
415
417
  return RandomOf(enumValues);
416
418
  }
417
419
 
418
420
  public Guid NextGuid()
419
421
  {
420
- return new Guid(GenerateGuidBytes());
422
+ return new Guid(GenerateGuidBytes(_guidBytes));
421
423
  }
422
424
 
423
425
  public KGuid NextKGuid()
424
426
  {
425
- return new KGuid(GenerateGuidBytes());
427
+ return new KGuid(GenerateGuidBytes(_guidBytes));
426
428
  }
427
429
 
428
- private byte[] GenerateGuidBytes()
430
+ private byte[] GenerateGuidBytes(byte[] guidBytes)
429
431
  {
430
- byte[] guidBytes = new byte[16];
431
432
  NextBytes(guidBytes);
432
433
  SetUuidV4Bits(guidBytes);
433
434
  return guidBytes;
@@ -8,6 +8,8 @@
8
8
  [DataContract]
9
9
  public sealed class DotNetRandom : AbstractRandom
10
10
  {
11
+ public static DotNetRandom Instance => ThreadLocalRandom<DotNetRandom>.Instance;
12
+
11
13
  public override RandomState InternalState =>
12
14
  new RandomState(unchecked((ulong)_seed), state2: _numberGenerated);
13
15
 
@@ -138,6 +138,7 @@
138
138
  double NextGaussian(double mean = 0, double stdDev = 1);
139
139
 
140
140
  Guid NextGuid();
141
+
141
142
  KGuid NextKGuid();
142
143
 
143
144
  T NextOf<T>(IEnumerable<T> enumerable);
@@ -0,0 +1,49 @@
1
+ namespace UnityHelpers.Core.Random
2
+ {
3
+ using System;
4
+ using System.Text.Json.Serialization;
5
+
6
+ public sealed class LinearCongruentialGenerator : AbstractRandom
7
+ {
8
+ public static LinearCongruentialGenerator Instance =>
9
+ ThreadLocalRandom<LinearCongruentialGenerator>.Instance;
10
+
11
+ public override RandomState InternalState => new(_state, 0, _cachedGaussian);
12
+
13
+ private uint _state;
14
+
15
+ public LinearCongruentialGenerator()
16
+ : this(Guid.NewGuid()) { }
17
+
18
+ public LinearCongruentialGenerator(int seed)
19
+ {
20
+ _state = unchecked((uint)seed);
21
+ }
22
+
23
+ public LinearCongruentialGenerator(Guid seed)
24
+ {
25
+ _state = unchecked((uint)seed.GetHashCode());
26
+ }
27
+
28
+ [JsonConstructor]
29
+ public LinearCongruentialGenerator(RandomState internalState)
30
+ {
31
+ _state = unchecked((uint)internalState.State1);
32
+ _cachedGaussian = internalState.Gaussian;
33
+ }
34
+
35
+ public override uint NextUint()
36
+ {
37
+ unchecked
38
+ {
39
+ _state = _state * 1664525U + 1013904223U;
40
+ }
41
+ return _state;
42
+ }
43
+
44
+ public override IRandom Copy()
45
+ {
46
+ return new LinearCongruentialGenerator(InternalState);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 23eaec3a914247e59d9bd39e199b718e
3
+ timeCreated: 1742702544
@@ -15,7 +15,7 @@
15
15
  IComparable,
16
16
  IComparable<PcgRandom>
17
17
  {
18
- public static IRandom Instance => ThreadLocalRandom<PcgRandom>.Instance;
18
+ public static PcgRandom Instance => ThreadLocalRandom<PcgRandom>.Instance;
19
19
 
20
20
  public override RandomState InternalState => new(_state, _increment, _cachedGaussian);
21
21
 
@@ -15,7 +15,7 @@
15
15
  IComparable,
16
16
  IComparable<RomuDuo>
17
17
  {
18
- public static IRandom Instance => ThreadLocalRandom<RomuDuo>.Instance;
18
+ public static RomuDuo Instance => ThreadLocalRandom<RomuDuo>.Instance;
19
19
  public override RandomState InternalState => new(_x, _y, _cachedGaussian);
20
20
 
21
21
  internal ulong _x;
@@ -12,7 +12,7 @@
12
12
  IComparable,
13
13
  IComparable<SplitMix64>
14
14
  {
15
- public static IRandom Instance => ThreadLocalRandom<SplitMix64>.Instance;
15
+ public static SplitMix64 Instance => ThreadLocalRandom<SplitMix64>.Instance;
16
16
 
17
17
  public override RandomState InternalState => new(_state, 0, _cachedGaussian);
18
18
 
@@ -16,7 +16,7 @@
16
16
  private const int SeedArraySize = 56;
17
17
  private const int LastSeedIndex = SeedArraySize - 1;
18
18
 
19
- public static IRandom Instance => ThreadLocalRandom<SystemRandom>.Instance;
19
+ public static SystemRandom Instance => ThreadLocalRandom<SystemRandom>.Instance;
20
20
 
21
21
  public override RandomState InternalState =>
22
22
  new(
@@ -14,7 +14,7 @@
14
14
  private const ulong Prime0 = 0xa0761d6478bd642f;
15
15
  private const ulong Prime1 = 0xe7037ed1a0b428db;
16
16
 
17
- public static IRandom Instance => ThreadLocalRandom<WyRandom>.Instance;
17
+ public static WyRandom Instance => ThreadLocalRandom<WyRandom>.Instance;
18
18
 
19
19
  public override RandomState InternalState => new RandomState(_state);
20
20
 
@@ -8,18 +8,25 @@
8
8
  [DataContract]
9
9
  public sealed class XorShiftRandom : AbstractRandom
10
10
  {
11
- public static IRandom Instance => ThreadLocalRandom<XorShiftRandom>.Instance;
11
+ public static XorShiftRandom Instance => ThreadLocalRandom<XorShiftRandom>.Instance;
12
12
 
13
13
  public override RandomState InternalState => new(_state, 0, _cachedGaussian);
14
14
 
15
15
  private uint _state;
16
16
 
17
17
  public XorShiftRandom()
18
- : this(Guid.NewGuid().GetHashCode()) { }
18
+ : this(Guid.NewGuid()) { }
19
19
 
20
20
  public XorShiftRandom(int state)
21
21
  {
22
22
  _state = unchecked((uint)state);
23
+ _state = _state != 0 ? _state : 2463534242U;
24
+ }
25
+
26
+ public XorShiftRandom(Guid seed)
27
+ {
28
+ _state = unchecked((uint)seed.GetHashCode());
29
+ _state = _state != 0 ? _state : 2463534242U;
23
30
  }
24
31
 
25
32
  [JsonConstructor]
@@ -31,12 +38,10 @@
31
38
 
32
39
  public override uint NextUint()
33
40
  {
34
- uint state = _state;
35
- state ^= state << 13;
36
- state ^= state >> 17;
37
- state ^= state << 5;
38
- _state = state;
39
- return state;
41
+ _state ^= _state << 13;
42
+ _state ^= _state >> 17;
43
+ _state ^= _state << 5;
44
+ return _state;
40
45
  }
41
46
 
42
47
  public override IRandom Copy()
@@ -12,6 +12,8 @@
12
12
  IComparable,
13
13
  IComparable<XorShiroRandom>
14
14
  {
15
+ public static XorShiroRandom Instance => ThreadLocalRandom<XorShiroRandom>.Instance;
16
+
15
17
  public override RandomState InternalState => new(_s0, _s1, _cachedGaussian);
16
18
 
17
19
  internal ulong _s0;
@@ -176,7 +176,7 @@
176
176
  int maxY = int.MinValue;
177
177
  foreach (AnimatedSpriteLayer layer in _layers)
178
178
  {
179
- if (!layer.frames.Any())
179
+ if (layer.frames.Length <= 0)
180
180
  {
181
181
  continue;
182
182
  }
@@ -210,7 +210,7 @@
210
210
 
211
211
  foreach (AnimatedSpriteLayer layer in _layers)
212
212
  {
213
- if (!layer.frames.Any())
213
+ if (layer.frames.Length <= 0)
214
214
  {
215
215
  continue;
216
216
  }