Arch.System.SourceGenerator 1.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Arch.System.SourceGenerator --version 1.0.2
NuGet\Install-Package Arch.System.SourceGenerator -Version 1.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Arch.System.SourceGenerator" Version="1.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Arch.System.SourceGenerator --version 1.0.2
#r "nuget: Arch.System.SourceGenerator, 1.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Arch.System.SourceGenerator as a Cake Addin
#addin nuget:?package=Arch.System.SourceGenerator&version=1.0.2

// Install Arch.System.SourceGenerator as a Cake Tool
#tool nuget:?package=Arch.System.SourceGenerator&version=1.0.2

Arch.Extended

Maintenance Nuget License C#

Extensions for Arch with some useful features like Systems, Source Generator and Utils.

  • 🛠️ Productive > Adds some useful tools and features to the main repository!
  • ☕️ SIMPLE > Works easily, reliably and understandably!
  • 💪 MAINTAINED > It's actively being worked on, maintained, and supported!
  • 🚢 SUPPORT > Supports .NetStandard 2.1, .Net Core 6 and 7 and therefore you may use it with Unity or Godot!

Features & Tools

  • ⚙️ Systems > By means of systems, it is now easy to organize, reuse and arrange queries.
  • ✍️ Source Generator > Declarative syntax using attributes and source generator, let your queries write themselves!

With this package you are able to write and group queries and systems for Arch automatically. And all this with the best possible performance

// Components ( ignore the formatting, this saves space )
public struct Position{ float X, Y };
public struct Velocity{ float Dx, Dy };

// BaseSystem provides several usefull methods for interacting and structuring systems
public class MovementSystem : BaseSystem<World, float>
{
    public MovementSystem(World world) : base(world) {}
    
    // Generates a query and calls that one automatically on BaseSystem.Update
    [Query]
    public void Move([Data] in float time, ref Position pos, ref Velocity vel)
    {
        pos.X += time * vel.X;
        pos.Y += time * vel.Y;
    }
    
    // Generates and filters a query and calls that one automatically on BaseSystem.Update in order
    [Query]
    [All<Player, Mob>, Any<Idle, Moving>, None<Alive>]
    public void ResetVelocity(ref Velocity vel)
    {
        vel = new Velocity{ X = 0, Y = 0 };
    }
}

public class Game 
{
    public static void Main(string[] args) 
    {     
        var deltaTime = 0.05f; // This is mostly given by engines, frameworks
        
        // Create a world and a group of systems which will be controlled 
        var world = World.Create();
        var _systems = new Group<float>(
            new MovementSystem(world),   // Run in order
            new MyOtherSystem(...),
            ...
        );
      
        _systems.Initialize();                  // Inits all registered systems
        _systems.BeforeUpdate(in deltaTime);    // Calls .BeforeUpdate on all systems ( can be overriden )
        _systems.Update(in deltaTime);          // Calls .Update on all systems ( can be overriden )
        _systems.AfterUpdate(in deltaTime);     // Calls .AfterUpdate on all System ( can be overriden )
        _systems.Dispose();                     // Calls .Dispose on all systems ( can be overriden )
    }
}

Contents

Systems Code sample

The Arch.System package provides a number of useful classes to organize and structure queries. These are organized into "systems" and can also be grouped.

The example below demonstrates a slightly larger code sample

// Components ( ignore the formatting, this saves space )
public struct Position{ float X, Y };
public struct Velocity{ float Dx, Dy };

// BaseSystem provides several usefull methods for interacting and structuring systems
public class MovementSystem : BaseSystem<World, float>{

    private QueryDescription _desc = new QueryDescription().WithAll<Position, Velocity>();
    public MovementSystem(World world) : base(world) {}
    
    // Can be called once per frame
    public override void Update(in float deltaTime)
    {
        // Run query, can also run multiple queries inside the update
        World.Query(in _desc, (ref Position pos, ref Velocity vel) => {
            pos.X += vel.X;
            pos.Y += vel.Y;
        });  
    }
}

public class Game 
{
    public static void Main(string[] args) 
    {     
        var deltaTime = 0.05f; // This is mostly given by engines, frameworks
        
        // Create a world and a group of systems which will be controlled 
        var world = World.Create();
        var _systems = new Group<float>(
            new MovementSystem(world),   // Run in order
            new MyOtherSystem(...),
            ...
        );
      
        _systems.Initialize();                  // Inits all registered systems
        _systems.BeforeUpdate(in deltaTime);    // Calls .BeforeUpdate on all systems ( can be overriden )
        _systems.Update(in deltaTime);          // Calls .Update on all systems ( can be overriden )
        _systems.AfterUpdate(in deltaTime);     // Calls .AfterUpdate on all System ( can be overriden )
        _systems.Dispose();                     // Calls .Dispose on all systems ( can be overriden )
    }
}

Systems source generator

The Arch.System.SourceGenerator package provides some code generation utils. With them, queries can be written virtually by themselves which saves some boilerplate code.

Query methods can be generated in all classes as long as they are partial. However it makes most sense in the BaseSystem. The attributes can be used to meaningfully describe what query to generate, and the query will always call the annotated method.

The generated methods can also be called manually.

Classes using the source generation should be within a namespace, the global one is NOT supported at the moment.

Syntax

Arch.System provides some attributes that can be used for code generation. These can be arranged and used arbitrarily.

  • [Query] > Update marks some method so that the code generator makes a query out of it.
  • [All<T0...T25>] > Mirrors QueryDescription.All, defines which components an entity needs.
  • [Any<T0...T25>] > Mirrors QueryDescription.Any, defines that an entity needs at least one of the components.
  • [None<T0...T25>] > Mirrors QueryDescription.None, defines that an entity should have none of those components.
  • [Exclusive<T0...T25>] > Mirrors QueryDescription.Exclusive, defines an exclusive set of entity components.
  • [Data] > Marks a method parameter and specifies that this type should be passed through the query.

Query is always required. All, Any, None, Exclusive, Data are optional and independent from each other.

Let us now look at some example.

Query Methods in BaseSystem

It makes most sense to use the generator directly in the BaseSystem. If this is the case, the BaseSystem.Update method is automatically generated and the queries are called in order.

// Components ( ignore the formatting, this saves space )
public struct Position{ float X, Y };
public struct Velocity{ float Dx, Dy };

// BaseSystem provides several usefull methods for interacting and structuring systems
public partial class MovementSystem : BaseSystem<World, float>{

    public MovementSystem(World world) : base(world) {}
    
    // Generates a query and calls this annotated method for all entities with position and velocity components.
    [Query]  // Marks method inside BaseSystem for source generation.
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void MoveEntity([Data] ref float time, ref Position pos, ref Velocity vel)  // Entity requires atleast those components. "in Entity" can also be passed. 
    {
        pos.X += time * vel.X;
        pos.Y += time * vel.Y;
    }
    
    /// Generates a query and calls this method for all entities with velocity, player, mob, particle, either moving or idle and no dead component.
    /// All, Any, None are seperate attributes and do not require each other.
    [Query]
    [All<Player, Mob, Particle>, Any<Moving, Idle>, None<Alive>]  // Adds filters to the source generation to adress certain entities. 
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void StopDeadEntities(ref Velocity vel)
    {
        vel.X = 0;
        vel.Y = 0;
    }
}

If a parameter of a method within a basesystem is marked as Data, then BaseSystem.Data is passed directly to the query. In the example above you can see this at the time: float.

If you use the source generator and its attributes in a class that does not override BaseSystem.Update, this method is implemented by the source generator itself. If the method is already implemented by the user, only "query" methods are generated, which you can call yourself.

public partial class MovementSystem : BaseSystem<World, GameTime>
{
    private readonly QueryDescription _customQuery = new QueryDescription().WithAll<Position, Velocity>();
    public DebugSystem(World world) : base(world) { }

    public override void Update(in GameTime t)
    {
        World.Query(in _customQuery, (in Entity entity) => Console.WriteLine($"Custom : {entity}"));  // Manual query
        MoveEntityQuery(World);  // Call source generated query, which calls the MoveEntity method
    }

    [Query]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void MoveEntity(ref Position pos, ref Velocity vel)
    {
        pos.X += vel.X;
        pos.Y += vel.Y;
    }
}

Query Methods in custom classes

Queries can be generated in any class, in the following example a move and a hit query is generated and used for the class. This is useful to group and reuse queries.

// Class which will generate 
public partial class MyQueries{

    [Query]  // Marks method inside BaseSystem for source generation.
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void MoveEntities([Data] in float time, ref Position pos, ref Velocity vel){
        pos.X += time * vel.X;
        pos.Y += time * vel.Y;
    }
    
    [Query]  // Marks method inside BaseSystem for source generation.
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void DamageEntities(ref Hitted hit, ref Health health){
        health.value -= hit.value;
    }
    
    ... other Queries
}

// Instantiate class and call the generated methods 
var myQueries = new MyQueries();
myQueries.MoveEntitiesQuery(someWorld, 10.0f);  // World is always required for the generated method, 10.0f is the [Data] parameter
myQueries.DamageEntitiesQuery(someWorld);

The same works for static classes, whose performance is better. The advantage is that no class instance is needed.

// Class which will generate 
public static partial class MyQueries{

    [Query]  // Marks method inside BaseSystem for source generation.
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void MoveEntities([Data] in float time, ref Position pos, ref Velocity vel){
        pos.X += time * vel.X;
        pos.Y += time * vel.Y;
    }
    
    [Query]  // Marks method inside BaseSystem for source generation.
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void DamageEntities(ref Hitted hit, ref Health health){
        health.value -= hit.value;
    }
    
    ... other Queries
}

// Instantiate class and call the generated methods 
MyQueries.MoveEntitiesQuery(someWorld, 10.0f);  // World is always required for the generated method, 10.0f is the [Data] parameter
MyQueries.DamageEntitiesQuery(someWorld);
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.4 477 2/15/2024
1.1.2 492 9/21/2023
1.1.0 508 4/15/2023
1.0.9 231 3/13/2023
1.0.8 201 3/13/2023
1.0.7 279 1/29/2023
1.0.6 302 1/19/2023
1.0.5 271 1/18/2023
1.0.4 275 1/12/2023
1.0.3 253 1/12/2023
1.0.2 260 1/12/2023
1.0.1 287 1/8/2023
1.0.0 289 1/8/2023

Parameters marked with data are now included in queries.
           Generated queries require world as input and optional data parameters.
           Some performance improvements, query structs are now cached.
           Queries can be generated in all classes now and do not require a basesystem anymore.