Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
D

DefaultEcs

> 编程语言
Open source

Entity Component System framework aiming for syntax and usage simplicity with maximum performance for game development.

758 stars0 likes2 views
WebsiteGitHub

About

Entity Component System framework aiming for syntax and usage simplicity with maximum performance for game development.

DefaultEcs is an Entity Component System framework which aims to be accessible with little constraints while retaining as much performance as possible for game development.

  • API documentation

  • FAQ

  • Benchmarks

  • Requirements

  • Versioning

  • Analyzer

  • Overview

    • World
      • Message
    • Entity
    • Component
      • Singleton
      • Resource
    • Query
      • AsPredicate
      • AsEnumerable
      • AsSet
      • AsMap
      • AsMultiMap
    • System
      • ISystem
      • ActionSystem
      • SequentialSystem
      • ParallelSystem
      • AComponentSystem
      • AEntitySetSystem
      • AEntityMultiMapSystem
    • Threading
      • IParallelRunnable
      • IParallelRunner
      • DefaultParallelRunner
    • Command
    • Serialization
      • TextSerializer
      • BinarySerializer
  • Extension

  • Sample

  • Projects using DefaultEcs

  • Dependencies

Requirements

DefaultEcs heavily uses features from C#7.0 and Span from the System.Memory package, compatible from .NETStandard 1.1.
For 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).
It is possible to use DefaultEcs in Unity (check FAQ).

Versioning

This is the current strategy used to version DefaultEcs: v0.major.minor

  • 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
  • major: incremented when there is a breaking change (reset minor number)
  • minor: incremented when there is a new feature or a bug fix

Analyzer

To help development with DefaultEcs, there is a roslyn analyzer which provides code generation and warnings against potential bad usage. It can be found here.

Overview

World

The 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.
Multiple 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.

Worlds are created as such:

csharp
World world = new World();

The World class also implements the IDisposable interface so you can easily clean up resources by disposing it.

Message

It is possible to send and receive message transiting in a World:

csharp
void On(in bool message) { }

// the method On will be called back every time a bool object is published
// it is possible to use any type
world.Subscribe(On);

world.Publish(true);

It is also possible to subscribe to multiple methods of an instance by using the SubscribeAttribute:

csharp
public class Dummy
{
    [Subscribe]
    void On(in bool message) { }
	
    [Subscribe]
    void On(in int message) { }
	
    void On(in string message) { }
}

Dummy dummy = new Dummy();

// this will subscribe the decorated methods only
world.Subscribe(dummy);

// the dummy bool method will be called
world.Publish(true);

// but not the string one as it dit not have the SubscribeAttribute
world.Publish(string.Empty);

Note that the Subscribe method returns an IDisposable object acting as a subscription. To unsubscribe, simply dispose this object.

Entity

Entities are simple structs acting as keys to manage components.

Entities are created as such:

csharp
Entity entity = world.CreateEntity();

You 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.
To clear an entity, simply call its Dispose method.

csharp
entity.Dispose();

Once disposed, you should not use the entity again. If you need a safeguard, you can check the IsAlive property:

csharp
#if DEBUG
if (!entity.IsAlive)
{
    // something is wrong
}
#endif

You 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:

csharp
entity.Disable();

// this will return false;
entity.IsEnabled();

entity.Enable();

// and now it will return true;
entity.IsEnabled();

Component

Components 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:

csharp
public struct Example
{
    public float Value;
}

public interface IExample
{
    float Value { get; set; }
}

public class CExample : IExample
{
    public float Value { get; set; }
}

To 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:

csharp
world.SetMaxCapacity(42);

Components can live on two levels, on entities or directly on the world itself:

csharp
entity.Set(42);

// check if the entity has an int component
if (entity.Has())
{
    // get the int component of the entity
    if (--entity.Get() ();

It 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:

csharp
referenceEntity.Set(42);
entity.SetSameAs(referenceEntity);

referenceEntity.Set(1337);
// the value for entity will also be 1337
entity.Get();

world.Set("hello");
entity.SetSameAsWorld();
world.Set("world");
// the value for entity will also be "world"
entity.Get();

If 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.

Components 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:

csharp
entity.Disable();

// this still return true
entity.Has();
// but this will return false
entity.IsEnabled();

entity.Enable();
// now this will return true
entity.IsEnabled();

There are two ways to update a component value, either use the Set(newValue) method or set/edit the value returned by Get(). One major difference between the two is that Set(newValue) will also notify internal queries that the component value has changed:

csharp
entity.Set(42);

// we set a new value
entity.Set(1337);

ref int component = ref entity.Get();
// we have actually changed the component value but the internal queries have not been notified.
component = 42;

// we can notify manually by calling this method if we need to.
entity.NotifyChanged();

Singleton

By combining the previous calls, it is possible to define a component type as a singleton:

csharp
// only one component value for int can exist on this world
world.SetMaxCapacity(1);

// and it is used by the world
world.Set(42);

// entity.Set(10); this line would throw
// the only way to set the component on entities would be to use the world component
entity.SetSameAsWorld();

Resource

Not 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.
To set up a managed resource on an entity, the type ManagedResource 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.
Should multiple resources of the same type be needed on a single entity, it is also possible to set the type ManagedResource as a component.
If 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.
To actually load the resource, an implementation of the class AResourceManager is needed as shown in the example:

…

This feature only cares for entity components, not the world components.

Query

To 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

…

Once you are satisfied with the rules you can then finish the query by choosing how to get the entities:

AsPredicate

csharp
world
    .GetEntities()
    ...
    .AsPredicate();

Gets a Predicate that checks for your declared rules. When... rules are ignored.
This is useful if you need to check for some entity component composition.

AsEnumerable

csharp
world
    .GetEntities()
    ...
    .AsEnumerable();

Gets an IEnumerable that when enumerated returns all the entities respecting your declared rules. When... rules are ignored.
This is useful if you need to do an initialisation on specific entities, this should not be used in a hot path.

AsSet

csharp
world
    .GetEntities()
    ...
    .AsSet();

Gets 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. If When... rules are present, you should call its Complete() method once you are done processing its entities to clear it. This is the base type used in the provided system implementation.

AsMap

csharp
world
    .GetEntities()
    ...
    .AsMap();

Gets an EntityMap 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. If When... rules are present, you should call its Complete() method once you are done processing its entities to clear it. This is useful if you need O(1) access to an entity based on a key.

AsMultiMap

csharp
world
    .GetEntities()
    ...
    .AsMultiMap();

Gets an EntityMultiMap 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. If When... rules are present, you should call its Complete() method once you are done processing its entities to clear it. This is useful if you need O(1) access to entities based on a key.

System

Although there is no obligation to use them, a set of base classes is provided to help with the creation of systems:

ISystem

This is a base interface for all systems. It exposes an Update method and an IsEnabled property. In all derived t

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#c-sharpdotnetecsentity-component-system

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言