Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and improvements #183

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
71 changes: 63 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,23 @@ Entities are created as such:
Entity entity = world.CreateEntity();
```

You should not store entities yourself and rely as much as possible on the returned objects from a world query as those will be updated accordingly to component changes.
To clear an entity, simply call its `Dispose` method.
```csharp
entity.Dispose();
```

You should not store entities yourself and rely as much as possible on the returned objects from a world query as those will be updated accordingly to component changes.
If you absolutely need to store it separately and found related hard-to-find bug, you might consider temporary enabling runtime entity misuse checks:
```csharp
Entity entity = world.CreateEntity();
Entity.IsMisuseDetectionEnabled = true; // this property is available in release as well, but is not used
entity.Set<bool>();
entity.Dispose();
if (entity.Has<bool>()) {
// in debug configuration you will get entity misuse exception trying to perform Has<T> there as the entity is already dead.
}
```

Once disposed, you should not use the entity again. If you need a safeguard, you can check the `IsAlive` property:
```csharp
#if DEBUG
Expand All @@ -138,6 +149,15 @@ if (!entity.IsAlive)
#endif
```

There is faster alternative to IsAlive property:
```csharp
if (!entity.IsAliveVersion)
{
// make sure to only use this if you are sure entity was at least valid before, and its world is still alive
// i.e created from existing valid world through CreateEntity method, and not through default struct constructor
}
```

You can also make an entity act as if it was disposed so it is removed from world queries while keeping all its components, this is useful when you need to activate/deactivate an entity from your game logic:
```csharp
entity.Disable();
Expand Down Expand Up @@ -311,6 +331,7 @@ entity.Set(ManagedResource<Texture2D>.Create("square.png", "circle.png")); // se
textureResourceManager.Manage(_world);
```
This feature only cares for entity components, not the world components.
Plus, it's intended mostly for init, so keep it in mind before trying to use it in any non-init systems as the performance might not be the best.

## Query
To perform operations, systems should query entities from the world. This is performed by requesting entities through the world and using the fluent API to create rules
Expand Down Expand Up @@ -368,9 +389,39 @@ world
.AsMap<TKey>();
```
Gets an `EntityMap<TKey>` which maps a single entity with a component type of `TKey` value. Its content is cached for fast access and is automatically updated as you change your entity component composition.
If `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.
You can think of it as ecs-framework intergated version of `Dictionary<TKey,Entity>`.
This is useful if you need O(1) access to an entity based on a key.

For complex situations when you want to compare specific field(s) instead of whole component, you can pass `IEqualityComparer<TKey>` when creating the map:
```csharp
public static class TestsApplication {
public static void Main() {
World testWorld = new World();
var comparer = new TestComponentComparer();
var testComponentMap = testWorld.GetEntities().AsMap<TestComponent>(comparer);

Entity one = testWorld.CreateEntity();
Entity two = testWorld.CreateEntity();
Entity three = testWorld.CreateEntity();
one.Set(new TestComponent() { UniqueID = 1, SomeBool = true });
two.Set(new TestComponent() { UniqueID = 1, SomeBool = false });

// Now we will get error because EntityMap we have only cares about the bool equality
three.Set(new TestComponent() { UniqueID = 2, SomeBool = true });
}
}
public struct TestComponent {
public byte UniqueID;
public bool SomeBool;
}
public class TestComponentComparer : IEqualityComparer<TestComponent> {
bool IEqualityComparer<TestComponent>.Equals(TestComponent x, TestComponent y) => x.SomeBool == y.SomeBool;
int IEqualityComparer<TestComponent>.GetHashCode(TestComponent obj) => obj.GetHashCode();
}
```

If `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.

### AsMultiMap
```csharp
world
Expand All @@ -379,8 +430,11 @@ world
.AsMultiMap<TKey>();
```
Gets an `EntityMultiMap<TKey>` which maps multiple entities with a component type of `TKey` value. Its content is cached for fast access and is automatically updated as you change your entity component composition.
If `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.
You can think of it as ecs-framework intergated version of `Dictionary<TKey,Entity[]>`.
This is useful if you need O(1) access to entities based on a key.
Just like `EntityMap<TKey>` it supports custom component equality by passing `IEqualityComparer<TKey>`

If `When...` rules are present, you should call its `Complete()` method once you are done processing its entities to clear it.

## System
Although there is no obligation to use them, a set of base classes is provided to help with the creation of systems:
Expand Down Expand Up @@ -495,14 +549,15 @@ public sealed class VelocitySystem : AEntitySetSystem<float>

It is also possible to declare the component types by using the `WithAttribute` and `WithoutAttribute` on the system type:
```csharp
[With(typeof(Velocity)]
[With<Velocity>]
[With(typeof(Position)]
public sealed class VelocitySystem : AEntitySetSystem<float>
{
public VelocitySystem(World world, IParallelRunner runner)
: base(world, runner)
{
}
// Note the usage of useBuffer paramater in constructor.
// Setting it to false will yield us better performance.
// But now setting removing, or enabling-disabling components on entity that are part of filter (namely, Velocity and Position) is no longer safe.
// Manipulating entity (i.e. disabling it, or disposing) is now also no longer safe.
public VelocitySystem(World world) : base(world, useBuffer: false) { }

protected override void Update(float elapsedTime, in Entity entity)
{
Expand Down
1 change: 1 addition & 0 deletions documentation/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Once the dlls inside your Unity project you will then need to disable `Validate
Note that this feature has only been available since a specific version of Unity.

You should then be able to use DefaultEcs in your Unity project. Keep in mind that if you choose the IL2CPP backend, some features will not work (the provided serializers) and some others will require extra code (check [AoTHelper](https://github.com/Doraku/DefaultEcs/blob/master/documentation/api/DefaultEcs-AoTHelper.md)).
You may also want to compile the framework manually while removing all System.Reflection.Emit-using code (the serializers code mentioned above) as it causes unity IL2CPP-based projects fail to build.

<a name='system_decorator'></a>
## How to update systems at half the framerate?
Expand Down
1 change: 1 addition & 0 deletions documentation/NEXT_RELEASENOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- added World.SubscribeWorldComponentChanged method (#165)
- added World.SubscribeWorldComponentRemoved method (#165)
- added World.NotifyChanged method
- added Entity.IsAliveVersion property
- added generic WithAttribute
- added generic WithoutAttribute
- added generic WhenAddedAttribute
Expand Down
6 changes: 0 additions & 6 deletions source/DefaultEcs.sln
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,11 @@ Global
{BD4F95D2-FB6F-4317-8B0A-58FBDF536C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD4F95D2-FB6F-4317-8B0A-58FBDF536C0B}.Release|Any CPU.Build.0 = Release|Any CPU
{6D2BA0F6-177E-454B-8AD7-42AF0AD539B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D2BA0F6-177E-454B-8AD7-42AF0AD539B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D2BA0F6-177E-454B-8AD7-42AF0AD539B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D2BA0F6-177E-454B-8AD7-42AF0AD539B7}.Release|Any CPU.Build.0 = Release|Any CPU
{95F4D6A0-FD01-4AA8-B603-5811C9FFAEEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95F4D6A0-FD01-4AA8-B603-5811C9FFAEEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95F4D6A0-FD01-4AA8-B603-5811C9FFAEEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95F4D6A0-FD01-4AA8-B603-5811C9FFAEEA}.Release|Any CPU.Build.0 = Release|Any CPU
{198A12F3-B396-4243-BFD4-B93FA488AB76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{198A12F3-B396-4243-BFD4-B93FA488AB76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{198A12F3-B396-4243-BFD4-B93FA488AB76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{198A12F3-B396-4243-BFD4-B93FA488AB76}.Release|Any CPU.Build.0 = Release|Any CPU
{17280FCB-26FC-4999-8831-2810AF764B86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17280FCB-26FC-4999-8831-2810AF764B86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17280FCB-26FC-4999-8831-2810AF764B86}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
6 changes: 1 addition & 5 deletions source/DefaultEcs/DefaultEcs.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Compilation">
<TargetFrameworks>
netstandard1.1;
netstandard2.0;
netstandard2.1;
</TargetFrameworks>
Expand All @@ -13,10 +12,7 @@
<None Include="..\..\image\logo.png" Pack="true" PackagePath="\" Visible="false" />
</ItemGroup>

<ItemGroup Label="Reference" Condition="'$(TargetFramework)'=='netstandard1.1'">
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Label="Reference" Condition="'$(TargetFramework)'=='netstandard1.1' Or '$(TargetFramework)'=='netstandard2.0'">
<ItemGroup Label="Reference" Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
</ItemGroup>
Expand Down
Loading
Loading