DefaultEcs Documentation

repository·master·Indexed 20 days ago

https://github.com/doraku/defaultecs

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

Tokens
107.8K
Snippets
474
Records
583
Agent score
71%

What's inside DefaultEcs

  1. Overview of DefaultEcs

    master
    DefaultEcs is an Entity Component System (ECS) framework designed for game development. It aims to provide high performance while remaining accessible with minimal constraints. It is built using C# 7.0 features and Span<T> from the System.Memory package.
  2. Implement Singletons using MaxCapacity

    master

    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 error
  3. Run processes in parallel with DefaultParallelRunner

    master

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

    1. Implement the IParallelRunnable interface in your class.
    2. Instantiate DefaultParallelRunner (optionally specifying the degree of parallelism via the constructor).
    3. Call Run(IParallelRunnable) to execute the process.
    4. Call Dispose() when finished to release resources.
    // Conceptual usage pattern
    IParallelRunnable myTask = new MyParallelTask();
    using (var runner = new DefaultParallelRunner()) 
    {
        runner.Run(myTask);
    }
  4. Avoid using ComponentAttribute directly

    master

    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:

    • WithAttribute
    • WithoutAttribute
  5. How AResourceManager works for resource management

    master

    An 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.IDisposable
  6. DefaultEcs Namespace Overview

    master
    The DefaultEcs 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.
  7. Use EntityRecord to create commands for EntityCommandRecorder

    master
    An 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.
  8. Define and manage components

    master

    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.

    Setting Capacity

    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.

    Component Levels

    Components can be attached to an Entity or directly to the World.

    Component Identity

    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.

    Disabling Components

    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>();
  9. Enable multi-threaded execution with IParallelRunner

    master

    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:

    • Creating or disposing entities.
    • Removing components from an entity or world.
    • Enabling/disabling entities or components.
    • Calling SetMaxCapacity, Optimize, or TrimExcess on a world.
    • Calling 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);
  10. Configure EntitySet filtering for AEntitySetSystem<T>

    master

    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.

    Inclusion Filters

    • [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.

    Exclusion Filters

    • [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.

    Reaction Filters (Deletion/Change)

    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.
  11. Update component values and notify queries

    master

    There are two ways to update a component value. Choosing the right one is critical for query performance and correctness:

    1. Set<T>(newValue): This method updates the value and automatically notifies internal queries that the component has changed.
    2. 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>();
  12. Use EntityQueryBuilder.EitherBuilder to create 'either' group rules

    master

    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.