Hybrid ECS solution for S&box

Additional documentation available inside of the library

Glossary
Entity - int value, only exists if there is components associated with it
Component - struct, contains data that we operate with
Systems - systems allow us to change iterate over entities, changing data in their components, stonks

World - container that stores entities and components
Filter - filters entities based on component presence or absence
Feature - list of systems

Basic example

var world = World.Default;
var filter = new EntityFilter( world );
filter.With<Position>().With<Input>().Without<Dead>;

foreach(int entity in filter){
   var input = world.GetComponent<Input>( entity );
   ref var position = ref world.GetComponent<Position>( entity );
   if ( input.X != 0 && input.Y != 0) continue;
   position.X += input.X;
   position.Y += input.Y;
}

Advanced example

First, we will create initializer. That's where all our game features will be initialized and updated.
public class MySwordGameInitializer : InitializerBase
{
    protected override void OnAwake()
    {
        AddFeature( new CombatFeature() );
        base.OnAwake(); // must be called after we add all the features
    }
}

Second, we will create feature. It will contain all relevant systems and update them one by one.
public class CombatFeature : FeatureBase
{
    public CombatFeature()
    {
        Add( new AttackSystem() );
        Add( new DefenseSystem() );
    }
}
Third, we will create our systems. 
public class AttackSystem : SystemBase
{
    private EntityFilter _filter = new EntityFilter( World.Default ).With<AttackComponent>();
    
    
    public override void Update( float deltaTime )
    {
        foreach ( var entity in _filter )
        {
            ref var attackComponent = ref entity.Get<AttackComponent>();
            // Perform attack logic here
        }
    }
}


Adding components
We can add a component. This approach is suitable for adding tags - components without any data inside of them.
// use world
world.AddComponent<Input>();

// use extension to add component for entity in Default world 
entity.AddComponent<Input>();

Setting componetns
If we want to set component with data AND/OR change existing component, we must use SetComponent instead. 
// use world
world.SetComponent( new Input { Value = InputExample } );

// use extension to set component for entity in Default world 
entity.SetComponent( new Input { Value = InputExample } );
 

Changing component values
Since we use struct for components, it is important to remember how value types differ from reference types. When you retrieve a component from an entity, you're working with a copy - not a reference to the original. This means that modifying the struct directly won't affect the component stored in the ECS unless you explicitly use ref.

To safely and correctly modify component data, always use ref access when reading or updating components within systems.
// ✅ Will change the position
ref var position = ref entity.GetComponent<Position>();
position.Value = Vector3.Zero;

// ❌ WONT change the position

var position = entity.GetComponent<Position>();
position.Value = Vector3.Zero;


Removing components
// use world
world.RemoveComponent<Input>();

// use extension to remove component for entity in Default world 
entity.RemoveComponent<Input>();

Linking GameObjects with ECS world
1. Create your component -> struct MyComponent
2. Create your provider -> class MyComponentProvider : EntityProvider<MyComponent>
3. Add provider to the target GameObject
4. Now you will be able to filter this GO -> Filter.With<MyComponent>

!) Important: modifying component in the inspector will not work in Play mode. Currently, there is no link between instantiated component and displayed one in the inspector.






Generally I'd prefer to use Morpeh, but it has issues with whitelisting, so ok then gonna make my own ecs