From 0b1a938fe2b034be591acb67fc087a607c79d030 Mon Sep 17 00:00:00 2001 From: Matt Thorson Date: Thu, 21 May 2020 21:24:04 -0700 Subject: [PATCH] Proper Entity and Component bucketing/tracking --- src/Core/Entity.bf | 2 + src/Core/Game.bf | 3 ++ src/Core/Scene.bf | 90 ++++++++++++++++++++++++++++++++++------- src/Static/Calc.bf | 8 ++++ src/Static/TypeTree.bf | 61 ++++++++++++++++++++++++++++ src/Struct/Cardinals.bf | 30 ++++++++++++++ 6 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 src/Static/TypeTree.bf diff --git a/src/Core/Entity.bf b/src/Core/Entity.bf index 548ddd8..fe91b73 100644 --- a/src/Core/Entity.bf +++ b/src/Core/Entity.bf @@ -88,6 +88,7 @@ namespace Strawberry for (var c in toRemove) { components.Remove(c); + Scene.[Friend]UntrackComponent(c); c.Removed(); delete c; } @@ -100,6 +101,7 @@ namespace Strawberry for (var c in toAdd) { components.Add(c); + Scene.[Friend]TrackComponent(c); c.Added(this); } diff --git a/src/Core/Game.bf b/src/Core/Game.bf index b82b508..d3ce23d 100644 --- a/src/Core/Game.bf +++ b/src/Core/Game.bf @@ -77,6 +77,8 @@ namespace Strawberry SDLImage.Init(.PNG | .JPG); SDLMixer.OpenAudio(44100, SDLMixer.MIX_DEFAULT_FORMAT, 2, 4096); SDLTTF.Init(); + + TypeTree.[Friend]Build(); Input.[Friend]Init(gamepadLimit); } @@ -97,6 +99,7 @@ namespace Strawberry delete VirtualInputs; } + TypeTree.[Friend]Dispose(); Input.[Friend]Dispose(); Game = null; } diff --git a/src/Core/Scene.bf b/src/Core/Scene.bf index fd35852..063d796 100644 --- a/src/Core/Scene.bf +++ b/src/Core/Scene.bf @@ -12,12 +12,22 @@ namespace Strawberry private List entities; private HashSet toRemove; private HashSet toAdd; + private Dictionary> entityTracker; + private Dictionary> componentTracker; public this() { entities = new List(); toAdd = new HashSet(); toRemove = new HashSet(); + + entityTracker = new Dictionary>(); + for (let type in TypeTree.[Friend]EntityAssignableLists.Keys) + entityTracker.Add(type, new List()); + + componentTracker = new Dictionary>(); + for (let type in TypeTree.[Friend]ComponentAssignableLists.Keys) + componentTracker.Add(type, new List()); } public ~this() @@ -36,6 +46,14 @@ namespace Strawberry delete toAdd; delete toRemove; + + for (let list in entityTracker.Values) + delete list; + delete entityTracker; + + for (let list in componentTracker.Values) + delete list; + delete componentTracker; } public virtual void Started() @@ -79,6 +97,7 @@ namespace Strawberry for (var e in toRemove) { entities.Remove(e); + UntrackEntity(e); e.Removed(); if (e.DeleteOnRemove) delete e; @@ -92,6 +111,7 @@ namespace Strawberry for (var e in toAdd) { entities.Add(e); + TrackEntity(e); e.Added(this); } } @@ -108,6 +128,38 @@ namespace Strawberry } } + // Tracking + + private void TrackEntity(Entity e) + { + for (let t in TypeTree.[Friend]EntityAssignableLists[e.GetType()]) + entityTracker[t].Add(e); + + for (let c in e.[Friend]components) + TrackComponent(c); + } + + private void UntrackEntity(Entity e) + { + for (let t in TypeTree.[Friend]EntityAssignableLists[e.GetType()]) + entityTracker[t].Remove(e); + + for (let c in e.[Friend]components) + UntrackComponent(c); + } + + private void TrackComponent(Component c) + { + for (let t in TypeTree.[Friend]ComponentAssignableLists[c.GetType()]) + componentTracker[t].Add(c); + } + + private void UntrackComponent(Component c) + { + for (let t in TypeTree.[Friend]ComponentAssignableLists[c.GetType()]) + componentTracker[t].Remove(c); + } + // Time public float TimeElapsed => Time.Elapsed - TimeStarted; @@ -125,52 +177,62 @@ namespace Strawberry // Finding Entities + public int Count() where T : Entity + { + return entityTracker[typeof(T)].Count; + } + public T First() where T : Entity { - for (var e in entities) - if (e is T) - return e as T; + for (var e in entityTracker[typeof(T)]) + return e as T; return null; } public T First(Point point) where T : Entity { - for (var e in entities) - if (e is T && e.Check(point)) + for (var e in entityTracker[typeof(T)]) + if (e.Check(point)) return e as T; return null; } public T First(Rect rect) where T : Entity { - for (var e in entities) - if (e is T && e.Check(rect)) + for (var e in entityTracker[typeof(T)]) + if (e.Check(rect)) return e as T; return null; } public List All(List into) where T : Entity { - for (var e in entities) - if (e is T) - into.Add(e as T); + for (var e in entityTracker[typeof(T)]) + into.Add(e as T); return into; } public List All(Point point, List into) where T : Entity { - for (var e in entities) - if (e is T && e.Check(point)) + for (var e in entityTracker[typeof(T)]) + if (e.Check(point)) into.Add(e as T); return into; } public List All(Rect rect, List into) where T : Entity { - for (var e in entities) - if (e is T && e.Check(rect)) + for (var e in entityTracker[typeof(T)]) + if (e.Check(rect)) into.Add(e as T); return into; } + + // Finding Components + + public int Count() where T : Component + { + return componentTracker[typeof(T)].Count; + } } } diff --git a/src/Static/Calc.bf b/src/Static/Calc.bf index 1339c31..8f5eb9b 100644 --- a/src/Static/Calc.bf +++ b/src/Static/Calc.bf @@ -53,5 +53,13 @@ namespace Strawberry v.ToString(string); Debug.WriteLine(string); } + + [Inline] + static public void Log(delegate void(String) del) + { + String string = scope String(); + del(string); + Debug.WriteLine(string); + } } } diff --git a/src/Static/TypeTree.bf b/src/Static/TypeTree.bf new file mode 100644 index 0000000..f62740b --- /dev/null +++ b/src/Static/TypeTree.bf @@ -0,0 +1,61 @@ +using System.Collections; +using System; + +namespace Strawberry +{ + static public class TypeTree + { + static private Dictionary> EntityAssignableLists; + static private Dictionary> ComponentAssignableLists; + + static private void Build() + { + /* + For each Type that extends Entity, we build a list of all the other Entity Types that it is assignable to. + We cache these lists, and use them later to bucket Entities as they are added to the Scene. + This allows us to retrieve Entities by type very easily. + */ + + EntityAssignableLists = new Dictionary>(); + for (let type in Type.Enumerator()) + { + if (type != typeof(Entity) && type.IsSubtypeOf(typeof(Entity))) + { + let list = new List(); + for (let check in Type.Enumerator()) + if (check != typeof(Entity) && check.IsSubtypeOf(typeof(Entity)) && type.IsSubtypeOf(check)) + list.Add(check); + EntityAssignableLists.Add(type, list); + } + } + + /* + And then we also do this for components + */ + + ComponentAssignableLists = new Dictionary>(); + for (let type in Type.Enumerator()) + { + if (type != typeof(Component) && type.IsSubtypeOf(typeof(Component))) + { + let list = new List(); + for (let check in Type.Enumerator()) + if (check != typeof(Component) && check.IsSubtypeOf(typeof(Component)) && type.IsSubtypeOf(check)) + list.Add(check); + ComponentAssignableLists.Add(type, list); + } + } + } + + static private void Dispose() + { + for (let list in EntityAssignableLists.Values) + delete list; + delete EntityAssignableLists; + + for (let list in ComponentAssignableLists.Values) + delete list; + delete ComponentAssignableLists; + } + } +} diff --git a/src/Struct/Cardinals.bf b/src/Struct/Cardinals.bf index 587a2d2..214dc78 100644 --- a/src/Struct/Cardinals.bf +++ b/src/Struct/Cardinals.bf @@ -1,3 +1,5 @@ +using System; + namespace Strawberry { public enum Cardinals @@ -66,5 +68,33 @@ namespace Strawberry return Point.Down; } } + + static public Result FromPoint(Point p) + { + if (p.X > 0 && p.Y == 0) + return .Right; + else if (p.X < 0 && p.Y == 0) + return .Left; + else if (p.Y < 0 && p.X == 0) + return .Up; + else if (p.Y > 0 && p.X == 0) + return .Down; + else + return .Err; + } + + static public Result FromVector(Vector v) + { + if (v.X > 0 && v.Y == 0) + return .Right; + else if (v.X < 0 && v.Y == 0) + return .Left; + else if (v.Y < 0 && v.X == 0) + return .Up; + else if (v.Y > 0 && v.X == 0) + return .Down; + else + return .Err; + } } }