Friflo.Engine.ECS icon indicating copy to clipboard operation
Friflo.Engine.ECS copied to clipboard

Add ForeachEntity to ArchTypeQuery

Open Shadowblitz16 opened this issue 7 months ago • 3 comments

I am trying to make a wrapper around friflo...

However I want it to be fast using SIMD.

The problem I run into is with generics. In C# there is no such thing as varadic generics. So I need to either write alot of boiler plate code or build the query up one component at a time and use Get, Add, Remove methods of Entities.

I don't know how bad this in on performance but Ideally the entity and it's components would be local to the cache when using Get, Add, Remove methods.

One of the things I ran into was the non generic ArchetypeQuery has no ForEachEntity method even though I build it up with All, Any, and Without methods.

I don't even know if this code works. I hate how C# doesn't have aliases.

using System.Runtime.Intrinsics;
using Friflo.Engine.ECS;

namespace SpaceTest.Ecs;

public readonly struct Query
{
    private readonly ArchetypeQuery _query;
    internal Query(EntityStore store)
    {
        _query     = store.Query();
        Components = new ComponentQuery(this, _query);
        Tags       = new TagQuery(this, _query);
    }

    internal Query(ArchetypeQuery query)
    {
        _query     = query;
        Components = new ComponentQuery(this, _query);
        Tags       = new TagQuery(this, _query);
    }

    public readonly IComponentQuery Components;
    public readonly ITagQuery       Tags;

    public Query Disabled => new(_query.WithDisabled());

    public void ForEach(Action<Entity> action)
    {
        _query.Each()
        {
            
        }
    }

    public interface IComponentQuery
    {
        public Query AllOf <T>() where T : struct, IComponent;
        public Query AnyOf <T>() where T : struct, IComponent;
        public Query NoneOf<T>() where T : struct, IComponent;

    }
    public interface ITagQuery 
    {
        public Query AllOf <T>() where T : struct, ITag;
        public Query AnyOf <T>() where T : struct, ITag;
        public Query NoneOf<T>() where T : struct, ITag;
    }
    
    public readonly struct ComponentQuery( Query query, ArchetypeQuery archetypeQuery) : IComponentQuery
    {
        public Query AllOf <T>() where T : struct, IComponent
        {
            return new(archetypeQuery.AllComponents(ComponentTypes.Get<T>()));
        }
        public Query AnyOf <T>() where T : struct, IComponent
        {
            return new(archetypeQuery.AnyComponents(ComponentTypes.Get<T>()));
        }
        public Query NoneOf<T>() where T : struct, IComponent
        {
            return new(archetypeQuery.WithoutAllComponents(ComponentTypes.Get<T>()));
        }

    }
    public readonly struct TagQuery      ( Query query, ArchetypeQuery archetypeQuery) : ITagQuery
    {
        public Query AllOf <T>() where T : struct, ITag
        {
            return new(archetypeQuery.AllTags(Friflo.Engine.ECS.Tags.Get<T>()));
        }
        public Query AnyOf <T>() where T : struct, ITag
        {
            return new(archetypeQuery.AnyTags(Friflo.Engine.ECS.Tags.Get<T>()));
        }
        public Query NoneOf<T>() where T : struct, ITag
        {
            return new(archetypeQuery.WithoutAllTags(Friflo.Engine.ECS.Tags.Get<T>()));
        }

    }
}

Shadowblitz16 avatar Jul 26 '25 19:07 Shadowblitz16

Every ArchetypeQuery has a property Entities. This property is an enumerator. It can be used to iterate all entities matching the query.

foreach (var entity in query.Entities) {
    entity.GetComponent<MyComponent>.value = 42;
}

You can use this loop in your void ForEach(Action<Entity> action) method.

friflo avatar Jul 27 '25 10:07 friflo

Every ArchetypeQuery has a property Entities. This property is an enumerator. It can be used to iterate all entities matching the query.

foreach (var entity in query.Entities) { entity.GetComponent<MyComponent>.value = 42; }

You can use this loop in your void ForEach(Action<Entity> action) method.

Is there a way to do it with simd?

Shadowblitz16 avatar Jul 27 '25 22:07 Shadowblitz16

Is there a way to do it with simd?

Simd requires the use of generic ArchetypeQuery<...>. Those queries provide access to the components with SIMD operations. This access is available via ArchetypeQuery<...>.Chunks property.

See: https://friflo.gitbook.io/friflo.engine.ecs/documentation/query-optimization#query-vectorization-simd.

The non generic ArchetypeQuery cannot ensure that specific component chunks exist. So it cannot provide access via a Chunks property.

friflo avatar Jul 28 '25 05:07 friflo