Skip to content

Commit

Permalink
Add ComponentIndex<,>
Browse files Browse the repository at this point in the history
  • Loading branch information
friflo committed Dec 1, 2024
1 parent 3893d1f commit 9d007ca
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 11 deletions.
63 changes: 63 additions & 0 deletions src/ECS/Index/ComponentIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using Friflo.Engine.ECS.Index;

// ReSharper disable once CheckNamespace
namespace Friflo.Engine.ECS;

public readonly struct ComponentIndex <TIndexedComponent,TValue>
where TIndexedComponent : struct, IIndexedComponent<TValue>
{
private readonly AbstractComponentIndex<TValue> index;

internal ComponentIndex(AbstractComponentIndex<TValue> index) {
this.index = index;
}

/// <summary>
/// Return the entities having a component with the passed component value.<br/>
/// Executes in O(1) with default index.
/// </summary>
public Entities this[TValue value] => index.GetHasValueEntities(value);

/// <summary>
/// Returns all indexed component values of the passed <typeparamref name="TIndexedComponent"/> type.<br/>
/// Executes in O(1). Each value in the returned list is unique. See remarks for additional infos.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>
/// The returned collection changes when indexed component values are updated, removed or added.
/// </item>
/// <item>
/// To get the entities having a specific component value use <see cref="this[TValue]"/>.
/// </item>
/// <item>
/// If <typeparamref name="TValue"/> is a class all collection values are not null.<br/>
/// Use <see cref="this[TValue]"/> to check if null is indexed.
/// </item>
/// </list>
/// </remarks>
public IReadOnlyCollection<TValue> Values => index.IndexedComponentValues;

/// <summary>
/// Returns all entities linked by the specified <see cref="ILinkComponent"/> type.<br/>
/// Executes in O(1). Each entity in the returned list is unique. See remarks for additional infos.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>
/// The returned collection changes when component link values are updated, removed or added.
/// </item>
/// <item>
/// To get the entities linking a specific entity use <see cref="IndexExtensions.GetIncomingLinks{TComponent}"/>.<br/>
/// </item>
/// <item>
/// The method is a specialized version of <see cref="Values"/><br/>
/// using <c> TIndexedComponent = ILinkComponent</c> and <c>TValue = Entity</c>.
/// </item>
/// </list>
/// </remarks>
public IReadOnlyCollection<Entity> LinkedEntities => ((AbstractComponentIndex<Entity>)(object)index).IndexedComponentValues;

public override string ToString() => $"Count: {Values.Count}";
}
2 changes: 1 addition & 1 deletion src/ECS/Index/IIndexedComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Friflo.Engine.ECS;
/// </item>
/// <item>
/// Return all entities with a component field of a specific value. <br/>
/// See <see cref="IndexExtensions.GetEntitiesWithComponentValue{TIndexedComponent,TValue}"/>.
/// See <see cref="ComponentIndex{TIndexedComponent,TValue}.this[TValue]"/>.
/// </item>
/// <item>
/// Return a collection of all unique component values.<br/>
Expand Down
22 changes: 18 additions & 4 deletions src/ECS/Index/IndexExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ public static EntityLinks<TComponent> GetIncomingLinks<TComponent>(this Entity t

#region EntityStore
/// <summary>
/// Returns the index for indexed components to search entities with a specific component value in O(1).<br/>
/// Executes in O(1).
/// </summary>
public static ComponentIndex<TIndexedComponent,TValue> ComponentIndex<TIndexedComponent, TValue>(this EntityStore store)
where TIndexedComponent: struct, IIndexedComponent<TValue>
{
var index = (AbstractComponentIndex<TValue>)StoreIndex.GetIndex(store, StructInfo<TIndexedComponent>.Index);
return new ComponentIndex<TIndexedComponent, TValue>(index);
}

/// <summary>
/// Obsolete: Use <see cref="ComponentIndex{TIndexedComponent,TValue}.this[TValue]"/><br/>
/// Return the entities with the passed component value.<br/>
/// Executes in O(1) with default index.
/// </summary>
Expand All @@ -42,6 +54,7 @@ public static Entities GetEntitiesWithComponentValue<TComponent, TValue>(this En
}

/// <summary>
/// Obsolete: Use <see cref="ComponentIndex{TIndexedComponent,TValue}.Values"/><br/>
/// Returns all indexed component values of the passed <typeparamref name="TComponent"/> type.<br/>
/// Executes in O(1). Each value in the returned list is unique. See remarks for additional infos.
/// </summary>
Expand All @@ -51,11 +64,11 @@ public static Entities GetEntitiesWithComponentValue<TComponent, TValue>(this En
/// The returned collection changes when indexed component values are updated, removed or added.
/// </item>
/// <item>
/// To get the entities having a specific component value use <see cref="GetEntitiesWithComponentValue{TComponent,TValue}"/>.
/// To get the entities having a specific component value use <see cref="ComponentIndex{TIndexedComponent,TValue}.this[TValue]"/>.
/// </item>
/// <item>
/// If <typeparamref name="TValue"/> is a class all collection values are not null.<br/>
/// Use <see cref="GetEntitiesWithComponentValue{TComponent,TValue}"/> to check if null is referenced.
/// Use <see cref="ComponentIndex{TIndexedComponent,TValue}.this[TValue]"/> to check if null is referenced.
/// </item>
/// </list>
/// </remarks>
Expand All @@ -67,6 +80,7 @@ public static IReadOnlyCollection<TValue> GetAllIndexedComponentValues<TCompone
}

/// <summary>
/// Obsolete: Use <see cref="ComponentIndex{TIndexedComponent,TValue}.LinkedEntities"/><br/>
/// Returns all entities linked by the specified <see cref="ILinkComponent"/> type.<br/>
/// Executes in O(1). Each entity in the returned list is unique. See remarks for additional infos.
/// </summary>
Expand All @@ -79,8 +93,8 @@ public static IReadOnlyCollection<TValue> GetAllIndexedComponentValues<TCompone
/// To get the entities linking a specific entity use <see cref="GetIncomingLinks{TComponent}"/>.<br/>
/// </item>
/// <item>
/// The method is a specialized version of <see cref="GetAllIndexedComponentValues{TComponent,TValue}"/><br/>
/// using <c> TComponent = ILinkComponent</c> and <c>TValue = Entity</c>.
/// The method is a specialized version of <see cref="ComponentIndex{TIndexedComponent,TValue}.Values"/><br/>
/// using <c> TIndexedComponent = ILinkComponent</c> and <c>TValue = Entity</c>.
/// </item>
/// </list>
/// </remarks>
Expand Down
File renamed without changes.
6 changes: 4 additions & 2 deletions src/Tests/ECS/Examples/Component_Types.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Friflo.Engine.ECS;
using NUnit.Framework;

// ReSharper disable UnusedVariable
// ReSharper disable NotAccessedField.Local
// ReSharper disable ArrangeTypeMemberModifiers
// ReSharper disable InconsistentNaming
Expand Down Expand Up @@ -171,12 +172,13 @@ struct Player : IIndexedComponent<string> // indexed field type: string
public static void IndexedComponents()
{
var store = new EntityStore();
var index = store.ComponentIndex<Player,string>();
for (int n = 0; n < 1000; n++) {
var entity = store.CreateEntity();
entity.AddComponent(new Player { name = $"Player-{n,0:000}"});
}
// get all entities where Player.name == "Player-001". O(1)
store.GetEntitiesWithComponentValue<Player,string>("Player-001"); // Count: 1
var entities = index["Player-001"]; // Count: 1

// return same result as lookup using a Query(). O(1)
store.Query().HasValue <Player,string>("Player-001"); // Count: 1
Expand All @@ -186,7 +188,7 @@ public static void IndexedComponents()
store.Query().ValueInRange<Player,string>("Player-000", "Player-099"); // Count: 100

// get all unique Player.name's. O(1)
store.GetAllIndexedComponentValues<Player,string>(); // Count: 1000
var values = index.Values; // Count: 1000
}

#endregion
Expand Down
10 changes: 6 additions & 4 deletions src/Tests/ECS/Github/Test_GitHub_15.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@ public static void Test_GitHub_15_update_indexed_components_on_deserialization()
var entityB = store.CreateEntity(2);
entityB.AddComponent(new IndexedInt { value = 12 });

AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(11).Count);
AreEqual(2, store.GetAllIndexedComponentValues<IndexedInt,int>().Count);
var index = store.ComponentIndex<IndexedInt,int>();
AreEqual(1, index[11].Count);
AreEqual(2, index.Values.Count);

using var stream = new MemoryStream();
var serializer = new EntitySerializer();
serializer.WriteStore(store, stream);

var readStore = new EntityStore();
var readIndex = readStore.ComponentIndex<IndexedInt,int>();
serializer.ReadIntoStore(readStore, stream);
AreEqual(2, readStore.Count);
AreEqual(1, readStore.GetEntitiesWithComponentValue<IndexedInt,int>(11).Count);
AreEqual(2, readStore.GetAllIndexedComponentValues<IndexedInt,int>().Count);
AreEqual(1, readIndex[11].Count);
AreEqual(2, readIndex.Values.Count);
}
}
}

0 comments on commit 9d007ca

Please sign in to comment.