You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here i am again... this time with a mechanic that is called "ReactiveSystem".
Well actually artemis ODB already has one : EntitySubscription-Listeners.
But one major issue i occured was the lack of runtime... sure callbacks are great ! But what if we wanna iterate with a normal system over those newly added/removed entities ? Or if we wanna run over all newly added/removed entities of a certain aspect since the last frame ? This is why i have implemented a addition to the reactive system :-)
It takes multiple frames to "complete" and works like this ( same for removed )
Entity of aspect was added
Entity gets "Added" marker component attached, on start of next frame
Entity with "Added" exists one frame
Entity gets "Added" marker removed, on end of the current frame
A system which should only exists once in the system hierarchy.
You can easily register a aspect, a component that should get added to that component if it was newly inserted and a component that gets added once it exited the aspect.
Furthermore it keeps track of those registered aspects to automaticly add/remove those "marker" component and as an addition it will store all of them since the last frame.
Its not perfect yet... and i havent found a good implementation of attaching the component to destroyed entitites, because they dont exist anymore at that point. But it should work for the most use cases :) ( In Unity this is managed by something called StateComponent )
It makes use of the BufferSystem i developed a week ago :)
You simply need to extend that system to inject your own BufferSystems, one running at the start of the frame and one on the end.
/** * A system which marks {@link com.artemis.Entity} once those are created and destroyed based on a {@link Aspect}. * It also stores lists of created or destroyed entities for each registered {@link Aspect} to ease the iteration * over such entities during the frame itself. */publicabstractclassReactiveSystemextendsBaseSystem {
/** Simply stores a composition of a aspect we are using **/privateclassComposition{
publicAspect.Builderaspect;
publicClass<? extendsComponent> toRemove;
publicClass<? extendsComponent> toAdd;
}
protectedBufferSystemstartBufferSystem;
protectedBufferSystemendBufferSystem;
protectedMap<Aspect.Builder, Composition> compositions = newConcurrentHashMap<>();
protectedMap<Aspect.Builder, IntBag> addedEntities = newConcurrentHashMap<>();
protectedMap<Aspect.Builder, IntBag> removedEntities = newConcurrentHashMap<>();
protectedMap<Aspect.Builder, EntitySubscription> removeAddedQuery = newConcurrentHashMap<>();
protectedMap<Aspect.Builder, EntitySubscription> removeRemovedQuery = newConcurrentHashMap<>();
@Overrideprotectedabstractvoidinitialize();
@OverrideprotectedvoidprocessSystem() {
// Remove the "add" marker component from entities that already had one whole iteration.for(varentry : removeAddedQuery.entrySet()){
varcomposition = compositions.get(entry.getKey());
varentities = entry.getValue().getEntities();
for(varindex = 0; index < entities.size(); index++){
varentityID = entities.get(index);
endBufferSystem.remove(entityID, composition.toAdd);
}
}
// Remove the "remove" marker component from entities that already had one whole iteration.for(varentry : removeRemovedQuery.entrySet()){
varcomposition = compositions.get(entry.getKey());
varentities = entry.getValue().getEntities();
for(varindex = 0; index < entities.size(); index++){
varentityID = entities.get(index);
endBufferSystem.remove(entityID, composition.toRemove);
}
}
// Process all added entities per aspect in order to mark them with the "add" componentfor(varentry : addedEntities.entrySet()){
varcomposition = compositions.get(entry.getKey());
for(varindex = 0; index < entry.getValue().size(); index++){
varentityID = entry.getValue().get(index);
startBufferSystem.create(entityID, composition.toAdd);
}
}
// Process all added entities per aspect in order to mark them with the "add" componentfor(varentry : removedEntities.entrySet()){
varcomposition = compositions.get(entry.getKey());
for(varindex = 0; index < entry.getValue().size(); index++){
varentityID = entry.getValue().get(index);
if(world.getEntityManager().isActive(entityID)) continue;
startBufferSystem.create(entityID, composition.toRemove);
}
}
}
@Overrideprotectedvoidend() {
super.end();
addedEntities.clear();
removedEntities.clear();
}
/** * Registers all entities of a {@link Aspect} for being marked with a special component once they entered or * exited the {@link Aspect} aswell as being tracked in the internal {@link IntBag} each frame. * @param builder The aspect we choose our entities with * @param toAdd The component that gets added to each of them on the start of the frame, once they enter the aspect. * @param toRemove The component that gets added to each of them on the start of the frame, once they exit the aspect. */publicvoidregister(Aspect.Builderbuilder, Class<? extendsComponent> toAdd, Class<? extendsComponent> toRemove){
if(compositions.containsKey(builder)) return;
varsubscribtion = world.getAspectSubscriptionManager().get(builder);
// Store compositionvarcomposition = newComposition();
composition.aspect = builder;
composition.toAdd = toAdd;
composition.toRemove = toRemove;
compositions.put(builder, composition);
// Create subscribtion for tracking the entitiessubscribtion.addSubscriptionListener(newEntitySubscription.SubscriptionListener() {
@Overridepublicvoidinserted(IntBagintBag) {
if(addedEntities.containsKey(builder)) addedEntities.get(builder).addAll(intBag);
else{
varnewBag = newIntBag();
newBag.addAll(intBag);
addedEntities.put(builder, newBag);
}
}
@Overridepublicvoidremoved(IntBagintBag) {
if(removedEntities.containsKey(builder)) removedEntities.get(builder).addAll(intBag);
else {
varnewBag = newIntBag();
newBag.addAll(intBag);
removedEntities.put(builder, newBag);
}
}
});
// Query used for removing the "Add" after one frame :)removeAddedQuery.put(builder, world.getAspectSubscriptionManager().get(builder.copy().all(toAdd)));
removeRemovedQuery.put(builder, world.getAspectSubscriptionManager().get(builder.copy().all(toRemove)));
}
/** * Returns an {@link IntBag} of all newly added entities to the {@link Aspect} since last frame. * Always one frame delayed, but who cares in an asyn. environment ? * @param builder The aspect we wanna search all new entities for * @return The {@link IntBag} filled with those entity ids */publicIntBaggetAdded(Aspect.Builderbuilder){ returnaddedEntities.get(builder); }
/** * A little helper method to execute logic for each entity added to the certain aspect since last frame. * The passed entities do not have the "added" marker component attached yet, that one is getting attached one * frame later. * @param builder The aspect we wanna search all new entities for * @param action The action getting executed for all of them */publicvoidforEachAdded(Aspect.Builderbuilder, Consumer<Integer> action){
varbag = getAdded(builder);
if(bag == null) return;
for(varindex = 0; index < bag.size(); index++) action.accept(bag.get(index));
}
/** * Returns an {@link IntBag} of all newly removed entities to the {@link Aspect} since last frame. * Always one frame delayed, but who cares in an asyn. environment ? * @param builder The aspect we wanna search all removed entities for * @return The {@link IntBag} filled with those entity ids */publicIntBaggetRemoved(Aspect.Builderbuilder){ returnremovedEntities.get(builder); }
/** * A little helper method to execute logic for each entity added to the certain aspect since last frame. * The passed entities do not have the "removed" marker component attached yet, that one is getting attached one * frame later. * @param builder The aspect we wanna search all new entities for * @param action The action getting executed for all of them */publicvoidforEachRemoved(Aspect.Builderbuilder, Consumer<Integer> action){
varbag = getRemoved(builder);
if(bag == null) return;
for(varindex = 0; index < bag.size(); index++) action.accept(bag.get(index));
}
}
Useage is pretty simple... you need to inject your own buffer systems
publicclassWiredReactiveSystemextendsReactiveSystem{
StartBufferSystemstartBufferSystem; // A buffer system in MY project running on the start of a frame, the first oneEndBufferSystemendBufferSystem; // Last one, at the end of the frame@Overrideprotectedvoidinitialize() {
super.startBufferSystem = startBufferSystem; // Injectsuper.endBufferSystem = endBufferSystem;
}
}
And then you simply start using it by acessing your extended ReactiveSystem like...
// Somewhere in initialize, main or some constructor// All entities with Player.class should get the "OnPlayerCreated" attached if newly inserted to the aspect// And OnPlayerDestroyed once they are out of the aspect.myReactiveSystem.register(Aspect.all(Player.class), OnPlayerCreated.class, OnPlayerDestroyed.class);
// Somewhere else myReactiveSystem.forEachAdded(Aspect.all(Player.class), id -> {
// Do something for all added aspects of this type since the last frame
});
// Or iterate over them without any need to care of tagging them@All({Player.class, OnPlayerCreated.class})
publicclassTestSystemextendsIteratingSystem {
@Overrideprotectedvoidprocess(inti) {
// I only run one frame wuuuu
}
}
The text was updated successfully, but these errors were encountered:
.. sure callbacks are great ! But what if we wanna iterate with a normal system over those newly added/removed entities ? Or if we wanna run over all newly added/removed entities of a certain aspect since the last frame ? This is why i have implemented a addition to the reactive system.
Cool! Do you have concrete examples of use?
As a feature request, this is fairly specialized and less core material (artemis-odb-contrib), unless there are extension points missing in artemis-odb.
Its not perfect yet... and i havent found a good implementation of attaching the component to destroyed entitites, because they dont exist anymore at that point. But it should work for the most use cases :) ( In Unity this is managed by something called StateComponent )
Non-typical lifecycle behavior could be done by adding a 'Deleted' component to signifiydeletion, instead of calling .deleteFromWorld(), and adding an exclusion filter on spots you don't want affected by the deleted entity.
Well i actually use those systems quite heavy. I often mark my entities ( Composition > Inheritance ) with components to signalize certain entity specific events. Thats where my reactive systems come in quite nicely.
Its just an alternative to the inserted and removed callbacks for components which allows us to iterate over them for those who prefer a nice flow. It should be better for multithreading than those added/removed callbacks ^^
Here i am again... this time with a mechanic that is called "ReactiveSystem".
Well actually artemis ODB already has one : EntitySubscription-Listeners.
But one major issue i occured was the lack of runtime... sure callbacks are great ! But what if we wanna iterate with a normal system over those newly added/removed entities ? Or if we wanna run over all newly added/removed entities of a certain aspect since the last frame ? This is why i have implemented a addition to the reactive system :-)
It takes multiple frames to "complete" and works like this ( same for removed )
A system which should only exists once in the system hierarchy.
You can easily register a aspect, a component that should get added to that component if it was newly inserted and a component that gets added once it exited the aspect.
Furthermore it keeps track of those registered aspects to automaticly add/remove those "marker" component and as an addition it will store all of them since the last frame.
Its not perfect yet... and i havent found a good implementation of attaching the component to destroyed entitites, because they dont exist anymore at that point. But it should work for the most use cases :) ( In Unity this is managed by something called StateComponent )
It makes use of the BufferSystem i developed a week ago :)
You simply need to extend that system to inject your own BufferSystems, one running at the start of the frame and one on the end.
Useage is pretty simple... you need to inject your own buffer systems
And then you simply start using it by acessing your extended ReactiveSystem like...
The text was updated successfully, but these errors were encountered: