Browse Source

Merge pull request #301 from Eideren/fp

feat: Add documentation on Flexible Processing
Vaclav Elias 1 year ago
parent
commit
c6ce3b19e3

+ 94 - 0
en/manual/engine/entity-component-system/flexible-processing.md

@@ -0,0 +1,94 @@
+# Flexible Processing
+
+This document expects the reader to be familiar with ECS, please take a look at [usage](usage.md) first.
+
+Handling components through [`EntityProcessor`](xref:Stride.Engine.EntityProcessor) may be too rigid in some cases, when the components 
+you're trying to process cannot share the same base implementation for example.
+
+[`Stride.Engine.FlexibleProcessing.IComponent<TProcessor, TThis>`](xref:Stride.Engine.FlexibleProcessing.IComponent`2) 
+provides similar features to [`EntityProcessor`](xref:Stride.Engine.EntityProcessor) while being more flexible on the component type, 
+this document covers some of the usage of this particular interface.
+
+The `IComponent` interface requires to type parameters, 
+- `TProcessor` which is your processor's type.
+- And `TThis` which is your component's type.
+
+While that last type may seem redundant, it is required to ensure your processor and 
+your implemented type are compatible.
+
+A summarised example satisfying those type constraint would look like so:
+```cs
+public class MyComponent : StartupScript, IComponent<MyComponent.MyProcessor, MyComponent>
+{
+    public class MyProcessor : IProcessor
+    {
+        public List<MyComponent> Components = new();
+
+        public void SystemAdded(IServiceRegistry registryParam) { }
+        public void SystemRemoved() { }
+
+        public void OnComponentAdded(MyComponent item) => Components.Add(item);
+        public void OnComponentRemoved(MyComponent item) => Components.Remove(item);
+    }
+}
+```
+
+The main difference compared to [`EntityProcessor`](xref:Stride.Engine.EntityProcessor) 
+is that [`IComponent`](xref:Stride.Engine.FlexibleProcessing.IComponent`2) is not limited to concrete types, your processor may operate on interfaces as well;
+```cs
+// Here, declaring the interface, which will be the type received by the processor
+public interface IInteractable : IComponent<IInteractable.ShapeProcessor, IInteractable>
+{
+    void Interact();
+    public class InteractableProcessor : IProcessor
+    {
+        // Process each IInteractable here
+        // Omitted method implementation for brievety
+    }
+}
+
+// Now any component implementing IInteractable will be processed by the ShapeProcessor
+public class Button : StartupScript, IInteractable
+{
+    public void Interact(){}
+}
+public class Character : SyncScript, IInteractable
+{
+    public void Interact(){}
+    public override void Update(){}
+}
+```
+
+## Updating Processors
+[`Processors`](xref:Stride.Engine.FlexibleProcessing.IComponent`2) do not receive any updates by default, you have to implement the [`IUpdateProcessor`](xref:Stride.Engine.FlexibleProcessing.IUpdateProcessor) or [`IDrawProcessor`](xref:Stride.Engine.FlexibleProcessing.IDrawProcessor) 
+interface to receive them:
+```cs
+public interface ISpecialTick : IComponent<ISpecialTick.Processor, ISpecialTick>
+{
+    void Tick();
+
+    public class Processor : IProcessor, IUpdateProcessor
+    {
+        public List<ISpecialTick> Components = new();
+
+        public void SystemAdded(IServiceRegistry registryParam) { }
+        public void SystemRemoved() { }
+
+        public void OnComponentAdded(ISpecialTick item) => Components.Add(item);
+        public void OnComponentRemoved(ISpecialTick item) => Components.Remove(item);
+
+        // The execution order of this Update, smaller values execute first compared to other IComponent Processors
+        public int Order => 0;
+
+        public void Update(GameTime gameTime)
+        {
+            foreach (var comp in Components)
+                comp.Tick();
+        }
+    }
+}
+```
+
+## Performance
+While it is more flexible, processing components as interfaces instead of concrete class may introduce some overhead.
+If the system you're writing is performance critical you should look into strategies to elide or reduce virtual calls in your hot path.

+ 1 - 0
en/manual/index.md

@@ -10,6 +10,7 @@ These pages contain information about how to use Stride, an open-source C# game
 ## Latest documentation
 
 ### Recent updates
+- <span class="badge text-bg-info">New</span> [ECS - Flexible Processing](engine/entity-component-system/flexible-processing.md) - Description and example of the Flexible Processing system
 - <span class="badge text-bg-info">Updated</span> [Linux - Setup and requirements](platforms/linux/setup-and-requirements.md) - Fedora OS option added
 - <span class="badge text-bg-success">New</span> [Scripts - Serialization](scripts/serialization.md) - Explanation of serialization
 - <span class="badge text-bg-info">Updated</span> [Scripts - Public properties and fields](scripts/public-properties-and-fields.md) - Content improvements and additions

+ 2 - 0
en/manual/toc.yml

@@ -118,6 +118,8 @@ items:
             href: engine/entity-component-system/usage.md
           - name: Manage entities
             href: engine/entity-component-system/managing-entities.md
+          - name: Flexible processing
+            href: engine/entity-component-system/flexible-processing.md
       - name: File system
         href: engine/file-system.md
       - name: Build pipeline