diff --git a/src/Arch.SourceGen/QueryGenerator.cs b/src/Arch.SourceGen/QueryGenerator.cs
index 3c4ea85..68425ec 100644
--- a/src/Arch.SourceGen/QueryGenerator.cs
+++ b/src/Arch.SourceGen/QueryGenerator.cs
@@ -39,6 +39,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
references.AppendLine("using System;");
references.AppendLine("using System.Runtime.CompilerServices;");
references.AppendLine("using CommunityToolkit.HighPerformance;");
+ references.AppendLine("using Arch.Core.Utils;");
references.AppendLine("namespace Arch.Core;");
references.AppendComponents(25);
references.AppendEntityComponents(25);
diff --git a/src/Arch/Arch.csproj b/src/Arch/Arch.csproj
index d5a5014..73e2fac 100644
--- a/src/Arch/Arch.csproj
+++ b/src/Arch/Arch.csproj
@@ -29,8 +29,7 @@ Added World.IsAlive(EntityReference);
Fixed bug where `World.TrimExcess` does not trim Recycled-Entities which results in an out of bounds exception sooner or later.
Fixed bug in JobScheduler which prevents a deadlock.
Moved CommandBuffer to Buffer namespace, might break references.
-CommandBuffer now accepts a world during playback, world in ctor was removed.
-CommandBuffer now triggers OnComponentRemoved events.
+CommandBuffer now accepts a world during playback, world in ctor was removed.
c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system;stride;unity;godot;
https://github.com/genaray/Arch
@@ -103,7 +102,7 @@ CommandBuffer now triggers OnComponentRemoved events.
-
+
diff --git a/src/Arch/Core/Chunk.cs b/src/Arch/Core/Chunk.cs
index ce0b262..69ed9d7 100644
--- a/src/Arch/Core/Chunk.cs
+++ b/src/Arch/Core/Chunk.cs
@@ -256,7 +256,7 @@ public T[] GetArray()
{
var index = Index();
Debug.Assert(index != -1 && index < Components.Length, $"Index is out of bounds, component {typeof(T)} with id {index} does not exist in this chunk.");
- ref var array = ref Components.DangerousGetReferenceAt(index);
+ var array = Components.DangerousGetReferenceAt(index);
return Unsafe.As(array);
}
@@ -288,7 +288,6 @@ public ref T GetFirst()
public partial struct Chunk
{
-
///
/// Sets or replaces a component for an index in the chunk.
/// This won't fire an event for .
diff --git a/src/Arch/Core/Enumerators.cs b/src/Arch/Core/Enumerators.cs
index 3977b87..2b72ef7 100644
--- a/src/Arch/Core/Enumerators.cs
+++ b/src/Arch/Core/Enumerators.cs
@@ -1,4 +1,5 @@
using Arch.Core.Extensions.Internal;
+using Arch.Core.Utils;
using CommunityToolkit.HighPerformance;
namespace Arch.Core;
diff --git a/src/Arch/Core/Utils/ReadOnlyRef.cs b/src/Arch/Core/Utils/ReadOnlyRef.cs
new file mode 100644
index 0000000..4557d41
--- /dev/null
+++ b/src/Arch/Core/Utils/ReadOnlyRef.cs
@@ -0,0 +1,71 @@
+using CommunityToolkit.HighPerformance.Helpers;
+
+namespace Arch.Core.Utils;
+
+#if NETSTANDARD2_1_OR_GREATER || NET6_0
+
+///
+/// The struct
+/// can store a readonly reference to a value of a specified type.
+///
+/// The type of value to reference.
+public readonly ref struct ReadOnlyRef
+{
+ ///
+ /// The 1-length instance used to track the target value.
+ ///
+ internal readonly ReadOnlySpan Span;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The readonly reference to the target value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyRef(in T value)
+ {
+ ref T r0 = ref Unsafe.AsRef(value);
+
+ this.Span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The pointer to the target value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe ReadOnlyRef(void* pointer)
+ : this(in Unsafe.AsRef(pointer))
+ {
+ }
+
+ ///
+ /// Gets the readonly reference represented by the current instance.
+ ///
+ public ref readonly T Value
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref MemoryMarshal.GetReference(this.Span);
+ }
+
+ ///
+ /// Implicitly converts a instance into a one.
+ ///
+ /// The input instance.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator ReadOnlyRef(Ref reference)
+ {
+ return new(in reference.Value);
+ }
+
+ ///
+ /// Implicitly gets the value from a given instance.
+ ///
+ /// The input instance.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator T(ReadOnlyRef reference)
+ {
+ return reference.Value;
+ }
+}
+
+#endif
diff --git a/src/Arch/Core/Utils/Ref.cs b/src/Arch/Core/Utils/Ref.cs
new file mode 100644
index 0000000..02be481
--- /dev/null
+++ b/src/Arch/Core/Utils/Ref.cs
@@ -0,0 +1,56 @@
+namespace Arch.Core.Utils;
+
+#if NETSTANDARD2_1_OR_GREATER || NET6_0
+
+///
+/// The strct
+/// can store a reference to a value of a specified type.
+///
+/// The type of value to reference.
+public readonly ref struct Ref
+{
+ ///
+ /// The 1-length instance used to track the target value.
+ ///
+ internal readonly Span Span;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The reference to the target value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Ref(ref T value)
+ {
+ this.Span = MemoryMarshal.CreateSpan(ref value, 1);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The pointer to the target value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe Ref(void* pointer)
+ : this(ref Unsafe.AsRef(pointer))
+ {
+ }
+
+ ///
+ /// Gets the reference represented by the current instance.
+ ///
+ public ref T Value
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref MemoryMarshal.GetReference(this.Span);
+ }
+
+ ///
+ /// Implicitly gets the value from a given instance.
+ ///
+ /// The input instance.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator T(Ref reference)
+ {
+ return reference.Value;
+ }
+}
+#endif