Конфигураторы компонентов

По умолчанию при добавлении или удалении компонента данные заполняются дефолтным значение, а при копировании компонент полностью копируется
Чтобы установить свою логику инициализации и сброса компонента можно воспользоваться конфигураторами
Также конфигураторы используются для сериализации


Пример:

// Создадим для примера компонент Scale
public struct Scale : IComponent {
    public float X, Y, Z;
    
    // Конфигуратор должен реализовывать интерфейс IComponentConfig
    // Для начала рассмотрим полную имплементацию
    public class Config<WorldType> : IComponentConfig<Scale, WorldType> where WorldType : struct, IWorldType {
        
        // заменяет поведение при создании компонента через метод Add
        public World<WorldType>.OnComponentHandler<Scale> OnAdd() {
            return (World<WorldType>.Entity entity, ref Scale component) => { };
        }

        // заменяет поведение при создании компонента через метод Add с передачей значения
        public World<WorldType>.OnComponentHandler<Scale> OnAddWithValue() {
            return (World<WorldType>.Entity entity, ref Scale component) => { };
        }

        // заменяет поведение при удалении компонента через метод Delete
        public World<WorldType>.OnComponentHandler<Scale> OnDelete() {
            return (World<WorldType>.Entity entity, ref Scale component) => { };
        }

        // заменяет поведение при копировании компонента
        public World<WorldType>.OnCopyHandler<Scale> OnCopy() {
            return (World<WorldType>.Entity entity, World<WorldType>.Entity dstEntity, ref Scale src, ref Scale dst) => { };
        }

        // является ли компонент копируемым, и будет ли скопирован при entity.Copy() или entity.Move()
        public bool IsCopyable() => true;

        // идентификатор компонента для сериализации (подробнее в разделе `Сериализации`)
        public Guid Id() => new("b121594c-456e-4712-9b64-b75dbb37e611");

        // версия компонента для сериализации (подробнее в разделе `Сериализации`)
        public byte Version() => 0;

        // писатель компонента для сериализации (подробнее в разделе `Сериализации`)
        public BinaryWriter<Scale> Writer() {
            return (ref BinaryPackWriter writer, in Scale value) => { };
        }

        // читатель компонента для сериализации (подробнее в разделе `Сериализации`)
        public BinaryReader<Scale> Reader() {
            return (ref BinaryPackReader reader) => { };
        }

        // миграция компонента при изменении для сериализации (подробнее в разделе `Сериализации`)
        public EcsComponentMigrationReader<Scale, WorldType> MigrationReader() {
            return (ref BinaryPackReader reader, World<WorldType>.Entity entity, byte version, bool disabled) => { };
        }

        // стратегия чтения\записи компонента при создании снимка мира для сериализации (подробнее в разделе `Сериализации`)
        public IPackArrayStrategy<Scale> ReadWriteStrategy() => new UnmanagedPackArrayStrategy<Scale>();
    }
    
    // Существует также дефолтный конфигуратор DefaultComponentConfig, позволяющий переопределить только нужные методы
    // Пример
    public class ConfigCompact<WorldType> : DefaultComponentConfig<Scale, WorldType> where WorldType : struct, IWorldType {
        public override World<WorldType>.OnComponentHandler<Scale> OnAdd() {
            return (World<WorldType>.Entity entity, ref Scale component) => { };
        }
    }
}

// Теперь при регистрации компонента возможно передать конфигурацию
W.RegisterComponentType<Scale>(new Scale.Config<WT>());

Аналогичный подход используется для:
стандартных компонентов (IStandardComponentConfig)
мультикомпонентов (IComponentConfig)
компонентов-отношений (IComponentConfig)
событий (IEventConfig)