Skip to content

Commit 7fa54a1

Browse files
Merge pull request #39 from andreakarasho/feat/change-detection-again
Added/Changed filters for bevy queries
2 parents fe7f15e + 6a47317 commit 7fa54a1

File tree

11 files changed

+828
-371
lines changed

11 files changed

+828
-371
lines changed

samples/MyBattleground/Program.cs

Lines changed: 96 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,66 +11,125 @@
1111
var scheduler = new Scheduler(ecs);
1212

1313

14-
scheduler.AddState(GameState.Loading);
15-
scheduler.AddState(AnotherState.C);
1614

17-
scheduler.OnUpdate(() =>
18-
{
19-
Console.WriteLine("im in loading state");
20-
}, ThreadingMode.Single)
21-
.RunIf((SchedulerState state) => state.InState(GameState.Loading))
22-
.RunIf((SchedulerState state) => state.InState(AnotherState.A));
15+
// scheduler.OnUpdate((Query<Data<Position>, Changed<Position>> query) =>
16+
// {
17+
// foreach ((var ent, var pos) in query)
18+
// {
2319

20+
// }
21+
// }, ThreadingMode.Single);
2422

25-
scheduler.OnEnter(GameState.Loading, () => Console.WriteLine("on enter loading"), ThreadingMode.Single);
26-
scheduler.OnEnter(GameState.Loading, () => Console.WriteLine("on enter loading 2"), ThreadingMode.Single);
27-
scheduler.OnExit(GameState.Loading, () => Console.WriteLine("on exit loading"), ThreadingMode.Single);
2823

29-
scheduler.OnEnter(GameState.Playing, () => Console.WriteLine("on enter playing"), ThreadingMode.Single);
30-
scheduler.OnExit(GameState.Playing, () => Console.WriteLine("on exit playing"), ThreadingMode.Single);
24+
var ab = ecs.Entity()
25+
.Set(new Position()).Set(new Velocity());
26+
// ecs.Entity()
27+
// .Set(new Position() { X = -1 });
28+
// // var q = ecs.QueryBuilder()
29+
// // .With<Position>()
30+
// // .Changed<Position>()
31+
// // .Build();
3132

32-
scheduler.OnEnter(GameState.Menu, () => Console.WriteLine("on enter Menu"), ThreadingMode.Single);
33-
scheduler.OnExit(GameState.Menu, () => Console.WriteLine("on exit Menu"), ThreadingMode.Single);
33+
// // var a = new QueryIter<Data<Position>, Changed<Position>>(q.Iter());
3434

35-
scheduler.OnUpdate((State<GameState> state, State<AnotherState> anotherState, Local<float> loading, Local<GameState[]> states, Local<int> index) =>
36-
{
37-
states.Value ??= Enum.GetValues<GameState>();
35+
// // foreach ((var ent, var pos) in a)
36+
// // {
37+
// // pos.Ref.X *= 2;
38+
// // pos.Ref.Y *= 2;
39+
// // }
3840

39-
loading.Value += 0.1f;
40-
// Console.WriteLine("next {0:P}", loading.Value);
4141

42-
Console.WriteLine("current state: {0}", state.Current);
42+
// // foreach ((var ent, var pos) in a)
43+
// // {
44+
// // pos.Ref.X *= 2;
45+
// // pos.Ref.Y *= 2;
46+
// // }
4347

44-
if (loading.Value >= 1f)
45-
{
46-
loading.Value = 0f;
47-
// Console.WriteLine("on swapping state");
48-
state.Set(states.Value[(++index.Value) % states.Value.Length]);
49-
anotherState.Set(AnotherState.A);
50-
}
5148

52-
}, threadingType: ThreadingMode.Single);
49+
// scheduler.RunOnce();
50+
// scheduler.RunOnce();
5351

52+
ab.Set(new Position() { X = 2 });
53+
// scheduler.RunOnce();
5454

55-
while (true)
56-
scheduler.RunOnce();
55+
// scheduler.AddState(GameState.Loading);
56+
// scheduler.AddState(AnotherState.C);
57+
58+
// scheduler.OnUpdate(() =>
59+
// {
60+
// Console.WriteLine("im in loading state");
61+
// }, ThreadingMode.Single)
62+
// .RunIf((SchedulerState state) => state.InState(GameState.Loading))
63+
// .RunIf((SchedulerState state) => state.InState(AnotherState.A));
64+
65+
66+
// scheduler.OnEnter(GameState.Loading, () => Console.WriteLine("on enter loading"), ThreadingMode.Single);
67+
// scheduler.OnEnter(GameState.Loading, () => Console.WriteLine("on enter loading 2"), ThreadingMode.Single);
68+
// scheduler.OnExit(GameState.Loading, () => Console.WriteLine("on exit loading"), ThreadingMode.Single);
69+
70+
// scheduler.OnEnter(GameState.Playing, () => Console.WriteLine("on enter playing"), ThreadingMode.Single);
71+
// scheduler.OnExit(GameState.Playing, () => Console.WriteLine("on exit playing"), ThreadingMode.Single);
72+
73+
// scheduler.OnEnter(GameState.Menu, () => Console.WriteLine("on enter Menu"), ThreadingMode.Single);
74+
// scheduler.OnExit(GameState.Menu, () => Console.WriteLine("on exit Menu"), ThreadingMode.Single);
75+
76+
// scheduler.OnUpdate((State<GameState> state, State<AnotherState> anotherState, Local<float> loading, Local<GameState[]> states, Local<int> index) =>
77+
// {
78+
// states.Value ??= Enum.GetValues<GameState>();
79+
80+
// loading.Value += 0.1f;
81+
// // Console.WriteLine("next {0:P}", loading.Value);
82+
83+
// Console.WriteLine("current state: {0}", state.Current);
84+
85+
// if (loading.Value >= 1f)
86+
// {
87+
// loading.Value = 0f;
88+
// // Console.WriteLine("on swapping state");
89+
// state.Set(states.Value[(++index.Value) % states.Value.Length]);
90+
// anotherState.Set(AnotherState.A);
91+
// }
92+
93+
// }, threadingType: ThreadingMode.Single);
94+
95+
96+
// while (true)
97+
// scheduler.RunOnce();
5798

5899
for (int i = 0; i < ENTITIES_COUNT; i++)
59100
ecs.Entity()
60101
.Set<Position>(new Position())
61102
.Set<Velocity>(new Velocity());
62103

63-
ecs.Entity().Set(new Position()).Set(new Velocity()).Set(new Mass());
104+
// ecs.Entity().Set(new Position()).Set(new Velocity()).Set(new Mass());
64105

65106

66107

67-
scheduler.AddSystem((Query<Data<Position, Velocity>> q) =>
108+
scheduler.AddSystem((
109+
Query<Data<Position, Velocity>, With<Position>> q,
110+
Query<Data<Position, Velocity>, Added<Position>> added
111+
) =>
68112
{
69-
foreach ((var ent, var pos, var vel) in q)
113+
foreach ((var pos, var vel) in q)
70114
{
71115
pos.Ref.X *= vel.Ref.X;
72116
pos.Ref.Y *= vel.Ref.Y;
117+
118+
// pos.Ref.X *= vel.Ref.X;
119+
// pos.Ref.Y *= vel.Ref.Y;
120+
121+
// if (pos.IsChanged)
122+
// pos.ClearState();
73123
}
124+
125+
// foreach ((var pos, var vel) in added)
126+
// {
127+
// pos.Ref.X *= vel.Ref.X;
128+
// pos.Ref.Y *= vel.Ref.Y;
129+
130+
// // if (pos.IsAdded)
131+
// // pos.ClearState();
132+
// }
74133
}, threadingType: ThreadingMode.Single);
75134

76135

@@ -87,9 +146,9 @@
87146
{
88147
for (int i = 0; i < 3600; ++i)
89148
{
90-
// scheduler.RunOnce();
149+
scheduler.RunOnce();
91150

92-
Execute(query);
151+
// Execute(query);
93152
// ExecuteIterator(query);
94153

95154
// var it = query.Iter();
@@ -121,7 +180,7 @@
121180

122181
static void Execute(Query query)
123182
{
124-
foreach ((var ent, var pos, var vel) in Data<Position, Velocity>.CreateIterator(query.Iter()))
183+
foreach ((var pos, var vel) in Data<Position, Velocity>.CreateIterator(query.Iter()))
125184
{
126185
pos.Ref.X *= vel.Ref.X;
127186
pos.Ref.Y *= vel.Ref.Y;

samples/TinyEcsGame/Program.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55

66
using var app = new App();
7-
app.AddPlugin(new RaylibPlugin() {
8-
WindowSize = new () { Value = { X = 800, Y = 600 } },
7+
app.AddPlugin(new RaylibPlugin()
8+
{
9+
WindowSize = new() { Value = { X = 800, Y = 600 } },
910
Title = "TinyEcs using raylib",
1011
VSync = true
1112
});
1213

13-
app.AddPlugin(new GameRootPlugin() {
14+
app.AddPlugin(new GameRootPlugin()
15+
{
1416
EntitiesToSpawn = 1_000_00,
1517
Velocity = 250
1618
});
@@ -22,7 +24,7 @@
2224
// =================================================================================
2325
sealed class App : Scheduler, IDisposable
2426
{
25-
public App() : base(new ()) { }
27+
public App() : base(new()) { }
2628

2729
public void Dispose() => World?.Dispose();
2830
}
@@ -64,7 +66,8 @@ public readonly void Build(Scheduler scheduler)
6466
scheduler.AddResource(WindowSize);
6567
scheduler.AddResource(new AssetsManager());
6668

67-
scheduler.AddSystem((Time time) => {
69+
scheduler.AddSystem((Time time) =>
70+
{
6871
time.Frame = Raylib.GetFrameTime();
6972
time.Total += time.Frame;
7073
}, Stages.BeforeUpdate);
@@ -228,9 +231,9 @@ static void RenderEntities(Query<Data<Sprite, Position, Rotation>> query, Res<As
228231

229232
static void DrawText(World ecs, Time time, Local<string> text, Local<float> timeout)
230233
{
231-
if (time.Total > timeout)
234+
//if (time.Total > timeout)
232235
{
233-
timeout.Value = time.Total + 0.25f;
236+
// timeout.Value = time.Total + 0.10f;
234237
text.Value = $"""
235238
[Debug]
236239
FPS: {Raylib.GetFPS()}
@@ -246,7 +249,7 @@ static void DrawText(World ecs, Time time, Local<string> text, Local<float> time
246249
// =================================================================================
247250
sealed class AssetsManager
248251
{
249-
private readonly Dictionary<uint, Texture2D> _ids = new ();
252+
private readonly Dictionary<uint, Texture2D> _ids = new();
250253

251254
public void Register(Texture2D texture)
252255
{
@@ -268,23 +271,23 @@ struct WindowSize
268271

269272
struct Position
270273
{
271-
public Vector2 Value;
274+
public Vector2 Value;
272275
}
273276

274277
struct Velocity
275278
{
276-
public Vector2 Value;
279+
public Vector2 Value;
277280
}
278281

279282
struct Sprite
280283
{
281-
public Color Color;
282-
public float Scale;
284+
public Color Color;
285+
public float Scale;
283286
public uint TextureId;
284287
}
285288

286289
struct Rotation
287290
{
288-
public float Value;
289-
public float Acceleration;
291+
public float Value;
292+
public float Acceleration;
290293
}

src/Archetype.cs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections.Frozen;
2-
using System.Numerics;
32

43
namespace TinyEcs;
54

@@ -8,19 +7,33 @@ namespace TinyEcs;
87
internal readonly struct Column
98
{
109
public readonly Array Data;
11-
// public readonly uint[] Changed;
10+
public readonly uint[] ChangedTicks, AddedTicks;
1211

1312
internal Column(ref readonly ComponentInfo component, int chunkSize)
1413
{
1514
Data = Lookup.GetArray(component.ID, chunkSize)!;
16-
// Changed = new uint[chunkSize];
15+
ChangedTicks = new uint[chunkSize];
16+
AddedTicks = new uint[chunkSize];
1717
}
1818

19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
public void MarkChanged(int index, uint ticks)
21+
{
22+
ChangedTicks[index] = ticks;
23+
}
24+
25+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
26+
public void MarkAdded(int index, uint ticks)
27+
{
28+
AddedTicks[index] = ticks;
29+
}
1930

2031
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2132
public void CopyTo(int srcIdx, ref readonly Column dest, int dstIdx)
2233
{
2334
Array.Copy(Data, srcIdx, dest.Data, dstIdx, 1);
35+
dest.ChangedTicks[dstIdx] = ChangedTicks[srcIdx];
36+
dest.AddedTicks[dstIdx] = AddedTicks[srcIdx];
2437
}
2538
}
2639

@@ -35,7 +48,7 @@ internal ArchetypeChunk(ReadOnlySpan<ComponentInfo> sign, int chunkSize)
3548
Entities = new EntityView[chunkSize];
3649
Columns = new Column[sign.Length];
3750
for (var i = 0; i < sign.Length; ++i)
38-
Columns[i] = new(in sign[i], chunkSize); // Lookup.GetArray(sign[i].ID, chunkSize)!;
51+
Columns[i] = new(in sign[i], chunkSize);
3952
}
4053

4154
public int Count { get; internal set; }
@@ -103,6 +116,18 @@ public readonly Span<T> GetSpan<T>(int column) where T : struct
103116
[MethodImpl(MethodImplOptions.AggressiveInlining)]
104117
public readonly ReadOnlySpan<EntityView> GetEntities()
105118
=> Entities.AsSpan(0, Count);
119+
120+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
121+
public void MarkChanged(int column, int row, uint ticks)
122+
{
123+
Columns![column].MarkChanged(row & Archetype.CHUNK_THRESHOLD, ticks);
124+
}
125+
126+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
127+
public void MarkAdded(int column, int row, uint ticks)
128+
{
129+
Columns![column].MarkAdded(row & Archetype.CHUNK_THRESHOLD, ticks);
130+
}
106131
}
107132

108133
public sealed class Archetype : IComparable<Archetype>
@@ -122,7 +147,6 @@ public sealed class Archetype : IComparable<Archetype>
122147
, _pairsLookup
123148
#endif
124149
;
125-
private readonly FrozenSet<EcsID> _ids;
126150
internal readonly List<EcsEdge> _add, _remove;
127151
private int _count;
128152
private readonly int[] _fastLookup;
@@ -182,15 +206,14 @@ ComponentComparer comparer
182206
.ToFrozenDictionary(s => s.Key, v => v.First().Value);
183207
#endif
184208

185-
_ids = All.Select(s => s.ID).ToFrozenSet();
186-
_add = new ();
187-
_remove = new ();
209+
_add = new();
210+
_remove = new();
188211
}
189212

190213

191214
public World World => _world;
192215
public int Count => _count;
193-
public readonly ComponentInfo[] All, Components, Tags, Pairs;
216+
public readonly ComponentInfo[] All, Components, Tags, Pairs = Array.Empty<ComponentInfo>();
194217
public EcsID Id { get; }
195218
internal ReadOnlySpan<ArchetypeChunk> Chunks => _chunks.AsSpan(0, (_count + CHUNK_SIZE - 1) >> CHUNK_LOG2);
196219
internal int EmptyChunks => _chunks.Length - ((_count + CHUNK_SIZE - 1) >> CHUNK_LOG2);
@@ -495,19 +518,19 @@ internal void GetSuperSets(ReadOnlySpan<IQueryTerm> terms, List<Archetype> match
495518

496519
internal ArchetypeSearchResult MatchWith(ReadOnlySpan<IQueryTerm> terms)
497520
{
498-
return FilterMatch.Match(_ids, terms);
521+
return FilterMatch.Match(this, terms);
499522
}
500523

501524
public void Print(int depth)
502-
{
503-
Console.WriteLine(new string(' ', depth * 2) + $"Node: [{string.Join(", ", All.Select(s => s.ID))}]");
504-
505-
foreach (ref var edge in CollectionsMarshal.AsSpan(_add))
506-
{
507-
Console.WriteLine(new string(' ', (depth + 1) * 2) + $"Edge: {edge.Id}");
508-
edge.Archetype.Print(depth + 2);
509-
}
510-
}
525+
{
526+
Console.WriteLine(new string(' ', depth * 2) + $"Node: [{string.Join(", ", All.Select(s => s.ID))}]");
527+
528+
foreach (ref var edge in CollectionsMarshal.AsSpan(_add))
529+
{
530+
Console.WriteLine(new string(' ', (depth + 1) * 2) + $"Edge: {edge.Id}");
531+
edge.Archetype.Print(depth + 2);
532+
}
533+
}
511534

512535
public int CompareTo(Archetype? other)
513536
{

0 commit comments

Comments
 (0)