Arch

A high-performance C# based Archetype & Chunks Entity Component System (ECS) with optional multithreading.

APACHE-2.0 License

Stars
971
Committers
23

Bot releases are hidden (Show)

Arch - 1.2.8 Latest Release

Published by genaray 7 months ago

Bug fixes & Changes 🛠

New Features 🎉

  • Allow getting and setting of recycled entity ids for persistence by @richdog in https://github.com/genaray/Arch/pull/193
  • New Dangerous methods for setting and getting recycled ids.
  • Integrated the updated and improved JobScheduler 1.1.2.
  • Added ScheduleParallelInlineChunkQuery for scheduling ChunkJobs more effectively and to determine dependencies.
  • Added World.IsAlive(EntityReference);.

New Contributors ❤

Full Changelog: https://github.com/genaray/Arch/compare/1.2.7...1.2.8

Arch - 1.2.7

Published by genaray about 1 year ago

Bug fixes & Changes

  • Added Archetype-Edges for faster single Remove-Component operations.
  • Improved performance for Single Add and Remove operations.
  • Entity now implements IComparable<Entity>
  • Checking a default Entity or EntityReference for IsAlive now returns false.
  • Archetypes with zero entities are now skipped during iteration.
  • BitSets were vectorized for faster checks (especially when there many registered components)
  • Query API was slightly improved, no more in modifier for entity iteration -> faster
  • ComponentType is now slimmer and blittable (just pure data, no managed types in it anymore).

New Features

  • Native AOT support by ComponentRegistry and ArrayRegistry - can now be used to register components native aot-wise.
  • A variety of DangerousUtilities to access arch internals. Used especially by Arch.Extended.
  • Some new scripts to build Arch directly for Unity in the scripts folder.
  • Arch.LowLevel is now a dependency

Contributions

Thanks to @reeseschultz , @Hertzole, @DrSmugleaf for several contributions! <3

Arch - 1.2.6

Published by genaray about 1 year ago

Bug fixes & Changes

  • Added Archetype-Edges for faster single Add-Component operations.
  • Increased performance for lookups and bulk operations due to better shift handling.
  • Worlds are now recycled correctly and more efficiently due to plain array usage.
  • CommandBuffer now invokes Events correctly.

New Features

  • Added Entity-Events.
  • Added dangerous utility methods for accessing some internals, this will be used in the serialization package.
  • Better Component-Registration and methods, now also work for most structs.

Contributions

Thanks to @DrSmugleaf, @nathanrw , @mikhail-dcl , @clibequilibrium for several contributions! <3

Arch - 1.2.5

Published by genaray over 1 year ago

Bug fixes & Changes

  • CommandBuffer do now clear correctly
  • Added Debug.Asserts to several places to aid development
  • Fixed World.Range which did not set components
  • Fixed World.Add(entity,cmp) which did not set component either.
  • ComponentTypes inside ComponentRegistry are now offset by +1 to fix a hash related issue
  • MurMurHash3 used internally for Components
  • Query._any now works correctly for larger bitsets > 32.
  • Improved performance of TryGet and TryGetRef methods.

New Features

  • Implicit conversion operation for EntityReference to `Entity´.
  • World.AddOrGet<T> to ensure components on entities.
  • Some other small utility methods.

Contributions

Huge thanks to @DrSmugleaf and @nathanrw for bug fixes and contributions! :)

Arch - 1.2.4.2-beta

Published by genaray over 1 year ago

Bug fixes & Changes

  • Fixed an issue where structural changes sometimes allocated too many chunks resulting in wasted memory and potential crashes.
  • Fixed PURE_ECS compatibility
  • Fixed EntityReference.IsAlive() returning true in certain scenarios where the entity was clearly dead or not valid anymore.
  • Fixed a couple of warnings
  • Fixed some tests

Contributions

Thanks to @DrSmugleaf and @nathanrw for fixing several warnings and bugs! :)

Arch - 1.2.4

Published by genaray over 1 year ago

Bug fixes & Changes

  • CommandBuffer operations were now fixed and do record Add and Sets correctly.
  • CommandBuffer resizes its internal arrays now correctly.
  • Non generic World.Add, World.AddRange, World.RemoveRange were fixed, they created wrong archetypes with doubled components in some cases.
  • Almost all API now uses spans, spanless alternatives can be found in Arch.Core.Extensions
  • Simplified HashCode generation in general.
  • Some slight performance improvements during entity move operations.
  • Removed in and ref modifiers for many operations because of performance reasons (most used structs are small enough to be copied and thus more efficient).

New Features

  • Added Debug-Assert which is triggered by undefined structural changes to notify the user about those.
  • Added non generic World.Remove.
  • Added EntityInfoStore which acts as an storage for Entity meta data and offers an unified API to access those data.

Contributions

Thanks to @DrSmugleaf for the PRs and fixing the compatibility with #define pure_ecs and nullable issues :)
Thanks to @Donaut for the EntityInfoDictionary and its integration! :)

Arch - 1.2.3

Published by genaray over 1 year ago

Bug fixes & Changes

  • Fixed a bug where frequent adding/removing components caused a messed up EntityInfo dictionary and a crash sooner or later.
  • Fixed a bug where adding components by a QueryDescription did not set its value correctly, this was now fixed as well.
  • Fixed BitSet.None, which now works correctly with large amounts of components.
  • Removed struct constraints on EntityExtension methods.
  • Entity.Set now uses default values
  • CommandBuffer methods now make use of default values.

New Features

  • World.Clear() was introduced and clears the whole world.
  • World.GetEntities(Span<Entity> ...); was introduced as an alternative to lists, soon all the available API will make use of spans.
  • ComponentRegistry features a method to register a ComponentType, therefore the user can register custom types.

ComponentRegistry.Add(ComponentType);

Arch tries to support so called managed structs. However, this is not always fully possible due to compatibility with a type-based API. And the existing C# Marshal methods are not always able to recognize managed structs and determine their size.

Therefore, a method has now been added to the ComponentRegister to allow the user to register components independently. He can specify the size himself and thus it is now also possible to add all managed structs.

public struct ManagedStruct{ public List<int> SomeInts{ get; set; } }

// Register
var componentType = new ComponentType(ComponentRegistry.Size, typeof(ManagedStruct), 8, false); // 8 = Size in bytes of the managed struct.
ComponentRegistry.Add(componentType);

// Use
var entity = world.Create(new ManagedStruct());

Contributions

Thanks to @DrSmugleaf for the PRs and fixing the compatibility with #define pure_ecs :)

Arch - 1.2.0

Published by genaray over 1 year ago

Bug fixes

  • Fixed an issue where the QueryArchetypeEnumerator throws an exception that was caused due to inaccurate pooling
  • => 32 components on an entity will not throws exceptions anymore
  • entity.Add(cmp) now works exactly as world.Add(in entity, cmp) does

Changes

  • Reworked some internals
  • ComponentRegistry now features several methods to modify registered components for hot reloading
  • Several other small changes in documentation and API

Contributors

Thanks to @stgeorge and @clibequilibrium for discovering bugs and other small missing features :)

Arch - 1.1.9

Published by genaray over 1 year ago

New Features

  • Fixed a structural changes related memory leak occurred by calling .Add or .Remove.
  • High-performance bulk operations for Destroy,Set,Add and Remove operations by targeting a range of entities using QueryDescription.
  • Some other small performance tweaks.

Bulk Operations

Until now, destroying entities or changing their component structure has always been a costly endeavor.
This is mainly due to the Archetype and Chunk architecture itself.

Bulk operations are now changing that. By means of these you can address all entities of a certain structure, which is significantly faster.

var query = new QueryDescription().WithAll<Player>();
world.Set(in query, new Position(), new Transform()); // Sets components for all entities fitting the query.
world.Add(in query, new KillTag(), new DropItems(),..); // Every single entity fitting the queryDescription will receive those components
world.Remove<Velocity, Defence>(in query);
world.Destroy(in query); 

Contributions

Many thanks to @Pheubel and @stgeorge for contributions and issues :)

Arch - 1.1.5

Published by genaray over 1 year ago

Changes

  • References<...> and EntityReferences<...> are now called Components<...> and EntityComponents<....>
  • Archetypes, chunk and the world do not automatically release resources anymore. That was slow as heck.
  • Entity version now works correctly.

New Features

  • World.TrimExcess() can now be called regularly to release unused memory and resources.
  • World.Reference(in entity) or entity.Reference() return a EntityReference used to reference entities.
  • World.Destroy is now slightly faster

Entity references

using var world = World.Create();

var entity = world.Create(_entityGroup);
var reference = world.Reference(entity);  // This should be stored in your component ( entity.Reference() is also available. )

// Checks if the entity is the same as before by comparing the version and its id
if(reference.IsAlive()) {
    var cmps = reference.Entity.Get(...);
}      

Bug fixes and contributors

Big thanks to @stgeorge , @Pheubel and @RoyAwesome for your bug discoverys and enhancements ! :)

Arch - 1.1.2

Published by genaray almost 2 years ago

Changes

  • HPQuery and HPEQuery, aswell as their parallel counterparts are now called InlineQuery, InlineEntityQuery e.g.
  • Removed ref iterators since they are incredible slow and can not be inlined #39
  • Proper documentation ( generated code is not documented however ) and code style enforcement.
  • Performance improvements

New Features

  • Classes can now be used as components :)
  • Several new overloads for Index, GetArray, GetSpan, GetFirst to improve performance and reduce boilerplate code.
  • Non generic set, get, has, add, remove operations for entities.
  • Added enumerator for entity inside chunk enumration.
  • world.CountEntities to... count entities which will be targeted by a query.

Non generic entity interaction :

var transform = (object)new Transform { X = 10, Y = 10 };
_entity.Set(transform);
var tramsformReference = (Transform)_entity.Get(typeof(Transform));

// Non generic additional methods
_entity.SetRange(...);
_entity.Has(...);
_entity.HasRange(...);
_entity.Get(...);
_entity.GetRange(...);
_entity.Add(...);
_entity.AddRange(...);
_entity.Remove(...);
_entity.RemoveRange(....);

Overloads for chunk & Chunk enumeration :

foreach (var chunk in _world.Query(in _queryDescription).GetChunkIterator())
{
    var refs = chunk.GetFirst<Transform, Velocity>(); // or chunk.GetSpan<Transform, Velocity>(out var trans, out var velos);
    foreach (var entity in chunk)  // New iteration, automatically iterates backwards over all valid chunk entities. 
    {
        ref var pos = ref Unsafe.Add(ref refs.t0, entity);
        ref var vel = ref Unsafe.Add(ref refs.t1, entity);

        pos.X += vel.X;
        pos.Y += vel.Y;
    }
}
Arch - 1.1.1

Published by genaray almost 2 years ago

Features & Changes

  • The World can now be modified during queries using world.Create, world.Destroy, world.Set, world.Add, world.Remove.
  • Queries and Enumerators now iterate backwards over the entities.
  • A new CommandBuffer-API is available which combines the previous ones.
  • QueryDescription now features a new API to reduce boilerplate : var desc = new QueryDescription().WithAll<Position, Velocity>....
  • Added Arch.Samples which provides a small usage sample with Arch and Monogame :)
  • Several small bug fixes and tweaks.

CommandBuffer

You no longer need several CommandBuffers, but a single one. The syntax is now much simpler.

var entity = world.Create<Transform, Rotation, int>();
var bufferedEntity = commandBuffer.Create(new ComponentType[] {typeof(Transform), typeof(Rotation), typeof(int) });  // Later also generic overloads to get rid of arrays. 
        
commandBuffer.Set(in entity, new Transform{ X = 20, Y = 20});
commandBuffer.Add(in entity, new Ai());
        
commandBuffer.Set(in bufferedEntity, new Transform{ X = 20, Y = 20});
commandBuffer.Add(in bufferedEntity, new Ai());

commandBuffer.Playback();

Acknowledgement

Many thanks to @andreakarasho for fixes and new features ! :)

Arch - 1.1.0

Published by genaray almost 2 years ago

Features & Changes

  • PURE_ECS preprocessor variable for utilizing pure ecs paradigma
  • Tag Components ( Empty components ) will not influence the amount of possible entities in a certain chunk
  • Entity.Null will not anymore create a new null entity each time its acessed
  • ComponentType is now required for several operations to provide additional component data and to prevent lookups
  • World.Id is now a int and not limited to 255 anymore
  • Structural changes, adds and removals are now way "smarter" and more efficient
  • Made several World, Archetype and Chunk methods internal for a better workflow and less headache
  • World will internally track entities and some meta data for faster lookup speed and less dictionarys
  • Major refactoring of internal structures Archetype and Chunk are now slimmer and act as pure containers without mapping entities and wasting space.
  • Several other little tweaks

Pure ECS

Theres a PURE_ECS preprocessor variable now which sets an entity to exactly one integer and disables all existing entity extension methods. This way the ECS can be accessed by a "pure ecs" mindset, thus more entities fit into one chunk and less unnecessary ballast is dragged around. This may only work with forks since the nugget package version will not have that flag enabled.

Increased lookup speed

Entities are now tracked in one single dictionary inside the world, this one stores archetype and chunk index data to provide instand acess. Compared to previous versions only one single lookup is now required instead of multiple ones, therefore the lookup speed increased by a lot.

Previous version:

Method Amount Mean Error StdDev Allocated
WorldEntityQuery 10000 513.765 us 297.3252 us 16.2974 us -
WorldEntityQuery 100000 5,032.846 us 2,116.4383 us 116.0091 us -
WorldEntityQuery 1000000 50,894.500 us 1,944.0534 us 106.5601 us -

Current version :

Method Amount Mean Error StdDev CacheMisses/Op Allocated
WorldEntityQuery 10000 147.660 us 13.2838 us 0.7281 us 746 -
WorldEntityQuery 100000 1,726.959 us 3,058.5935 us 167.6518 us 11,761 -
WorldEntityQuery 1000000 20,419.798 us 4,491.2760 us 246.1820 us 90,624 -

Contributors

@andreakarasho thanks for pointing out issues with commandbuffers and the entity tweaks ! :)

Arch - 1.0.17

Published by genaray almost 2 years ago

Bug Fixes

Fixed the acessability of CreationBuffer and ModificationBuffer constructors, thanks @andreakarasho ! :)
You may now use them like this...

var creationBuffer = new CreationBuffer(world, 1024); <- Initial capacity, not total capacity 
world.ParallelQuery(in desc, (in Entity en) => {

   var entity = creationBuffer.Create(group); 
   entity.Set<Explosion>(...);
   entity.Set<Position>(...);
});
creationBuffer.Playback();  // Must happen after a queries on the mainthread. 

or this...

var buffer = new ModificationBuffer(world, 1024);
world.ParallelQuery(in desc, (in Entity en) => {
   var modifiedEntity = buffer.Modify(in en);
   buffer.Set(...);
});
buffer.Playback();
Arch - 1.0.16

Published by genaray almost 2 years ago

New Features

Several new features were added in this release, especially gameplay relevant features to ease development.

Exclusive Queries

With the previous query API you could not target exclusive entity architectures.
This now has changed.

var exclusiveGroup = new[] { typeof(Transform), typeof(Rotation) };
var query = new QueryDescription { Exclusive = exclusiveGroup };  // Only targets entities with EXACTLY those components.
world.Query(in query, (in Entity en) => {});  // Only queries entities with "Transform" and "Rotation", no entities with other components. 

Huge thanks to @MindSwipe for the contribution of the exclusive queries ! :)

Command Buffers

Due to the iteration mechanism it was not possible to the world or the entity itself during queries. Now theres a way for exactly those purposes, called "Buffers". They "buffer" operations to play them back after a query.

There 4 specialised buffers, one for each operation :

  • CreationBuffer - A buffer which records creation commands
  • ModificationBuffer - A Buffer which records set operations
  • StructuralBuffer - A buffer which records structural operations
  • DestructionBuffer - A buffer which records destroy operations

A small example :

var creationBuffer = new CreationBuffer(world, 1024); <- Initial capacity, not total capacity 
world.ParallelQuery(in desc, (in Entity en) => {

   var entity = creationBuffer.Create(group); 
   entity.Set<Explosion>(...);
   entity.Set<Position>(...);
});
creationBuffer.Playback();  // Must happen after a queries on the mainthread. 

They are all thread safe and can be used with parallel queries.

More Parallel methods

The parallel queries were extended by a few methods like...

world.ParallelQuery(in desc, (in Entity en ) => {});
...
Arch - 1.0.15

Published by genaray almost 2 years ago

New Features

Lots of new generic overloads for world, Chunk, Archetype and Entity. To ease the development for the enduser and to operate on entities more efficient by batching calls.

All of the types above received .Has<T0...T9>, .Get<T0...T9>, .Set<T0...T9>, .Add<T0...T9> and .Remove<T0...T9> methods. The world also received world.Create<T0...T9> as an alternative to the world.Create(Type[]) call.

Using generic overloads instead of multiple single calls is way more efficient and faster.


var entity = world.Create(archetype);
entity.Set(new Transform());
entity.Set(new Movement());
ref var t = ref entity.Get<Transform>();
ref var m = ref entity.Get<Movement>();

becomes

var entity = world.Create(new Transform(), new Movement());
var refs = entity.Get<Transform, Movement>();
Arch - 1.0.14

Published by genaray almost 2 years ago

Changes

  • ComponentIdToArrayIndex was a dictionary inside Chunk and is now a lookup table instead
  • Most entity related operations are now likely twice as fast since the dictionary lookup was avoided.
  • Some code removal
Arch - 1.0.13

Published by genaray almost 2 years ago

Changes & new Features

  • Structural changes ( Add, Remove Components )
  • Type[] arrays are now mappd to their specific Archetype by a unique hash
  • Due to that unique hash the component order does not matter anymore Transform, Rotation & Rotation, Transform are handled the same
  • World now features all entity related methods and EntityExtensions just forward them for easier API usage.
  • World,Archetype, Chunk are now divded into partial classes/structs by features to give a better overview.
  • Added test for structural changes in WorldTest and ArchetypeTest

Structural changes

Structural changes move entities from one to another archetype. It is used to remove and add components to entities during runtime.
This should never happen during a query.

world.Add<T>(in entity, new T());
world.Remove<T>(in entity);

entity.Add<T>(new T());
entity.Remove<T>();
Arch - 1.0.12

Published by genaray almost 2 years ago

Fixed a bug where queries not targeting any entities would throw a NullReferenceException #11.

Thanks @MindSwipe for the discovery and fix of this bug ! :)

Arch - 1.0.11

Published by genaray almost 2 years ago

Fixed a bug ( #9 ) where similar structured archetypes could exist next to each other, aswell as a similar bug for queries.

Big thanks at @MindSwipe and his PR #10 for noticing and fixing this bug :)