stride3d / stride

Stride Game Engine (formerly Xenko)
https://stride3d.net
MIT License
6.32k stars 917 forks source link

feat: FlexibleProcessing as alternative to EntityComponentProcessor for lightweight, abstract and typesafe processing in idiomatic c# #2331

Closed Eideren closed 1 week ago

Eideren commented 1 week ago

PR Details

This PR enables building of processors from interfaces, enabling unrelated types to be processed by the same system.

The current processor implementation force users to derive from the same root type to build their logic, this does not work well with the fact that we have different 'Script' variant which have all their specific use. It becomes a composition issue when more abstract concepts, like event functions, are introduced.

A good example of that is when components should manipulate physics object, it is preferable to do so in lockstep with the physics engine's tick. But those component may also require an async or update loop to process inputs. We would be forced to either create another new 'script' type which users have to derive from which would split their logic between two different types, or tie it to one of the other 'script', both solutions would introduce additional overhead while not being practical.

Example usage:

public class MyComponent : StartupScript, IPhysicsTick
{
    public void Tick(float deltaTime){ }
}

public interface IPhysicsTick : IComponent<IPhysicsTick.PhysicsTickProc, IPhysicsTick>
{
    void Tick(float deltaTime);
    public class PhysicsTickProc : IProcessor, IUpdateProcessor
    {
        public int Order => 5;
        public List<IPhysicsTick> Components = new();
        public void SystemAdded(IServiceRegistry registryParam) { }
        public void SystemRemoved() { }
        public void OnComponentAdded(IPhysicsTick item) => Components.Add(item);
        public void OnComponentRemoved(IPhysicsTick item) => Components.Remove(item);
        public void Update(GameTime gameTime)
        {
            foreach (var comp in Components)
                comp.Tick((float)gameTime.Elapsed.TotalSeconds);
        }
    }
}

Implementer may include additional processing during the OnComponentAdded step to reduce or elide virtual calls

Related Issue

Introduced for usage in #2131

Types of changes

Checklist

Eideren commented 1 week ago

Thanks a bunch for reviewing this @manio143