Skip to content

drownedNonsense/PipeKit

Repository files navigation

Pipe Kit

License: GPL v3

Description

A simple C# ECS library.

ko-fi

Example

Computing PI

global using World        = PipeKit.World<Flags, string, double>;
global using Query        = PipeKit.World<Flags, string, double>.Query;
global using QueryBuilder = PipeKit.World<Flags, string, double>.QueryBuilder;
global using ISystem      = PipeKit.World<Flags, string, double>.ISystem;
using FluentResponse;

var world = World.CreateBuilder()
    .WithComponent<Position>()
    .WithComponent<Velocity>()
    .WithComponent<Size>()
    .WithComponent<Mass>()
    .WithSystem(world => new MovementSystem(world))
    .WithSystem(world => new CollisionSystem(world))
    .WithEventBus<Position>()
    .WithEventBus<CollisionEvent>()
    .Build();

uint collisionCount = 0;
const uint   PI_DIGITS                = 6;
const double PHYSICS_FIXED_DELTA_TIME = 0.00001;

world.SubscribeEvent<CollisionEvent>(_ => collisionCount++);

// Heavy block
var blockA = world.CreateEntityBuilder()
    .WithComponent(new Position(5))
    .WithComponent(new Velocity(-1))
    .WithComponent(new Size(1))
    .WithComponent(new Mass(Math.Pow(100, PI_DIGITS - 1)))
    .Build();

// Light block
var blockB = world.CreateEntityBuilder()
    .WithComponent(new Position(2.5))
    .WithComponent(new Velocity(0))
    .WithComponent(new Size(1))
    .WithComponent(new Mass(1))
    .Build();

// Wall entity
var wall = world.CreateEntityBuilder()
    .WithComponent(new Position(0))
    .WithComponent(new Size(0))
    .Build();

while (true) {

    world.RunSystems(PHYSICS_FIXED_DELTA_TIME);

    var velocityA = world.TryGetComponent<Velocity>(blockA).Unwrap().X;
    var velocityB = world.TryGetComponent<Velocity>(blockB).Unwrap().X;

    if (velocityA is > 0 && velocityB > velocityA)
        break;
}

Console.WriteLine($"Collision Count = {collisionCount}");
Console.WriteLine($"Estimated digits of Pi = {PI_DIGITS}");
Console.WriteLine($"Estimated value of Pi = {(collisionCount << 1) / Math.Pow(10, PI_DIGITS - 1)}");
using PipeKit;

enum Flags {};

record struct CollisionEvent(Entity A, Entity B);
record struct Position(double X);
record struct Velocity(double X);
record struct Size(double Width);
record struct Mass(double Value);

class MovementSystem(World world) : ISystem {
    public QueryBuilder QueryBuilder { get; } = world.CreateQueryBuilder()
        .WithComponent<Position>()
        .WithComponent<Velocity>(velocity => velocity.X is > 0);

    public void Run(Query query, double deltaTime) {

        var positions  = query.GetComponents<Position>();
        var velocities = query.GetComponents<Velocity>();

        foreach (var entity in query.Entities) {

            var position = positions[entity];
            var velocity = velocities[entity];

            position.X += velocity.X * deltaTime;
            world.TrySetComponent(entity, position);
            world.PublishEvent(entity, position);
        }
    }
}

class CollisionSystem(World world) : ISystem {
    public QueryBuilder QueryBuilder { get; } = world.CreateQueryBuilder()
        .WithComponent<Position>()
        .WithComponent<Velocity>()
        .WithComponent<Size>()
        .WithComponent<Mass>();

    public void Run(Query query, double deltaTime) {

        var entities = query.Entities;
        var positions  = query.GetComponents<Position>();
        var velocities = query.GetComponents<Velocity>();
        var sizes      = query.GetComponents<Size>();
        var masses     = query.GetComponents<Mass>();

        foreach (var a in query.Entities)
            foreach (var b in query.Entities)
                if (a != b) {

                    var positionA = positions[a];
                    var positionB = positions[b];
                    var sizeA     = sizes[a];
                    var sizeB     = sizes[b];

                    if (Math.Abs(positionA.X - positionB.X) < (sizeA.Width + sizeB.Width) * 0.5) {

                        // 1D elastic collision
                        var massA = masses[a].Value;
                        var massB = masses[b].Value;
                        var velocityA = velocities[a];
                        var velocityB = velocities[b];

                        world.TrySetComponent(a, new Velocity((massA - massB) / (massA + massB) * velocityA.X + 2 * massB / (massA + massB) * velocityB.X));
                        world.TrySetComponent(b, new Velocity(2 * massA / (massA + massB) * velocityA.X + (massB - massA) / (massA + massB) * velocityB.X));

                        world.PublishEvent(new CollisionEvent(a, b));

                    }
                }

        var wallQuery = world.CreateQueryBuilder()
            .WithComponent<Position>()
            .WithComponent<Size>()
            .WithoutComponent<Velocity>()
            .Build();

        var wallEntities  = wallQuery.Entities;
        var wallPositions = wallQuery.GetComponents<Position>();
        var wallSizes     = wallQuery.GetComponents<Size>();

        foreach (var entity in entities) {

            var position = positions[entity];
            var velocity = velocities[entity];
            var size     = sizes[entity];

            foreach (var wallEntity in wallEntities) {

                var wallPosition = wallPositions[wallEntity];
                var wallSize     = wallSizes[wallEntity];

                if (Math.Abs(position.X - wallPosition.X) < (size.Width + wallSize.Width) * 0.5)
                    world.TrySetComponent(entity, new Velocity(-velocity.X));

            }
        }
    }
}

About

A simple C# Entity Component System library.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages