{"id":13533909,"url":"https://github.com/Doraku/DefaultEcs","last_synced_at":"2025-04-01T22:31:01.772Z","repository":{"id":41420203,"uuid":"121024896","full_name":"Doraku/DefaultEcs","owner":"Doraku","description":"Entity Component System framework aiming for syntax and usage simplicity with maximum performance for game development.","archived":false,"fork":false,"pushed_at":"2024-03-01T16:21:19.000Z","size":5715,"stargazers_count":696,"open_issues_count":19,"forks_count":62,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-03-15T11:28:15.019Z","etag":null,"topics":["c-sharp","dotnet","ecs","entity-component-system","game","game-development","game-engine","gamedev"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit-0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Doraku.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":"https://paypal.me/LaszloPaillat"}},"created_at":"2018-02-10T15:00:05.000Z","updated_at":"2025-03-13T06:24:20.000Z","dependencies_parsed_at":"2024-04-13T10:05:54.483Z","dependency_job_id":null,"html_url":"https://github.com/Doraku/DefaultEcs","commit_stats":null,"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Doraku%2FDefaultEcs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Doraku%2FDefaultEcs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Doraku%2FDefaultEcs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Doraku%2FDefaultEcs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Doraku","download_url":"https://codeload.github.com/Doraku/DefaultEcs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246720458,"owners_count":20822907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","dotnet","ecs","entity-component-system","game","game-development","game-engine","gamedev"],"created_at":"2024-08-01T07:01:24.203Z","updated_at":"2025-04-01T22:31:01.259Z","avatar_url":"https://github.com/Doraku.png","language":"C#","readme":"﻿![DefaultEcs](https://github.com/Doraku/DefaultEcs/blob/master/image/DefaultEcsLogo.png)\nDefaultEcs is an [Entity Component System](https://en.wikipedia.org/wiki/Entity_component_system) framework which aims to be accessible with little constraints while retaining as much performance as possible for game development.\n\n[![NuGet](https://buildstats.info/nuget/DefaultEcs)](https://www.nuget.org/packages/DefaultEcs)\n[![Coverage Status](https://coveralls.io/repos/github/Doraku/DefaultEcs/badge.svg?branch=master)](https://coveralls.io/github/Doraku/DefaultEcs?branch=master)\n![continuous integration status](https://github.com/doraku/defaultecs/workflows/continuous%20integration/badge.svg)\n[![preview package](https://img.shields.io/badge/preview-package-blue?style=flat\u0026logo=github)](https://github.com/Doraku/DefaultEcs/packages/26448)\n[![Join the chat at https://gitter.im/Doraku/DefaultEcs](https://badges.gitter.im/Doraku/DefaultEcs.svg)](https://gitter.im/Doraku/DefaultEcs?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n- [API documentation](./documentation/api/DefaultEcs.md 'API documentation')\n- [FAQ](./documentation/FAQ.md 'Frequently Asked Questions')\n- [Benchmarks](https://github.com/Doraku/Ecs.CSharp.Benchmark)\n\u003ca/\u003e\n\n- [Requirements](#requirements)\n- [Versioning](#versioning)\n- [Analyzer](#analyzer)\n- [Overview](#overview)\n  - [World](#world)\n    - [Message](#message)\n  - [Entity](#entity)\n  - [Component](#component)\n    - [Singleton](#singleton)\n    - [Resource](#resource)\n  - [Query](#query)\n    - [AsPredicate](#aspredicate)\n    - [AsEnumerable](#asenumerable)\n    - [AsSet](#asset)\n    - [AsMap](#asmap)\n    - [AsMultiMap](#asmultimap)\n  - [System](#system)\n    - [ISystem](#isystem)\n    - [ActionSystem](#actionsystem)\n    - [SequentialSystem](#sequentialsystem)\n    - [ParallelSystem](#parallelsystem)\n    - [AComponentSystem](#acomponentsystem)\n    - [AEntitySetSystem](#aentitysetsystem)\n    - [AEntityMultiMapSystem](#aentitymultimapsystem)\n  - [Threading](#threading)\n    - [IParallelRunnable](#iparallelrunnable)\n    - [IParallelRunner](#iparallelrunner)\n    - [DefaultParallelRunner](#defaultparallelrunner)\n  - [Command](#command)\n  - [Serialization](#serialization)\n    - [TextSerializer](#textserializer)\n    - [BinarySerializer](#binaryserializer)\n- [Extension](#extension)\n- [Sample](#sample)\n- [Projects using DefaultEcs](#projects-using-defaultecs)\n- [Dependencies](#dependencies)\n\n# Requirements\nDefaultEcs heavily uses features from C#7.0 and Span from the System.Memory package, compatible from .NETStandard 1.1.  \nFor development, a C#9.0 compatible environment, net framework 4.8, net6.0 are required to build and run all tests (it is possible to disable some targets in the test project if needed).  \nIt is possible to use DefaultEcs in Unity (check [FAQ](./documentation/FAQ.md#unity)).\n\n# Versioning\nThis is the current strategy used to version DefaultEcs: v0.major.minor\n- 0: DefaultEcs is still in heavy development and although a lot of care is given not to break the current API, it can still happen\n- major: incremented when there is a breaking change (reset minor number)\n- minor: incremented when there is a new feature or a bug fix\n\n# Analyzer\nTo help development with DefaultEcs, there is a roslyn analyzer which provides code generation and warnings against potential bad usage. It can be found [here](https://github.com/Doraku/DefaultEcs.Analyzer).\n\n# Overview\n## World\nThe World class acts as the central hub to create entities, query specific entities, get all components for a given type or publish and subscribe to messages that can be used to communicate across types.  \nMultiple World objects can be used in parallel, each instance being thread-safe from one another but operations performed on a single instance and all of its created items should be thought as non thread-safe. Depending on what is done, it is still possible to process operations concurrently to optimise performance.\n\nWorlds are created as such:\n```csharp\nWorld world = new World();\n```\n\nThe World class also implements the `IDisposable` interface so you can easily clean up resources by disposing it.\n\n### Message\nIt is possible to send and receive message transiting in a World:\n```csharp\nvoid On(in bool message) { }\n\n// the method On will be called back every time a bool object is published\n// it is possible to use any type\nworld.Subscribe\u003cbool\u003e(On);\n\nworld.Publish(true);\n```\n\nIt is also possible to subscribe to multiple methods of an instance by using the `SubscribeAttribute`:\n```csharp\npublic class Dummy\n{\n    [Subscribe]\n    void On(in bool message) { }\n\t\n    [Subscribe]\n    void On(in int message) { }\n\t\n    void On(in string message) { }\n}\n\nDummy dummy = new Dummy();\n\n// this will subscribe the decorated methods only\nworld.Subscribe(dummy);\n\n// the dummy bool method will be called\nworld.Publish(true);\n\n// but not the string one as it dit not have the SubscribeAttribute\nworld.Publish(string.Empty);\n```\n\nNote that the Subscribe method returns an `IDisposable` object acting as a subscription. To unsubscribe, simply dispose this object.\n\n## Entity\nEntities are simple structs acting as keys to manage components.\n\nEntities are created as such:\n```csharp\nEntity entity = world.CreateEntity();\n```\n\nYou should not store entities yourself and rely as much as possible on the returned objects from a world query as those will be updated accordingly to component changes.  \nTo clear an entity, simply call its `Dispose` method.\n```csharp\nentity.Dispose();\n```\n\nOnce disposed, you should not use the entity again. If you need a safeguard, you can check the `IsAlive` property:\n```csharp\n#if DEBUG\nif (!entity.IsAlive)\n{\n    // something is wrong\n}\n#endif\n```\n\nYou can also make an entity act as if it was disposed so it is removed from world queries while keeping all its components, this is useful when you need to activate/deactivate an entity from your game logic:\n```csharp\nentity.Disable();\n\n// this will return false;\nentity.IsEnabled();\n\nentity.Enable();\n\n// and now it will return true;\nentity.IsEnabled();\n```\n\n## Component\nComponents are not restricted by any heritage hierarchy. It is recommended that component objects only hold data and be structs to generate as little as possible garbage and to have them contiguous in memory, but you can use classes and interfaces too:\n```csharp\npublic struct Example\n{\n    public float Value;\n}\n\npublic interface IExample\n{\n    float Value { get; set; }\n}\n\npublic class CExample : IExample\n{\n    public float Value { get; set; }\n}\n```\n\nTo reduce memory usage, it is possible to set a maximum capacity for a given component type. If nothing is set, then the maximum entity count of the world will be used. This call needs to be done before any component of the specified type is set:\n```csharp\nworld.SetMaxCapacity\u003cExample\u003e(42);\n```\n\nComponents can live on two levels, on entities or directly on the world itself:\n```csharp\nentity.Set\u003cint\u003e(42);\n\n// check if the entity has an int component\nif (entity.Has\u003cint\u003e())\n{\n    // get the int component of the entity\n    if (--entity.Get\u003cint\u003e() \u003c= 0)\n    {\n        // remove the int component from the entity\n        entity.Remove\u003cint\u003e();\n    }\n}\n\n// all those methods are also available on the World type\nworld.Set\u003cint\u003e(42);\n\nif (world.Has\u003cint\u003e() \u0026\u0026 --world.Get\u003cint\u003e() \u003c= 0)\n{\n    world.Remove\u003cint\u003e();\n}\n\n// be careful that the component type is specific to the method generic parameter type, not the component type\nentity.Set\u003cIExample\u003e(new CExample());\n\n// this will return false as the component type previously set is IExample, not CExample\nentity.Has\u003cCExample\u003e();\n```\n\nIt is possible to share the same component value between entities or even the world. This is useful if you want to update a component value on multiple entities with a single call:\n```csharp\nreferenceEntity.Set\u003cint\u003e(42);\nentity.SetSameAs\u003cint\u003e(referenceEntity);\n\nreferenceEntity.Set\u003cint\u003e(1337);\n// the value for entity will also be 1337\nentity.Get\u003cint\u003e();\n\nworld.Set\u003cstring\u003e(\"hello\");\nentity.SetSameAsWorld\u003cstring\u003e();\nworld.Set\u003cstring\u003e(\"world\");\n// the value for entity will also be \"world\"\nentity.Get\u003cstring\u003e();\n```\n\nIf the component is removed from the entity used as reference or the world, it will not remove the component from the other entities using the same component.\n\nComponents on entities can also be disabled and re-enabled. This is useful if you want to quickly make an entity act as if its component composition has changed so it is picked up by world queries without paying the price of actually removing the component:\n```csharp\nentity.Disable\u003cint\u003e();\n\n// this still return true\nentity.Has\u003cint\u003e();\n// but this will return false\nentity.IsEnabled\u003cint\u003e();\n\nentity.Enable\u003cint\u003e();\n// now this will return true\nentity.IsEnabled\u003cint\u003e();\n```\n\nThere are two ways to update a component value, either use the `Set\u003cT\u003e(newValue)` method or set/edit the value returned by `Get\u003cT\u003e()`. One major difference between the two is that `Set\u003cT\u003e(newValue)` will also notify internal queries that the component value has changed:\n```csharp\nentity.Set\u003cint\u003e(42);\n\n// we set a new value\nentity.Set\u003cint\u003e(1337);\n\nref int component = ref entity.Get\u003cint\u003e();\n// we have actually changed the component value but the internal queries have not been notified.\ncomponent = 42;\n\n// we can notify manually by calling this method if we need to.\nentity.NotifyChanged\u003cint\u003e();\n```\n\n### Singleton\nBy combining the previous calls, it is possible to define a component type as a singleton:\n```csharp\n// only one component value for int can exist on this world\nworld.SetMaxCapacity\u003cint\u003e(1);\n\n// and it is used by the world\nworld.Set\u003cint\u003e(42);\n\n// entity.Set\u003cint\u003e(10); this line would throw\n// the only way to set the component on entities would be to use the world component\nentity.SetSameAsWorld\u003cint\u003e();\n```\n\n### Resource\nNot all components can easily be serialized to be loaded from data files (texture, sound, ...). To help with the handling of those cases, helper types are provided to provide a way to load managed resources, shared across entities and even worlds, and automatically dispose them once no entity using them exists anymore.  \nTo set up a managed resource on an entity, the type `ManagedResource\u003cTInfo, TResource\u003e` needs to be set as a component where `TInfo` is a type used as a single identifier for a single resource and information needed to load it, and `TResource` is the type of the resource.  \nShould multiple resources of the same type be needed on a single entity, it is also possible to set the type `ManagedResource\u003cTInfo[], TResource\u003e` as a component.  \nIf the `ManagedResource` component is removed from the entity or the entity holding it is disposed, the internal reference count on the resource will decrease and it will be disposed if zero is reached.  \nTo actually load the resource, an implementation of the class `AResourceManager\u003cTInfo, TResource\u003e` is needed as shown in the example:\n```csharp\n// TInfo is string, the name of the texture and TResource is Texture2D\npublic sealed class TextureResourceManager : AResourceManager\u003cstring, Texture2D\u003e\n{\n    private readonly GraphicsDevice _device;\n    private readonly ITextureLoader _loader;\n\n    // ITextureLoader is the actual loader, not shown here\n    public TextureResourceManager(GraphicsDevice device, ITextureLoader loader)\n    {\n        _device = device;\n        _loader = loader;\n    }\n\n    // this will only be called if the texture with the key info has never been loaded yet or it has previously been disposed because it was not used anymore\n    protected override Texture2D Load(string info) =\u003e _loader.Load(_device, info);\n\n    // this is the callback method where the entity with the ManagedResource\u003cstring, Texture2D\u003e component is set, the TInfo and the resource are given do act as needed\n    protected override void OnResourceLoaded(in Entity entity, string info, Texture2D resource)\n    {\n        // here we just set the texture to a field of an other component of the entity which contains all the information needed to draw it (position, size, origin, rotation, texture, ...)\n        entity.Get\u003cDrawInfo\u003e().Texture = resource;\n    }\n}\n\n// we simply set the special component like any other one\nentity.Set(new ManagedResource\u003cstring, Texture2D\u003e(\"square.png\"));\n\n// or we could set multiple resources like this\nentity.Set(new ManagedResource\u003cstring[], Texture2D\u003e(new [] { \"square.png\", \"circle.png\" }));\n\n// you can also use the helper class\nentity.Set(ManagedResource\u003cTexture2D\u003e.Create(\"square.png\")); // set up a ManagedResource\u003cstring, Texture2D\u003e\nentity.Set(ManagedResource\u003cTexture2D\u003e.Create(\"square.png\", \"circle.png\")); // set up a ManagedResource\u003cstring[], Texture2D\u003e\n\n// this is how to set up a resource manager on a world, it will process all currently existing entities with the special component type, and react to all future entities also\ntextureResourceManager.Manage(_world);\n```\nThis feature only cares for entity components, not the world components.\n\n## Query\nTo perform operations, systems should query entities from the world. This is performed by requesting entities through the world and using the fluent API to create rules\n```csharp\nworld\n    .GetEntities() // this is the starting point of a query, you can also query disabled entities with GetDisabledEntities()\n    .With\u003cint\u003e().With\u003cdouble\u003e() // require that entities have both an int and double components\n    .Without\u003cstring\u003e().Without\u003cchar\u003e() // require that entities do not have both a string nor a char component\n    .WithEither\u003clong\u003e().Or\u003culong\u003e() // require that entities have either a long or ulong component\n    .WithoutEither\u003cshort\u003e().Or\u003cushort\u003e() // require that entities do not have either a short or a ushort component\n    ...\n    .WhenAdded\u003cint\u003e() // require that entities have just been added an int component\n    .WhenChanged\u003cint\u003e() // require that entities have their int component changed\n    .WhenRemoved\u003cstring\u003e() // require that entities have their string component removed\n    ...\n```\n\nOnce you are satisfied with the rules you can then finish the query by choosing how to get the entities:\n### AsPredicate\n```csharp\nworld\n    .GetEntities()\n    ...\n    .AsPredicate();\n```\nGets a `Predicate\u003cEntity\u003e` that checks for your declared rules. `When...` rules are ignored.  \nThis is useful if you need to check for some entity component composition.\n\n### AsEnumerable\n```csharp\nworld\n    .GetEntities()\n    ...\n    .AsEnumerable();\n```\nGets an `IEnumerable\u003cEntity\u003e` that when enumerated returns all the entities respecting your declared rules. `When...` rules are ignored.  \nThis is useful if you need to do an initialisation on specific entities, this should not be used in a hot path.\n\n### AsSet\n```csharp\nworld\n    .GetEntities()\n    ...\n    .AsSet();\n```\nGets an `EntitySet` containing all entities respecting your declared rules. Its content is cached for fast access and is automatically updated as you change your entity component composition.\nIf `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.\nThis is the base type used in the provided system implementation.\n\n### AsMap\n```csharp\nworld\n    .GetEntities()\n    ...\n    .AsMap\u003cTKey\u003e();\n```\nGets an `EntityMap\u003cTKey\u003e` which maps a single entity with a component type of `TKey` value. Its content is cached for fast access and is automatically updated as you change your entity component composition.\nIf `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.\nThis is useful if you need O(1) access to an entity based on a key.\n\n### AsMultiMap\n```csharp\nworld\n    .GetEntities()\n    ...\n    .AsMultiMap\u003cTKey\u003e();\n```\nGets an `EntityMultiMap\u003cTKey\u003e` which maps multiple entities with a component type of `TKey` value. Its content is cached for fast access and is automatically updated as you change your entity component composition.\nIf `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.\nThis is useful if you need O(1) access to entities based on a key.\n\n## System\nAlthough there is no obligation to use them, a set of base classes is provided to help with the creation of systems:\n### ISystem\nThis is a base interface for all systems. It exposes an `Update` method and an `IsEnabled` property. In all derived types provided in DefaultEcs, the responsibility to check this property is handled by the callee, not the caller. It is set to true by default.\n\n### ActionSystem\nThis class is used to quickly make a system with a given custom action to be called on every update:\n```csharp\nprivate void Exit(float elapsedTime)\n{\n    if (EscapedIsPressed)\n    {\n        // escape code\n    }\n}\n\n...\n\nISystem\u003cfloat\u003e system = new ActionSystem\u003cfloat\u003e(Exit);\n\n...\n\n// this will call the Exit method as a system\nsystem.Update(elapsedTime);\n```\n\n### SequentialSystem\nThis class is used to easily create a list of systems to be updated in a sequential order:\n```csharp\nISystem\u003cfloat\u003e system = new SequentialSystem\u003cfloat\u003e(\n    new InputSystem(),\n    new AISystem(),\n    new PositionSystem(),\n    new DrawSystem()\n);\n...\n\n// this will call the systems in order: InputSystem -\u003e AISystem -\u003e PositionSystem -\u003e DrawSystem\n```\n\n### ParallelSystem\nThis class is used to easily create a list of systems to be updated in parallel:\n```csharp\nISystem\u003cfloat\u003e system = new ParallelSystem\u003cfloat\u003e(\n    new DefaultParallelRunner(Environment.ProcessorCount),    \n    new System1(),\n    new System2(),\n    new System3(),\n    new System4()\n);\n...\n\n// this will call the systems in parallel\n```\n\n### AComponentSystem\nThis is a base class to create systems that update based on a specific component type from a given World:\n```csharp\npublic class DrawSystem : AComponentSystem\u003cfloat, DrawInfo\u003e\n{\n    private readonly SpriteBatch _batch;\n    private readonly Texture2D _square;\n\n    public DrawSystem(SpriteBatch batch, Texture2D square, World world)\n        : base(world)\n    {\n        _batch = batch;\n        _square = square;\n    }\n\n    protected override void PreUpdate(float elapsedTime)\n    {\n        _batch.Begin();\n    }\n\n    protected override void Update(float elapsedTime, ref DrawInfo component)\n    {\n        _batch.Draw(_square, component.Destination, component.Color);\n    }\n\n    protected override void PostUpdate(float elapsedTime)\n    {\n        _batch.End();\n    }\n}\n```\nNote that components do not have to reside on entities to be processed by such a system (world components) and their enabled/disabled state on their owning entity is ignored.\n\n### AEntitySetSystem\nThis is a base class to create system to update a given `EntitySet`:\n```csharp\npublic sealed class VelocitySystem : AEntitySetSystem\u003cfloat\u003e\n{\n    public VelocitySystem(World world, IParallelRunner runner)\n        : base(world.GetEntities().With\u003cVelocity\u003e().With\u003cPosition\u003e().AsSet(), runner)\n    {\n    }\n\n    protected override void Update(float elapsedTime, in Entity entity)\n    {\n        ref Velocity velocity = ref entity.Get\u003cVelocity\u003e();\n        ref Position position = ref entity.Get\u003cPosition\u003e();\n\n        Vector2 offset = velocity.Value * elapsedTime;\n\n        position.Value.X += offset.X;\n        position.Value.Y += offset.Y;\n    }\n}\n```\n\nIt is also possible to declare the component types by using the `WithAttribute` and `WithoutAttribute` on the system type:\n```csharp\n[With(typeof(Velocity)]\n[With(typeof(Position)]\npublic sealed class VelocitySystem : AEntitySetSystem\u003cfloat\u003e\n{\n    public VelocitySystem(World world, IParallelRunner runner)\n        : base(world, runner)\n    {\n    }\n\n    protected override void Update(float elapsedTime, in Entity entity)\n    {\n        ref Velocity velocity = ref entity.Get\u003cVelocity\u003e();\n        ref Position position = ref entity.Get\u003cPosition\u003e();\n\n        Vector2 offset = velocity.Value * elapsedTime;\n\n        position.Value.X += offset.X;\n        position.Value.Y += offset.Y;\n    }\n}\n```\n\n### AEntityMultiMapSystem\nThis is a base class to create systems that update a given `EntityMultiMap`. If the key implements `IComparable`, the keys will be processed in a sorted order. Otherwise you can provide your own key order:\n```csharp\npublic sealed class LayerSystem : AEntityMultiMapSystem\u003cfloat, Layer\u003e\n{\n    // we define the layer order\n    private static readonly Layer[] _layers = new[]\n    {\n        Layer.Background,\n        Layer.Unit,\n        Layer.Particle,\n        Layer.Ui\n    };\n\n    private readonly SpriteBatch _batch;\n    \n    public LayerSystem(SpriteBatch batch, World world)\n        : base(world.GetEntities().With\u003cDrawInfo\u003e().AsMultiMap\u003cLayer\u003e())\n    {\n        _batch = batch;\n    }\n\n    protected override Span\u003cLayer\u003e GetKeys()\n    {\n        return _layers.AsSpan();\n    }\n\n    // entities will be drawn layer by layer\n    protected override void Update(float state, in Layer key, in Entity entity)\n    {\n        _batch.Draw(drawInfo.Texture, drawInfo.Position, null, drawInfo.Color, drawInfo.Rotation, drawInfo.Origin, drawInfo.Size, SpriteEffects.None, 0f);\n    }\n}\n```\n\n## Threading\nSome systems are compatible with multi-threaded execution: `ParallelSystem`, `AEntitySetSystem` and `AComponentSystem`. This is done by passing an `IParallelRunner` to their respective constructors:\n```csharp\nIParallelRunner runner = new DefaultParallelRunner(Environment.ProcessorCount);\n\nISystem\u003cfloat\u003e system = new VelocitySystem(world, runner);\n\n// this will process the update on Environment.ProcessorCount threads\nsystem.Update(elapsedTime);\n```\nIt is safe to run a system with multi-threading when:\n* for an AEntitySetSystem or an AEntityMultiMapSystem\n  * each entity with no dependency to another entity can be safely updated separately\n  * there is no entity component composition altering operation done on a world nor on an entity. Here is a list of non thread-safe operations you shouldn't do, see [Command](#Overview_Command) for how to perform such operations safely:\n    * Creating and disposing an entity\n    * Removing a component from an entity/world\n    * Enabling or disabling an entity\n    * Enabling or disabling a component on an entity\n    * Calling `SetMaxCapacity` on a world\n    * Calling `Optimize` on a world\n    * Calling `TrimExcess` on a world\n    * Calling `Set`/`SameAs`/`SameAsWorld` on an entity/world\n    * Calling `NotifyChanged` on an entity\n    * Calling `CopyTo` on an entity\n* for an `AComponentSystem`\n  * each component with no dependency to another component can be safely updated separately\n\n### IParallelRunnable\nThis interface allows for creation of a custom parallelisable process by an `IParallelRunner`:\n```csharp\nIParallelRunner runner = new DefaultParallelRunner(Environment.ProcessorCount);\n\npublic class CustomRunnable : IParallelRunnable\n{\n    public void Run(int index, int maxIndex)\n    {\n        // a runnable is separated into (maxIndex + 1) parts running in parallel, index indicates the part currently running\n    }\n}\n\nrunner.Run(new CustomRunnable());\n```\n\n### IParallelRunner\nThis interface allows for creation of custom parallel execution:\n```csharp\npublic class TaskRunner : IParallelRunner\n{\n    public int DegreeOfParallelism { get; }\n\n    public void Run(IParallelRunnable runnable)\n    {\n        Enumerable.Range(0, DegreeOfParallelism).AsParallel().ForAll(i =\u003e runnable.Run(i, DegreeOfParallelism));\n    }\n}\n```\n\n### DefaultParallelRunner\nThis is the default implementation of `IParallelRunner`. It uses tasks to run an `IParallelRunnable` and only returns when the entire runnable has been processed. When `index == maxIndex` it indicates the code is in the calling thread.  \nIt is safe to reuse the same `DefaultParallelRunner` in multiple systems but it should not be used in parallel itself.\n```csharp\nIParallelRunner runner = new DefaultParallelRunner(Environment.ProcessorCount);\n\n// wrong\nISystem\u003cfloat\u003e system = new ParallelSystem\u003cfloat\u003e(runner,\n    new ParallelSystem1(runner),\n    new ParallelSystem2(runner));\n    \n// ok\nISystem\u003cfloat\u003e system = new SequentialSystem\u003cfloat\u003e(\n    new ParallelSystem1(runner),\n    new ParallelSystem2(runner));\n```\n\n## Command\nSince it is not possible to make structural modification on an entity in multi-threaded context, the `EntityCommandRecorder` type is provided to address this.  \nIt is possible to record commands on entities in a thread-safe way to later execute them when those structural modifications are safe to do:\n```csharp\n// This creates an expandable recorder with a default capacity of 1024 commands\nEntityCommandRecorder recorder = new EntityCommandRecorder();\n\n// This creates a fixed capacity recorder of 512 commands\nEntityCommandRecorder recorder = new EntityCommandRecorder(512);\n\n// This creates an expandable recorder with a default capacity of 512 command which can expand to a maximum capacity of 2048\nEntityCommandRecorder recorder = new EntityCommandRecorder(512, 2048);\n```\n\nNote that a fixed capacity `EntityCommandRecorder` (or one which has expanded to its max capacity) has the best performance.  \nWhen needed, an expandable `EntityCommandRecorder` will double its capacity so it is preferred to use a power of 2 as the default capacity.\n\nSome examples:\n```csharp\n// Get a WorldRecord to record entity creation\nWorldRecord worldRecord = recorder.Record(world);\n\n// Defers the creation of a new entity\nEntityRecord entityRecord = worldRecord.CreateEntity();\n\n// EntityRecord has the same API as Entity so all actions expected are available to record as commands\nentityRecord.Set\u003cbool\u003e(true);\n\n// To execute all recorded commands\nrecorder.Execute();\n```\n\n## Serialization\nDefaultEcs supports serialization to save and load a world state. Two implementations are provided which are equal in functionality and it is possible to create a custom serialization engine using the framework of your choice by implementing a set of interfaces:\n\n- `ISerializer` is the base interface\n- `IComponentTypeReader` is used to get the settings of the serialized world in case a component max capacity has been set for a specific type different from the world's max capacity\n- `IComponentReader` is used to get all the components of an entity\n\nThe provided implementations for `TextSerializer` and `BinarySerializer` are highly permissive and will serialize all fields and properties even if they are private or read-only, and do not require any attributes to work.  \nThis was the goal from the start as graphic and framework libraries do not always have well decorated types which would be used as components.  \nAlthough the lowest target is netstandard1.1, please be aware that the capability of both implementations to handle types with no default constructors may not work if the version of your .NET platform is too low. Other known limitations are:\n- no multi-dimensional array support\n- no cyclic object graph support (all objects are copied, thus creating an infinite loop)\n- not compatible with Xamarin.iOS, AOT platforms (uses the `System.Reflection.Emit` namespace)\n\n\n```csharp\nISerializer serializer = new TextSerializer(); // or BinarySerializer\n\nusing (Stream stream = File.Create(filePath))\n{\n    serializer.Serialize(stream, world);\n}\n\nusing (Stream stream = File.OpenRead(filePath))\n{\n    World worldCopy = serializer.Deserialize(stream);\n}\n```\n\nEach implementation has its own serialization context which can be used to transform a given type to something else or just change the value during serialization or deserialization.\n```csharp\nusing BinarySerializationContext context = new BinarySerializationContext()\n    .Marshal\u003cstring, string\u003e(_ =\u003e null) // set every string as null during serialization\n    .Marshal\u003cNonSerializableData, SerializableData\u003e(d =\u003e new SerializableData(d)) // transform non serializable data to a serializable type during serialization\n    .Unmarshal\u003cSerializableData, NonSerializableData\u003e(d =\u003e Load(d)); // reload non serializable data from serializable data during deserialization\n\nBinarySerializer serializer = new BinarySerializer(context);\n```\n\n### TextSerializer\nThe purpose of this serializer is to provide a readable save format which can be edited by hand.\n```\n// declare the maximum number of entities in the World, this must be before any Entity or ComponentMaxCapacity line\nWorldMaxCapacity 42\n\n// this line is used to define an alias for a type used as a component inside the world and must be declared before being used\nComponentType Int32 System.Int32, System.Private.CoreLib\n\n// this line is used to set the max capacity for the given type, in case it is different from the world max capacity\nComponentMaxCapacity Int32 13\n\n// this creates a new entity with the id \"Foo\"\nEntity Foo\n\n// this line sets the component of the type with the alias Int32 on the previously created Entity to the value 13\nComponent Int32 13\n\n// let's say we have the type defined as such already declared with the alias Test\n// struct Test\n// {\n//     int Hello\n//     int World\n// }\nComponentType Test MyNamespace.Text, MyLib\n\n// composite objects are set like this\nComponent Test {\n\tHello 42\n\t// this line is ignored since the type does not have a member with the name Wow\n\t// also the World member will have its default value since not present\n\tWow 43\n}\n\n// this creates a new entity with no id\nEntity\n\n// this sets the component of the type with the alias Test of the previously created Entity as the same as the one of the Entity with the id Foo\nComponentSameAs Test Foo\n```\n### BinarySerializer\nThis serializer is optimized for speed and file size.\n\n# Extension\nA DefaultEcs.Extension project is present to show how other features can be built upon the base framework. Those features are just provided as example and are not part of DefaultEcs because the implementation is not generic nor satisfactory enough.\n- [Children](https://github.com/Doraku/DefaultEcs/blob/master/source/DefaultEcs.Extension/Children/EntityExtension.cs) makes it so entities can be linked together and disposing the parent will also dispose its children\n- [Hierarchy](https://github.com/Doraku/DefaultEcs/tree/master/source/DefaultEcs.Extension/Hierarchy) shows an example on how to create a hierarchy level between entities so parents are processed before their children\n\n# Sample\nSome sample projects are available to paint a better picture on how to use DefaultEcs. Those examples were done relatively fast so they might not be an adequate representation of what an Entity Component System framework application might look like.\n\n[DefaultBoids](https://github.com/Doraku/DefaultEcs/tree/master/source/Sample/DefaultBoids)\n\n[![DefaultBoids](https://img.youtube.com/vi/yEdcqOTCteY/0.jpg)](https://youtu.be/yEdcqOTCteY)\n\nA really simple implementation of a [boids simulation](https://en.wikipedia.org/wiki/Boids), here displaying 30k boids with an old Intel Core i5-3570K CPU 3.40GHz at ~70fps.  \nThis project uses code generation from [DefaultEcs.Analyzer](https://github.com/Doraku/DefaultEcs.Analyzer) for its systems where possible.\n\n[DefaultBrick](https://github.com/Doraku/DefaultEcs/tree/master/source/Sample/DefaultBrick)\n[win10-x64](https://github.com/Doraku/DefaultEcs/releases/download/v0.9.0/DefaultBrick_win10-x64.zip)\n\n![](https://github.com/Doraku/DefaultEcs/raw/master/image/defaultbrick.gif)\n\nBasic breakout clone. The collision is buggy! As said before, not much time was spent debugging those. Ball moves faster the more bricks you destroy and resets to default speed if you lose. The stage reloads once completed.  \nThis project does not use attributes for system queries, everything is statically declared.\n\n[DefaultSlap](https://github.com/Doraku/DefaultEcs/tree/master/source/Sample/DefaultSlap)\n[win10-x64](https://github.com/Doraku/DefaultEcs/releases/download/v0.9.0/DefaultSlap_win10-x64.zip)\n\n![](https://github.com/Doraku/DefaultEcs/raw/master/image/defaultslap.gif)\n\nBasic fly swatter clone. Every five seconds flies (blue squares) will damage the player (up to 3 times until the \"game\" resets) and new ones will spawn.  \nThis project uses attributes for system queries.\n\n# Projects using DefaultEcs\nDoes your game use DefaultEcs? Don't hesitate to contact me.  \n\n[![Chambers of Anubis](https://img.itch.zone/aW1nLzQ2MDYzODcucG5n/original/IALw4S.png)](https://github.com/PodeCaradox/HellowIInJam)\n\n# Dependencies\nCI, tests and code quality rely on these awesome projects:\n- [Coverlet](https://github.com/coverlet-coverage/coverlet)\n- [NFluent](https://github.com/tpierrain/NFluent)\n- [NSubstitute](https://github.com/nsubstitute/NSubstitute)\n- [Roslynator](https://github.com/JosefPihrt/Roslynator)\n- [XUnit](https://github.com/xunit/xunit)\n","funding_links":["https://paypal.me/LaszloPaillat"],"categories":["[ECS Libraries](#contents)","ECS Libraries","C\\#","Open Source Repositories","Libraries"],"sub_categories":["ECS Framework","C#"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDoraku%2FDefaultEcs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDoraku%2FDefaultEcs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDoraku%2FDefaultEcs/lists"}