Doraku / DefaultEcs.Analyzer

Roslyn analyzers for DefaultEcs users.
MIT No Attribution
13 stars 1 forks source link
csharp defaultecs dotnet ecs entity-component-system game game-development game-engine gamedev

DefaultEcs This project provides a set of analyzers to enhance the Visual Studio experience when working on a DefaultEcs project by adding diagnostics or by removing general C# diagnostics that do not apply.
The main repo for DefaultEcs is here, this repo is specific to the analyzer.

Here is the list of analyzers and suppressors defined in this project.

NuGet Coverage Status continuous integration status preview package Join the chat at https://gitter.im/Doraku/DefaultEcs

Requirement

Versioning

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

Code generation

Referencing DefaultEcs.Analyzer in your project gives you access to some attributes to automatically generate code for you:

UpdateAttribute

This attribute should be used on a void method inside a type inheriting AEntitySetSystem or AEntityMultiMapSystem.

    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [Update]
        private static void Update(float time, ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }
    }

The containing type need to be declared as partial for the code generation to work. Your system component composition will be deduced from the parameters of the method. In the example above, it would be world.GetEntities().With<Position>().With<LinearVelocity>().AsSet().

Parameters can be requested as ref or in depending on whether you want to change their value or not.
If a parameter has the same type as the generic type of the system, it is the state of ISystem.Update method that is passed and not a component (float in the example above).

You can request the Entity as a parameter too, this is usefull of you want to use it to record action on a EntityCommandRecorder.

    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [Update]
        private static void Update(float time, in Entity entity, ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }
    }

If you need to define more exotic rules, all attributes (WithAttribute, WithoutAttribute, DisabledAttribute, ...) that you normally define on the parent type will also be used.

    [Without(typeof(bool))]
    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [Update]
        private static void Update(float time, ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }

        [WithPredicate]
        private bool Filter(in int _) => true;
    }

This would generate world.GetEntities().With<Position>().With<LinearVelocity>().Without<bool>().With(Filter).AsSet() for you.

If for some reason you need to define a constructor, you can use the factory overcharge of the base type with the produced CreateEntityContainer method.

    public sealed partial class PlayerSystem : AEntitySetSystem<float>
    {
        public PlayerSystem(World world)
            : base(world, CreateEntityContainer, null, 0)
        { }

        [Update]
        private static void Update(...)
        {
            ...
        }
    }

UseBufferAttribute

If your system need to use a buffer to process entities (no multithreading and need to do composition change operation), you can add this attribute with the UpdateAttribute.

    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [Update, UseBuffer]
        private static void Update(float time, ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }
    }

The correct constructors will be generated for you.

ConstructorParameterAttribute

If you need some fields or properties to be set in the constructor of your system but still want it to be done for you, you can decorate them with the attribute.

    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [ConstructorParameter]
        private readonly int _myField;

        [Update]
        private static void Update(float time, in Entity entity, ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }
    }

The generated constructors for the above example will request an extra int myField parameter.

AddedAttribute, ChangedAttribute

If you need reactive rules on the component composition, you can do so by decorating the parameter of your Update method.

    public sealed partial class MovementSystem : AEntitySetSystem<float>
    {
        [Update]
        private static void Update(float time, [Added][Changed] ref Position position, in LinearVelocity linearVelocity)
        {
            position.Value += linearVelocity.Value * time;
        }
    }

This would generate world.GetEntities().WhenAdded<Position>().WhenChanged<Position>().With<LinearVelocity>().AsSet().