EcsBase 1.0.0
dotnet add package EcsBase --version 1.0.0
NuGet\Install-Package EcsBase -Version 1.0.0
<PackageReference Include="EcsBase" Version="1.0.0" />
<PackageVersion Include="EcsBase" Version="1.0.0" />
<PackageReference Include="EcsBase" />
paket add EcsBase --version 1.0.0
#r "nuget: EcsBase, 1.0.0"
#:package EcsBase@1.0.0
#addin nuget:?package=EcsBase&version=1.0.0
#tool nuget:?package=EcsBase&version=1.0.0
EcsBase
A minimalist starting point for using ECS in C#.
🧠 The Idea
ECS libraries tend to come with a list of features,
which is what the library maintainer(s) deem useful.
However, these lists varying greatly between libraries shows there is no "silver bullet".
Instead, the idea for EcsBase is:
- Add this library via NuGet and start developing.
- Use it through a wrapper or a replacement for
EcsService
and start developing. - When reaching EcsBase's limits, copy its files and quickly understand and modify them.
Now, let's go through those steps!
📦 1. Add the Package
dotnet add package EcsBase
🚀 2. Use the Library
2.1 Add the EcsBase namespace 🏷️
using EcsBase;
2.2 Create a world 🌍
var ecsService = EcsService<int>.Create();
This creates a new EcsService
, which contains a World
, a ChangeBuffer
, and a QueryExecutor
.
We are using int
as the key type here - this is what is usually called the "entity".
2.3 Add an Entity 👤
ecsService
.ChangeBuffer
.AddEntity(7)
.WithComponent(new Transform2D { Position = new Vector2(1.0f, 3.0f) });
ecsService.ChangeBuffer.ApplyChanges();
Identifiers
Note that you are responsible for providing a unique key for each entity
(hardcoded to 7 in this example).
Component Types
Components can be any class or struct, really.
Of course, structs would have a performance advantage,
but see the section about performance further down.
ChangeBuffer
The actual lists of components should not be changed while iterating over it.
⚠️ Note: There are no built-in protections that prevent you from doing this! ⚠️
That is why ChangeBuffer
comes with an ApplyChanges()
method.
This will actually apply any changes that are requested via its other methods.
So, call ChangeBuffer.ApplyChanges()
at the beginning or end of a frame and you're good.
2.4 Execute a query 🚀
public class MyQuery : IQuery<int, Transform2D>
{
public void Execute(Span<int> e, Span<Transform2D> component1Span)
{
for (int i = 0; i < e.Length; i++)
{
component1Span[i].Position.X++;
}
}
}
var myQuery = new MyQuery();
ecsService.Queries.Execute<MyQuery, Transform2D, ComponentB>(myQuery);
The first generic type parameter of IQuery
is the key type,
the rest defines which components an entity needs to have to be part of the query.
🏗️ 3. Modify the Library
🗂️ File Overview
- Component state
Chunk
holds a somewhat memory-optimized collection of componentsChunkList
holds multiple chunks, and creates new chunks when necessaryArchetype
is basically just a collection of typesWorld
manages aChunkList
perArchetype
, and keeps track of entities
- Editing entities
EntityBuilder
is a fluent interface to create entitiesChangeBuffer
helps with delaying changes to the start or end of a frame
- Querying
IQuery
contains 9 variations (1-9 component parameters) of the query interfaceQueryExecutor
contains 9 methods to run these queries
- And finalle
EcsService
ties it all together as a common entry point
"Features"
🦺 Safe
No unsafe
code. I have seen this advertised as a bonus.
To be honest, I do not know where that would come into play -
but yeah, there is no direct memory-wrangling involved.
🤏 Minimalism
The entire code is in 9 files, all but one smaller than 200 lines of code.
The library is only 35Kb in size (not that this would be unexpected for, well, 9 files).
It can arguably not even be called an "Entity/Component/System" library,
because there is no system (IQuery
hardly qualifies).
It's not an ECS, just an "EC".
Strike that, an entity is not provided either.
You can freely choose what to use as entity via the TKey
generic parameter.
int
, long
, Guid
- or just any class.
So it's not an ECS, just a "C"... wouldn't that be a confusing name.
🏎️ Performance
Performance is somewhat of an afterthought here.
Components inside a Chunk
are stored contiguously per component,
and which ChunkList
s are applicable for a specific combination of components is cached.
I'm simply not making the type of game where I need to process 100 000 entities per frame.
Anyway, 3 different queries over different combinations of components on 100 000 entities
take about 100 microseconds on my machine. (See the EcsBase.Benchmarks
project.)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- 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.0.0 | 113 | 10/25/2024 |
0.0.1-alpha | 90 | 10/25/2024 |