Skip to content

Commit 902b427

Browse files
Merge pull request #38 from andreakarasho/feat/states
Feat/states
2 parents 3ff5502 + a74c6a2 commit 902b427

File tree

3 files changed

+489
-36
lines changed

3 files changed

+489
-36
lines changed

samples/MyBattleground/Program.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,61 @@
1010
using var ecs = new World();
1111
var scheduler = new Scheduler(ecs);
1212

13+
14+
scheduler.AddState(GameState.Loading);
15+
scheduler.AddState(AnotherState.C);
16+
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));
23+
24+
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);
28+
29+
scheduler.OnEnter(GameState.Playing, () => Console.WriteLine("on enter playing"), ThreadingMode.Single);
30+
scheduler.OnExit(GameState.Playing, () => Console.WriteLine("on exit playing"), ThreadingMode.Single);
31+
32+
scheduler.OnEnter(GameState.Menu, () => Console.WriteLine("on enter Menu"), ThreadingMode.Single);
33+
scheduler.OnExit(GameState.Menu, () => Console.WriteLine("on exit Menu"), ThreadingMode.Single);
34+
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>();
38+
39+
loading.Value += 1f;
40+
// Console.WriteLine("next {0:P}", loading.Value);
41+
42+
Console.WriteLine("current state: {0}", state.Current);
43+
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+
}
51+
52+
}, threadingType: ThreadingMode.Single);
53+
54+
55+
while (true)
56+
scheduler.RunOnce();
57+
1358
for (int i = 0; i < ENTITIES_COUNT; i++)
1459
ecs.Entity()
1560
.Set<Position>(new Position())
1661
.Set<Velocity>(new Velocity());
1762

1863
ecs.Entity().Set(new Position()).Set(new Velocity()).Set(new Mass());
1964

20-
scheduler.AddSystem((Query<Data<Position, Velocity>> q)=>
65+
66+
67+
scheduler.AddSystem((Query<Data<Position, Velocity>> q) =>
2168
{
2269
foreach ((var ent, var pos, var vel) in q)
2370
{
@@ -115,3 +162,17 @@ struct Velocity
115162
struct Mass { public float Value; }
116163

117164
struct Tag { }
165+
166+
167+
enum GameState
168+
{
169+
Loading,
170+
Playing,
171+
Menu,
172+
Menu2
173+
}
174+
175+
enum AnotherState
176+
{
177+
A, B, C
178+
}

src/Bevy.cs

Lines changed: 146 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ public enum Stages
127127
BeforeUpdate,
128128
Update,
129129
AfterUpdate,
130-
FrameEnd
130+
FrameEnd,
131+
132+
OnEnter,
133+
OnExit
131134
}
132135

133136
public enum ThreadingMode
@@ -140,10 +143,11 @@ public enum ThreadingMode
140143
public partial class Scheduler
141144
{
142145
private readonly World _world;
143-
private readonly LinkedList<FuncSystem<World>>[] _systems = new LinkedList<FuncSystem<World>>[(int)Stages.FrameEnd + 1];
146+
private readonly LinkedList<FuncSystem<World>>[] _systems = new LinkedList<FuncSystem<World>>[(int)Stages.OnExit + 1];
144147
private readonly List<FuncSystem<World>> _singleThreads = new();
145148
private readonly List<FuncSystem<World>> _multiThreads = new();
146149
private readonly Dictionary<Type, IEventParam> _events = new();
150+
private readonly Dictionary<Type, IState> _states = new();
147151

148152
public Scheduler(World world)
149153
{
@@ -172,9 +176,15 @@ public void RunOnce()
172176
foreach ((_, var ev) in _events)
173177
ev.Clear();
174178

179+
foreach ((_, var state) in _states)
180+
state.Update();
181+
175182
RunStage(Stages.Startup);
176183
_systems[(int)Stages.Startup].Clear();
177184

185+
RunStage(Stages.OnExit);
186+
RunStage(Stages.OnEnter);
187+
178188
for (var stage = Stages.FrameStart; stage <= Stages.FrameEnd; stage += 1)
179189
RunStage(stage);
180190
}
@@ -186,6 +196,9 @@ private void RunStage(Stages stage)
186196

187197
var systems = _systems[(int)stage];
188198

199+
if (systems.Count == 0)
200+
return;
201+
189202
foreach (var sys in systems)
190203
{
191204
if (sys.IsResourceInUse())
@@ -213,21 +226,43 @@ internal void Add(FuncSystem<World> sys, Stages stage)
213226
sys.Node = _systems[(int)stage].AddLast(sys);
214227
}
215228

216-
public FuncSystem<World> AddSystems(ReadOnlySpan<FuncSystem<World>> systems, Stages stage = Stages.Update, ThreadingMode threadingType = ThreadingMode.Auto)
229+
public FuncSystem<World> AddSystem(Action system, Stages stage = Stages.Update, ThreadingMode threadingType = ThreadingMode.Auto)
217230
{
218-
var rootSystem = AddSystem(() => { }, stage, threadingType);
219-
foreach (var system in systems)
231+
var sys = new FuncSystem<World>(_world, (args, runIf) =>
220232
{
221-
if (system == rootSystem)
222-
continue;
233+
if (runIf?.Invoke(args) ?? true)
234+
{
235+
system();
236+
return true;
237+
}
238+
return false;
239+
}, () => false, stage, threadingType);
240+
Add(sys, stage);
223241

224-
system.RunAfter(rootSystem);
225-
}
242+
return sys;
243+
}
226244

227-
return rootSystem;
245+
public FuncSystem<World> OnEnter<TState>(TState st, Action system, ThreadingMode threadingType = ThreadingMode.Auto)
246+
where TState : struct, Enum
247+
{
248+
var sys = new FuncSystem<World>(_world, (args, runIf) =>
249+
{
250+
if (runIf?.Invoke(args) ?? true)
251+
{
252+
system();
253+
return true;
254+
}
255+
return false;
256+
}, () => false, Stages.OnEnter, threadingType)
257+
.RunIf((State<TState> state) => state.EnterState(st));
258+
259+
Add(sys, Stages.OnEnter);
260+
261+
return sys;
228262
}
229263

230-
public FuncSystem<World> AddSystem(Action system, Stages stage = Stages.Update, ThreadingMode threadingType = ThreadingMode.Auto)
264+
public FuncSystem<World> OnExit<TState>(TState st, Action system, ThreadingMode threadingType = ThreadingMode.Auto)
265+
where TState : struct, Enum
231266
{
232267
var sys = new FuncSystem<World>(_world, (args, runIf) =>
233268
{
@@ -237,8 +272,10 @@ public FuncSystem<World> AddSystem(Action system, Stages stage = Stages.Update,
237272
return true;
238273
}
239274
return false;
240-
}, () => false, stage, threadingType);
241-
Add(sys, stage);
275+
}, () => false, Stages.OnExit, threadingType)
276+
.RunIf((State<TState> state) => state.ExitState(st));
277+
278+
Add(sys, Stages.OnExit);
242279

243280
return sys;
244281
}
@@ -263,9 +300,14 @@ public Scheduler AddEvent<T>() where T : notnull
263300
return AddSystemParam(ev);
264301
}
265302

266-
public Scheduler AddState<T>(T initialState = default!) where T : notnull, Enum
303+
public Scheduler AddState<T>(T initialState = default!) where T : struct, Enum
267304
{
268-
return AddResource(initialState);
305+
if (_states.ContainsKey(typeof(T)))
306+
return this;
307+
308+
var state = new State<T>(initialState, initialState);
309+
_states.Add(typeof(T), state);
310+
return AddSystemParam(state);
269311
}
270312

271313
public Scheduler AddResource<T>(T resource) where T : notnull
@@ -284,6 +326,13 @@ internal bool ResourceExists<T>() where T : notnull, ISystemParam<World>
284326
{
285327
return _world.Entity<Placeholder<T>>().Has<Placeholder<T>>();
286328
}
329+
330+
internal bool InState<T>(T state) where T : struct, Enum
331+
{
332+
if (!_world.Entity<Placeholder<State<T>>>().Has<Placeholder<State<T>>>())
333+
return false;
334+
return _world.Entity<Placeholder<State<T>>>().Get<Placeholder<State<T>>>().Value.InState(state);
335+
}
287336
}
288337

289338
internal struct Placeholder<T> where T : ISystemParam<World> { public T Value; }
@@ -342,6 +391,7 @@ internal EventParam()
342391
public static ISystemParam<World> Generate(World arg)
343392
{
344393
if (arg.Entity<Placeholder<EventParam<T>>>().Has<Placeholder<EventParam<T>>>())
394+
345395
return arg.Entity<Placeholder<EventParam<T>>>().Get<Placeholder<EventParam<T>>>().Value;
346396

347397
var ev = new EventParam<T>();
@@ -369,6 +419,7 @@ public void Enqueue(T ev)
369419
public static ISystemParam<World> Generate(World arg)
370420
{
371421
if (arg.Entity<Placeholder<EventParam<T>>>().Has<Placeholder<EventParam<T>>>())
422+
372423
return arg.Entity<Placeholder<EventParam<T>>>().Get<Placeholder<EventParam<T>>>().Value.Writer;
373424

374425
throw new NotImplementedException("EventWriter<T> must be created using the scheduler.AddEvent<T>() method");
@@ -394,6 +445,7 @@ public EventReaderIterator GetEnumerator()
394445
public static ISystemParam<World> Generate(World arg)
395446
{
396447
if (arg.Entity<Placeholder<EventParam<T>>>().Has<Placeholder<EventParam<T>>>())
448+
397449
return arg.Entity<Placeholder<EventParam<T>>>().Get<Placeholder<EventParam<T>>>().Value.Reader;
398450

399451
throw new NotImplementedException("EventReader<T> must be created using the scheduler.AddEvent<T>() method");
@@ -434,6 +486,7 @@ internal Query(Query query) : base(query) { }
434486
public new static ISystemParam<World> Generate(World arg)
435487
{
436488
if (arg.Entity<Placeholder<Query<TQueryData>>>().Has<Placeholder<Query<TQueryData>>>())
489+
437490
return arg.Entity<Placeholder<Query<TQueryData>>>().Get<Placeholder<Query<TQueryData>>>().Value;
438491

439492
var builder = arg.QueryBuilder();
@@ -455,6 +508,7 @@ public class Query<TQueryData, TQueryFilter> : SystemParam<World>, IIntoSystemPa
455508
public static ISystemParam<World> Generate(World arg)
456509
{
457510
if (arg.Entity<Placeholder<Query<TQueryData, TQueryFilter>>>().Has<Placeholder<Query<TQueryData, TQueryFilter>>>())
511+
458512
return arg.Entity<Placeholder<Query<TQueryData, TQueryFilter>>>().Get<Placeholder<Query<TQueryData, TQueryFilter>>>().Value;
459513

460514
var builder = arg.QueryBuilder();
@@ -501,6 +555,7 @@ internal Single(Query query) : base(query) { }
501555
public new static ISystemParam<World> Generate(World arg)
502556
{
503557
if (arg.Entity<Placeholder<Single<TQueryData>>>().Has<Placeholder<Single<TQueryData>>>())
558+
504559
return arg.Entity<Placeholder<Single<TQueryData>>>().Get<Placeholder<Single<TQueryData>>>().Value;
505560

506561
var builder = arg.QueryBuilder();
@@ -522,6 +577,7 @@ public class Single<TQueryData, TQueryFilter> : SystemParam<World>, IIntoSystemP
522577
public static ISystemParam<World> Generate(World arg)
523578
{
524579
if (arg.Entity<Placeholder<Single<TQueryData, TQueryFilter>>>().Has<Placeholder<Single<TQueryData, TQueryFilter>>>())
580+
525581
return arg.Entity<Placeholder<Single<TQueryData, TQueryFilter>>>().Get<Placeholder<Single<TQueryData, TQueryFilter>>>().Value;
526582

527583
var builder = arg.QueryBuilder();
@@ -562,6 +618,75 @@ public int Count()
562618
=> _query.Count();
563619
}
564620

621+
public interface IState
622+
{
623+
void Update();
624+
}
625+
626+
public sealed class State<T>(T previous, T current) : SystemParam<World>, IIntoSystemParam<World>, IState
627+
where T : struct, Enum
628+
{
629+
private bool _enteredStateFrame;
630+
private bool _exitedStateFrame;
631+
632+
internal T Previous { get; private set; } = previous;
633+
public T Current { get; private set; } = current;
634+
635+
636+
public static ISystemParam<World> Generate(World arg)
637+
{
638+
if (arg.Entity<Placeholder<State<T>>>().Has<Placeholder<State<T>>>())
639+
return arg.Entity<Placeholder<State<T>>>().Get<Placeholder<State<T>>>().Value;
640+
641+
var state = new State<T>(default, default);
642+
arg.Entity<Placeholder<State<T>>>().Set(new Placeholder<State<T>>() { Value = state });
643+
return state;
644+
}
645+
646+
public void Set(T value)
647+
{
648+
if (!Equals(Current, value))
649+
{
650+
Previous = Current;
651+
Current = value;
652+
653+
_enteredStateFrame = false;
654+
_exitedStateFrame = false;
655+
}
656+
}
657+
658+
void IState.Update()
659+
{
660+
if (Equals(Current, Previous))
661+
{
662+
_enteredStateFrame = true;
663+
_exitedStateFrame = true;
664+
}
665+
}
666+
667+
internal bool InState(T? state)
668+
{
669+
return Equals(Current, state);
670+
}
671+
672+
internal bool EnterState(T state)
673+
{
674+
if (!_enteredStateFrame && Equals(Current, state))
675+
{
676+
return true;
677+
}
678+
return false;
679+
}
680+
681+
internal bool ExitState(T state)
682+
{
683+
if (!_exitedStateFrame && Equals(Previous, state))
684+
{
685+
return true;
686+
}
687+
return false;
688+
}
689+
}
565690

566691
public sealed class Res<T> : SystemParam<World>, IIntoSystemParam<World> where T : notnull
567692
{
@@ -613,6 +738,12 @@ public void AddResource<T>(T resource) where T : notnull
613738
public bool ResourceExists<T>() where T : notnull
614739
=> _scheduler.ResourceExists<Res<T>>();
615740

741+
public void AddState<T>(T state = default!) where T : struct, Enum
742+
=> _scheduler.AddState(state);
743+
744+
public bool InState<T>(T state) where T : struct, Enum
745+
=> _scheduler.InState(state);
746+
616747
public static ISystemParam<World> Generate(World arg)
617748
{
618749
if (arg.Entity<Placeholder<SchedulerState>>().Has<Placeholder<SchedulerState>>())

0 commit comments

Comments
 (0)