diff --git a/README.md b/README.md index 86a4548..35be511 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ class EchoesExample { Echoes.init(); //Create and activate an instance of our system. - Echoes.addSystem(new RenderSystem()); //Details below. + new RenderSystem().activate(); //Details below. //Create an entity with the components our system will use. Entities are //activated automatically unless `false` is passed. @@ -114,8 +114,8 @@ class EchoesExample { //Adding `physicsSystems` first means that entire group will run before //`RenderSystem`, even if more systems are added later. - Echoes.addSystem(physicsSystems); - Echoes.addSystem(new RenderSystem()); //Details in previous example. + physicsSystems.activate(); + new RenderSystem().activate(); //Details in previous example. //Create entities: one tree and two rabbits. The rabbits will race //towards the tree. @@ -303,9 +303,9 @@ class Main { //Run all `enterFrame` systems first, then all `midFrame` systems, then //all `exitFrame` systems. - Echoes.addSystem(enterFrame); - Echoes.addSystem(midFrame); - Echoes.addSystem(exitFrame); + enterFrame.activate(); + midFrame.activate(); + exitFrame.activate(); //Even if `exitFrame` systems are defined first, they'll run last. exitFrame.add(new ExitFrameSystem()); @@ -333,11 +333,11 @@ class Main { var enterFrame:SystemList = new SystemList(); enterFrame.add(new EnterFrameSystem()); enterFrame.add(new EnterFrameSystem2()); - Echoes.addSystem(enterFrame); + enterFrame.activate(); var midFrame:SystemList = new SystemList(); midFrame.add(new MidFrameSystem()); - Echoes.addSystem(midFrame); + midFrame.activate(); //Set up `physics` as part of `midFrame`. var physics:SystemList = new SystemList(); @@ -355,7 +355,7 @@ class Main { var exitFrame:SystemList = new SystemList(); exitFrame.add(new ExitFrameSystem()); exitFrame.add(new ExitFrameSystem2()); - Echoes.addSystem(exitFrame); + exitFrame.activate(); } } ``` @@ -430,8 +430,8 @@ class Main { //Because `AverageSystem` and `list` both have priority 0, they run in //the order they're added. - Echoes.addSystem(new AverageSystem()); - Echoes.addSystem(list); + new AverageSystem().activate(); + list.activate(); //No matter how high a system's priority, if it's added to `list` it //will run during `list`, and will come after `AverageSystem`. @@ -576,11 +576,13 @@ Echoes offers a few ways to customize compilation. ### Since v1.0.0-rc.5 +- `Echoes.addSystem()`, `Echoes.hasSystem()`, and `Echoes.removeSystem()` have been replaced by `system.activate()`, `system.active`, and `system.deactivate()`, respectively. - `Entity.getComponents()` now returns a list of `ComponentStorage` instances, instead of a map. If you prefer the old format, you can perform an implicit cast: `var map:Map = Entity.getComponents()`. - Systems no longer receive `@:remove` events when deactivated. For instance, a system removed by `Echoes.removeSystem()` won't receive a bunch of events. - `View.entities` is now an `Array` rather than a `List`. You can still iterate over it as before, but you'll have to call `contains()` rather than `has()` if you want to check existence. - `Echoes.activeEntities` and `View.entities` may be re-ordered when entities or their components are removed. You can set `-D echoes_stable_order` to preserve the order, potentially at the cost of speed. - `@:remove` listeners are no longer allowed to add back the component that's currently being removed. They may still add other components as normal. +- `SystemList.exists()` now searches recursively, returning true for grandchildren as well as direct children. ### Since v1.0.0-rc.3 @@ -630,6 +632,8 @@ Find | Replace with | Notes `Echoes.entities` | `Echoes.activeEntities` `Echoes.views` | `Echoes.activeViews` `Echoes.systems` | `Echoes.activeSystems` +`Echoes.addSystem(system)` | `system.activate()` | You might have used a different variable name than `system`. +`Echoes.removeSystem(system)` | `system.deactivate()` | Ditto. `AbstractView` | `ViewBase` | Import `echoes.View`. `ISystem` | `System` | Change "`implements`" to "`extends`," if applicable. `ICleanableComponentContainer` | `ComponentStorage` diff --git a/src/echoes/Echoes.hx b/src/echoes/Echoes.hx index 5b73a0a..da67d43 100644 --- a/src/echoes/Echoes.hx +++ b/src/echoes/Echoes.hx @@ -46,15 +46,38 @@ class Echoes { @:allow(echoes.ViewBase) private static final _activeViews:Array = []; + /** + * All currently-active views. + */ public static var activeViews(get, never):ReadOnlyArray; private static inline function get_activeViews():ReadOnlyArray return _activeViews; + /** + * All currently-active systems. Unlike `activeEntities` and `activeViews`, + * this is not a flat array, but rather the root node of a tree: it may + * contain `SystemList`s containing `SystemList`s. All active systems will + * be somewhere in this tree. + * + * Adding a system to this list (whether directly, via `addSystem()`, or by + * adding a list containing that system) activates that system. + * + * Removing a system from this list (whether directly, via `removeSystem()`, + * or by removing a list containing that system) deactivates that system. + * + * To search the full tree, use `activeSystems.find()`. + */ public static var activeSystems(default, null):SystemList = { var activeSystems:SystemList = new SystemList(); activeSystems.__activate__(); activeSystems.clock.maxTime = 1; activeSystems; }; + + /** + * The clock used to update `activeSystems`. This starts with all the usual + * defaults, except `maxTime` is set to 1 second. Any changes you make to + * this clock will be preserved, even after `Echoes.reset()`. + */ public static var clock(get, never):Clock; private static inline function get_clock():Clock { return activeSystems.clock; @@ -141,21 +164,6 @@ class Echoes { init(0); } - //System management - //================= - - public static inline function addSystem(system:System):Void { - activeSystems.add(system); - } - - public static inline function removeSystem(system:System):Void { - activeSystems.remove(system); - } - - public static inline function hasSystem(system:System):Bool { - return activeSystems.exists(system); - } - //Singleton getters //================= diff --git a/src/echoes/System.hx b/src/echoes/System.hx index 15cc2e5..7799a4a 100644 --- a/src/echoes/System.hx +++ b/src/echoes/System.hx @@ -160,6 +160,22 @@ class System { //Everything else is handled by macro. } + /** + * Adds this to `activeSystems`, activating it. + * + * Note: you can also activate this by adding it to an active `SystemList`. + */ + public inline function activate():Void { + Echoes.activeSystems.add(this); + } + + /** + * Removes this from `activeSystems`, deactivating it. + */ + public inline function deactivate():Void { + parent.remove(this); + } + /** * @see `SystemList.find()` */ diff --git a/src/echoes/SystemList.hx b/src/echoes/SystemList.hx index b93ec43..0143ef7 100644 --- a/src/echoes/SystemList.hx +++ b/src/echoes/SystemList.hx @@ -131,11 +131,15 @@ class SystemList extends System { } /** - * Returns whether this list directly contains the given system. - * @see `find()` if you need to recursively search child lists. + * Returns whether this list directly or indirectly contains `system`. Use + * `system.parent` instead if you only want its direct parent. */ public inline function exists(system:System):Bool { - return system.parent == this; + var parent:SystemList = system.parent; + while(parent != null && parent != this) { + parent = parent.parent; + } + return parent == this; } /** diff --git a/test/AdvancedFunctionalityTest.hx b/test/AdvancedFunctionalityTest.hx index 593eb87..c4633a3 100644 --- a/test/AdvancedFunctionalityTest.hx +++ b/test/AdvancedFunctionalityTest.hx @@ -51,8 +51,8 @@ class AdvancedFunctionalityTest extends Test { } private function testEntityTemplates():Void { - Echoes.addSystem(new NameSystem()); - Echoes.addSystem(new AppearanceSystem()); + new NameSystem().activate(); + new AppearanceSystem().activate(); var entity:Entity = new Entity(); entity.add(("John":Name)); @@ -112,7 +112,7 @@ class AdvancedFunctionalityTest extends Test { private function testGenerics():Void { var system:GenericSystem = new GenericSystem(); - Echoes.addSystem(system); + system.activate(); var entity:Entity = new Entity(); entity.add("STRING"); @@ -133,7 +133,7 @@ class AdvancedFunctionalityTest extends Test { } var system = new GenericSystem, String>(); - Echoes.addSystem(system); + system.activate(); entity.add(("NAME":Alias)); switch(system.record) { @@ -311,13 +311,13 @@ class AdvancedFunctionalityTest extends Test { Assert.equals(0, viewOfColor.entities.length); //Adding/removing the system should activate/deactivate the linked view. - Echoes.addSystem(nameSystem); + nameSystem.activate(); Assert.isTrue(viewOfColor.active); Assert.equals(2, viewOfColor.entities.length); Assert.isTrue(viewOfColor.entities.contains(colorName)); Assert.isTrue(viewOfColor.entities.contains(colorShape)); - Echoes.removeSystem(nameSystem); + nameSystem.deactivate(); Assert.isFalse(viewOfColor.active); Assert.equals(0, viewOfColor.entities.length); } diff --git a/test/BasicFunctionalityTest.hx b/test/BasicFunctionalityTest.hx index 5c59682..b3624e7 100644 --- a/test/BasicFunctionalityTest.hx +++ b/test/BasicFunctionalityTest.hx @@ -137,7 +137,7 @@ class BasicFunctionalityTest extends Test { Assert.isFalse(inactive.active); Assert.equals(0, Echoes.activeEntities.length); - Echoes.addSystem(new AppearanceSystem()); + new AppearanceSystem().activate(); assertTimesCalled(0, "AppearanceSystem.colorAdded"); //Add some components the system looks for. @@ -159,7 +159,7 @@ class BasicFunctionalityTest extends Test { var appearanceSystem:AppearanceSystem = new AppearanceSystem(); Assert.equals(0, Echoes.activeSystems.length); - Echoes.addSystem(appearanceSystem); + appearanceSystem.activate(); Assert.equals(1, Echoes.activeSystems.length); assertTimesCalled(0, "AppearanceSystem.colorAdded"); @@ -193,7 +193,7 @@ class BasicFunctionalityTest extends Test { redLine.add(("redLine":Name)); assertTimesCalled(0, "NameSystem.nameAdded", "NameSystem isn't active but its method was still called."); - Echoes.addSystem(nameSystem); + nameSystem.activate(); assertTimesCalled(2, "NameSystem.nameAdded"); assertTimesCalled(0, "NameSystem.nameRemoved"); @@ -223,7 +223,7 @@ class BasicFunctionalityTest extends Test { assertTimesCalled(2, "NameSystem.nameRemoved"); //Deactivate a system. - Echoes.removeSystem(nameSystem); + nameSystem.deactivate(); assertTimesCalled(2, "NameSystem.nameRemoved"); //Destroy the remaining entity. @@ -238,7 +238,7 @@ class BasicFunctionalityTest extends Test { private function testUpdateEvents():Void { //Create a `TimeCountSystem` and use a custom `Clock`. var systems:SystemList = new SystemList(new OneSecondClock()); - Echoes.addSystem(systems); + systems.activate(); var timeCountSystem:TimeCountSystem = new TimeCountSystem(); Assert.equals(0.0, timeCountSystem.totalTime); diff --git a/test/EdgeCaseTest.hx b/test/EdgeCaseTest.hx index aa3ce2d..6a3b9c4 100644 --- a/test/EdgeCaseTest.hx +++ b/test/EdgeCaseTest.hx @@ -23,14 +23,14 @@ class EdgeCaseTest extends Test { //Tests may be run in any order, but not in parallel. private function testChildSystems():Void { - Echoes.addSystem(new NameSubsystem()); + new NameSubsystem().activate(); var entity:Entity = new Entity(); entity.add(("Name":Name)); assertTimesCalled(0, "NameSystem.nameAdded"); assertTimesCalled(1, "NameSubsystem.nameAdded"); - Echoes.addSystem(new NameSystem()); + new NameSystem().activate(); assertTimesCalled(1, "NameSystem.nameAdded"); assertTimesCalled(0, "NameSystem.nameRemoved"); assertTimesCalled(0, "NameSubsystem.nameRemoved"); @@ -45,7 +45,7 @@ class EdgeCaseTest extends Test { } private function testComponentsExist():Void { - Echoes.addSystem(new ComponentsExistSystem()); + new ComponentsExistSystem().activate(); var entity:Entity = new Entity(); entity.add(("name":Name)); @@ -136,7 +136,7 @@ class EdgeCaseTest extends Test { } private function testRedundantOperations():Void { - Echoes.addSystem(new AppearanceSystem()); + new AppearanceSystem().activate(); var entity:Entity = new Entity(false); @@ -171,7 +171,7 @@ class EdgeCaseTest extends Test { assertTimesCalled(1, "AppearanceSystem.colorRemoved"); //Replace a `Name` with itself. - Echoes.addSystem(new NameSystem()); + new NameSystem().activate(); entity.add(("name":Name)); assertTimesCalled(1, "NameSystem.nameAdded"); assertTimesCalled(0, "NameSystem.nameRemoved"); @@ -189,7 +189,7 @@ class EdgeCaseTest extends Test { var entity:Entity = new Entity(); //Activate the system first so that it can process events first. - Echoes.addSystem(new RecursiveEventSystem()); + new RecursiveEventSystem().activate(); //Certain events should stop propagating after `RecursiveEventSystem` //gets to them.