Overview of DefaultEcs
masterSpan<T> from the System.Memory package.repository·master·Indexed 20 days ago
https://github.com/doraku/defaultecsA high-performance Entity Component System (ECS) framework for C# designed for game development. Built using C# 7.0 and Span<T>, it is compatible with .NETStandard 1.1 and Unity. The framework features a central World class for entity and component management, a fluent API for querying entities, and various system base classes like AComponentSystem and AEntitySetSystem to organize game logic.
Span<T> from the System.Memory package.To define a component type as a singleton (where only one instance exists in the entire world), set the maximum capacity of that component type to 1 on the World. Entities can then access this single instance using SetSameAsWorld<T>().
// Only one int can exist in this world
world.SetMaxCapacity<int>(1);
world.Set<int>(42);
// Entities must use the world instance
entity.SetSameAsWorld<int>();
// entity.Set<int>(10); // This would throw an errorThe DefaultEcs.Threading namespace provides tools for multithreading operations. The DefaultParallelRunner class implements the IParallelRunner interface and is used to execute tasks that implement IParallelRunnable using System.Threading.Tasks.Task.
To use it:
IParallelRunnable interface in your class.DefaultParallelRunner (optionally specifying the degree of parallelism via the constructor).Run(IParallelRunnable) to execute the process.Dispose() when finished to release resources.// Conceptual usage pattern
IParallelRunnable myTask = new MyParallelTask();
using (var runner = new DefaultParallelRunner())
{
runner.Run(myTask);
}While ComponentAttribute serves as the base attribute for declaring how to build the inner EntitySet of an AEntitySetSystem<T>, it is intended for internal use or as a base for other attributes.
When defining system requirements, you should prefer using the following specialized attributes instead:
WithAttributeWithoutAttributeAn AResourceManager<TInfo, TResource> is an abstract base type used to manage the lifecycle of resources of type TResource. It uses TInfo as a unique identifier (key) to ensure that if multiple Entity instances request the same resource via the same TInfo, the resource is only loaded once.
Automatic Unloading
When you call Manage(World), the manager begins monitoring ManagedResource<TInfo, TResource> components on entities within that World. As soon as no entities in the world contain a ManagedResource<TInfo, TResource> component for a specific TInfo, the manager automatically unloads that resource.
Disposal
If TResource implements System.IDisposable, the manager will automatically call .Dispose() on the resource during the unloading process (either via automatic unloading or when Dispose() is called on the manager itself).
public abstract class AResourceManager<TInfo,TResource> : System.IDisposableDefaultEcs namespace is the primary entry point for the library, containing the core types and logic required to manage entities, components, and systems. Most high-level operations, including the management of the World, Entity, and Component types, are accessed through this namespace.EntityRecord is a readonly ref struct used to target a specific Entity when creating commands for an EntityCommandRecorder. Instead of applying changes directly to the World, you use an EntityRecord to queue up operations like adding components, removing components, or enabling/disabling entities, which are then processed by the recorder.Components in DefaultEcs are data containers. While you can use classes and interfaces, it is highly recommended to use structs to ensure data is contiguous in memory and to minimize garbage collection.
You can optimize memory by setting a maximum capacity for a specific component type. This must be called before any component of that type is set in the world.
Components can be attached to an Entity or directly to the World.
Be aware that component lookups are type-specific. If you set a component using an interface type, you must use that same interface type to retrieve or check for it; checking for the concrete implementation type will return false.
You can disable a component on an entity without removing it. This allows the entity to change its behavior (and query results) without the performance cost of full removal/re-addition.
// Recommended: use structs for performance
public struct Example
{
public float Value;
}
// Set capacity before use
world.SetMaxCapacity<Example>(42);
// Entity-level component management
entity.Set<int>(42);
if (entity.Has<int>())
{
entity.Remove<int>();
}
// Disabling components
entity.Disable<int>();
// entity.Has<int>() is still true, but entity.IsEnabled<int>() is false
entity.Enable<int>();Systems like ParallelSystem, AEntitySetSystem, and AComponentSystem support multi-threading if you pass an IParallelRunner to their constructor.
Thread Safety Rules: When running in parallel, you must not perform structural modifications to the world or entities. Non-thread-safe operations include:
SetMaxCapacity, Optimize, or TrimExcess on a world.Set, SameAs, SameAsWorld, NotifyChanged, or CopyTo on an entity/world.Runner Usage:
DefaultParallelRunner: The standard implementation using tasks. It is safe to reuse the same instance across multiple systems, but do not run the runner itself in parallel (e.g., don't put a ParallelSystem inside another ParallelSystem using the same runner).IParallelRunnable: Implement this interface to create custom parallelizable logic for a runner.IParallelRunner runner = new DefaultParallelRunner(Environment.ProcessorCount);
ISystem<float> system = new VelocitySystem(world, runner);
system.Update(elapsedTime);When building the inner EntitySet of an AEntitySetSystem<T> using a World instance, you can use several attributes to define which entities are included or excluded based on their component composition. These attributes act as filters for the system's target entities.
[WithAttribute(Type[])]: Requires that all specified component types are present.[WithEitherAttribute(Type[])]: Requires that at least one of the specified component types is present.[WithPredicateAttribute]: Uses a decorated method of type ComponentPredicate<T>(T) as a custom predicate to determine inclusion.[WithoutAttribute(Type[])]: Excludes entities if any of the specified component types are present.[WithoutEitherAttribute(Type[])]: Excludes entities if at least one of the specified component types is present.These attributes allow the system to react to specific lifecycle events of components:
[WhenRemovedAttribute(Type[])]: Reacts when the specified component types are deleted.[WhenRemovedEitherAttribute(Type[])]: Reacts when at least one of the specified component types is deleted.There are two ways to update a component value. Choosing the right one is critical for query performance and correctness:
Set<T>(newValue): This method updates the value and automatically notifies internal queries that the component has changed.Get<T>() (via ref): You can get a reference to the component and modify it directly. This is faster but does not notify internal queries. If you modify a component via a reference, you must manually call NotifyChanged<T>() to ensure queries reflect the change.// Method 1: Notifies queries automatically
entity.Set<int>(1337);
// Method 2: Fast, but requires manual notification
ref int component = ref entity.Get<int>();
component = 42;
entity.NotifyChanged<int>();The EntityQueryBuilder.EitherBuilder is a helper class used to define "either" group rules. These rules allow you to retrieve or observe a subset of Entity objects that satisfy a logical OR condition (e.g., an entity that has either Component A or Component B).
Once the rules are defined using the builder, you can convert the query into various collection types or predicates to use in your logic.