EntityMonkey, a simple Entity System for JME

@polygnome said:
Nah, that's imprecise. You'd process it the next time your "system" is updated. If you update your system from the update loop, then sure, it is only updated on the next frame. But you don't have to update it from the render thread. You can use a multitude of threads. You might for example have a network thread that updates your ES, a thread for physics, one for AI, and maybe one or more threads from a thread pool that process a bunch of other systems which update for ES.


Yes, in my case in the next frame, but I'll solve this.

@polygnome said:
Why would you? It can still be in two systems. You just need to think about when to attach what component, when to process which component and when to detach it.


I mean I would remove some extra amount of code that I currently have to make it work with the 2 systems type. I have already changed it, and now it looks like much better.

So here is an update:

I am going with the design which updates the Spatials from inside an EntitySystem if needed. Even if it might not be the correct way since the EntitySystem should not care about engine specific things, but i have tested both and in my simple test scenario pushing changes is faster if less then 70% of the entitys are moving. Simply because entitys who have not changes position do not need an update. If done the JME way (ControlUpdate querys EntitySystem) a update is needed even if there was no change.



Additionally i can heavily use multithreading if done inside a loop. After day’s of tunig my EntitySystem is now able to handle 100k moving entitys at 25FPS. Not bad i think, since most games have probalby much less moving entitys at the same time.



Currently the biggest bottleneck is adding new EntitySystems at runtime since it requires to reMap all Entity’s.



The biggest performance gain i have found is using a:

[java]

Set<Entity> entities = ConcurrentHashMap<Entity, String>>.get(t).keySet();

ConcurrentLinkedQueue<Entity> entities2 = new ConcurrentLinkedQueue<Entity>();

for (Entity e : entities) {

entities2.offer(e);

}

new RunnerThread(entities2);

[/java]

instead of:

[java]

Iterator iterator:ConcurrentHashMap<Entity, String>>.get(t).keySet().iterator();

new RunnerThread(iterator);

[/java]



For multithreading passing the iterator to the workerthread is useless since it blocks. (Did not know that, i assumed as long i don’t use ConcurrentHashMap.put the iterator does not block)

Either your definition of “block” is incorrect or what you think you are seeing is.

Well, i did not rely on seeing stuff, but on measuring the execution time.

MapSize: 10000

Without Threads ~7ms

With 10 Threads using the Iterator ~7ms

With 10 Threads using the Queue ~2ms



Maybe i should have said, the iterator synchronizes to heavy :wink:

Out of interest if you have multiple systems all accessing the entity system asynchronously then how do you avoid threading issues?



Obviously immutable components and atomic set operations mitigates things a bit but you still have problems with things like read->modify->write being non atomic, as is processing of multiple components within one system.



Are all accesses to the entity system synchronized (clearly bad for performance and losing some of the gains from threading) or is there some “magic” at work?

@zzuegg said:
Well, i did not rely on seeing stuff, but on measuring the execution time.
MapSize: 10000
Without Threads ~7ms
With 10 Threads using the Iterator ~7ms
With 10 Threads using the Queue ~2ms

Maybe i should have said, the iterator synchronizes to heavy ;)


Yes, that would have been more "correct". :) Still, I wonder if it would improve if the concurrency level was set higher on the map... but I didn't see how it was setup.

My ES does things a little differently. It keeps with the pure concept of thread independent result sets... kind of like if you have each thread reading its own JDBC result set. However, it adds the concept of being able to sort of "reissue" the query in place and then it can tell you what was added, updated, or removed.

This is done through an EntitySet (implements Set) that acts as my result set for queries.
[java]
EntitySet myEntities = entityData.getEntities( someFilter, Name.class, Position.class, ModelInfo.class.... );
[/java]

I have lots of manager update loops that look like:
[java]
if( myEntities.applyChanges() ) {
removeStuff( myEntities.getRemoveEntities() );
addStuff( myEntities.getAddedEntities() );
updateStuff( myEntities.getChangedEntities() );
}
[/java]

This would be pretty trivial to implement with a thin database layer and you could still get a fully scalable entity system (where systems could even exist on different machines, etc.). In my case, I know that I will always be single process so I do some event fanciness under the covers. When a component changes, I distribute the change event to any interested EntitySets that are still active. This makes applyChanges() a very quick operation... nearly free in 99% of cases.
1 Like
@pspeed said:
Yes, that would have been more "correct". :) Still, I wonder if it would improve if the concurrency level was set higher on the map... but I didn't see how it was setup.


Increasing the concurrency did not had any effect.

I currently have 4 major components in my ES:
[java]
private ConcurrentHashMap<String, ConcurrentHashMap<Class, EntityComponent>> entityComponentMapper;
private ConcurrentHashMap<Class, EntitySystem> entitySystems;
private ConcurrentHashMap<Class, List<Entity>> entitySystemsToEntityMapper;
private ConcurrentLinkedQueue<Entity> entitiesToUpdateList;
[/java]
The EntityThread update loop looks like:
[java]
entityToSystemMapper.updateE2SMap();
entitySystemExecutor.evaluateSystems(SystemThreadType.EntityThread, delta);
[/java]
When i add/remove a Component to an Entity the entity gets addes to the entityToUpdate Queue. In the updateloop my entityToSystemMapper parses the Queue and adds, or removes an Entity form the entitySystemToEntityMapper.

My EntitySystemExecutor then can get all interested Entity's for free. The executor itself evaluates the systems sequentially and the entity's within the systems paralell. Trough this, the heavily used entityComponentMapper will not block/ stall for synchronizing.

On the other side, on the updateloop i evaluate all systems which updated the scenegraph:
[java]
entitySystemExecutor.evaluateSystems(SystemThreadType.RenderThread, tpf);
[/java]
Those Systems are the only one with the possiblility to modify EntityComponents concurrently. But since they only need to get Informations from the ES i have no problem.

From what you have wrote, your systems depends strongly on the performance of the used querys. I have changed to my current ‘mapper’ design because i belive most entity’s will keep they components for most of the time. And if they change, i have only a ‘one time’ cost. Additionally the mapping process could be possible done in the background if many remaps are need at the same loop.

In my approach, “systems” are everything but the entity manager, the components, and the entities. They require no formal specification since anything that uses an entity is a “system” and they can do it from any thread. There is no queuing on set. If you call set( someComponent ) on an entity then it gets stored right away.



It’s the code that uses entities that can choose when they update their view since they always have just a snapshot in time.



The code is simple to use and other than the loop that applies events to EntitySets it is a super-simple straight forward implementation. Even the SQL database layer is pretty thin and I can support persistent and non-persistent components with just a tagging interface change.

@zzuegg said:
From what you have wrote, your systems depends strongly on the performance of the used querys. I have changed to my current 'mapper' design because i belive most entity's will keep they components for most of the time. And if they change, i have only a 'one time' cost. Additionally the mapping process could be possible done in the background if many remaps are need at the same loop.


In my approach, an "Entity" is just a view of a set of components at a particular time (when it was queried). It can be refreshed to get new data when required. This prevents inconsistent results during multithreaded execution.

At the interface level, here is the entirety of my entity system:



Since an entity system is really a data layer and because “systems” are just any old code… I tried to avoid the word “system” wherever possible because I think it pollutes the water.



This is the main “entity manager” interface:

[java]

public interface EntityData {

public EntityId createEntity();

public void removeEntity( EntityId entityId );

public void setComponent( EntityId entityId, EntityComponent component );

public void setComponents( EntityId entityId, EntityComponent… components );

public boolean removeComponent( EntityId entityId, Class type );

public <T extends EntityComponent> T getComponent( EntityId entityId, Class<T> type );

public Entity getEntity( EntityId entityId, Class… types );

public EntityId findEntity( ComponentFilter filter, Class… types );

public Set<EntityId> findEntities( ComponentFilter filter, Class… types );

public EntitySet getEntities( Class… types );

public EntitySet getEntities( ComponentFilter filter, Class… types );

public void releaseEntitySet( EntitySet entities );

public void close();

}

[/java]



Entities are just localized views of a subset of an entity’s components:

[java]

public interface Entity {

public EntityId getId();

public <T extends EntityComponent> T get( Class<T> type );

public void set( EntityComponent c );

public boolean isComplete();

public EntityComponent[] getComponents();

}

[/java]



This is an abbreviated look at the “live results” EntitySet class that is where all of the magic happens. This is the largest class in the whole entity system even as compared to the database layer.

[java]

public class EntitySet extends AbstractSet<Entity> {

/**

  • Swaps out the current main filter for a new one. Returns
  • true if the entity set was changed during this process.
  • This is similar to applyChanges() and any current pending
  • changes will be applied during the filter transition.

    */

    public boolean resetFilter( ComponentFilter filter ){}



    public boolean containsId( EntityId id ) {}



    public Entity getEntity( EntityId id ) {}



    public Iterator<Entity> iterator() {}



    /**
  • Returns the entities that were added during applyChanges.

    */

    public Set<Entity> getAddedEntities() {}



    /**
  • Returns the entities that were changed during applyChanges.

    */

    public Set<Entity> getChangedEntities() {}



    /**
  • Returns the entities that were removed during applyChanges.

    */

    public Set<Entity> getRemovedEntities() {}



    public void clearChangeSets() {}



    public boolean hasChanges() {}



    /**
  • Applies any accumulated changes to this list’s entities since
  • the last time it was called and returns true if there were
  • changes.

    */

    public boolean applyChanges() { }



    /**
  • Releases this entity set from processing further entity
  • updates and destroys any internal data structures.

    */

    public void release() {}

    }

    [/java]



    EntitySet internally keeps a queue of change events… but these events are filtered based on the components and filters that the entity set has so that mostly only change events that apply to the entity set are kept. This means that usually applyChanges() is free and only minimally expensive when there are change events. No more expensive than any other update strategy.



    And that’s it. Everything else is an implementation… the bulk of the code being in the classes that implement the JDBC layer.
1 Like
@pspeed said:
In my approach, an "Entity" is just a view of a set of components at a particular time (when it was queried). It can be refreshed to get new data when required. This prevents inconsistent results during multithreaded execution.


Indeed your approch offers much more flexibility your systems are not binded to a fixed set of components. However, i was not able to implement a fast selection algorithm. Iterating on every query over all Entitys to check it's components was too much overhead to handle for me :(


The performance cost between using simple controls like:
[java]
public class BoxControl extends AbstractControl{
float timeLeft=0;
Vector3f currentMovement;
@Override
protected void controlUpdate(float tpf) {
timeLeft-=tpf;
if(timeLeft<=0){
currentMovement=new Vector3f((FastMath.nextRandomFloat()*2)-1,(FastMath.nextRandomFloat()*2)-1,(FastMath.nextRandomFloat()*2)-1);
timeLeft=FastMath.nextRandomFloat()*5;
}
this.spatial.setLocalTranslation(this.spatial.getLocalTranslation().clone().addLocal(currentMovement.mult(tpf)));
}
}
[/java]
and a ES driven approch was nearly 50%. I use the above Control attached to 10000 boxes as worst case benchmark. With the current implementation i have only a ES cost of 10%.
@zzuegg said:
Indeed your approch offers much more flexibility your systems are not binded to a fixed set of components. However, i was not able to implement a fast selection algorithm. Iterating on every query over all Entitys to check it's components was too much overhead to handle for me :(


My queries are pretty fast but they aren't particularly smart... I just don't do them very often. Usually only during startup or when a new set of entities is required (like leaving one zone and moving to a new one).

Inside my base SqlEntityData implemention, I have a map like:
[java]
private Map<Class, ComponentHandler> handlers = new ConcurrentHashMap<Class, ComponentHandler>();
[/java]

ComponentHandler looks like:
[java]
public interface ComponentHandler<T extends EntityComponent> {
public void setComponent( EntityId entityId, T component );
public boolean removeComponent( EntityId entityId );
public T getComponent( EntityId entityId );
public Set<EntityId> getEntities();
public Set<EntityId> getEntities( ComponentFilter filter );
public EntityId findEntity( ComponentFilter filter );
}
[/java]

I have some map-based ones that simply map an entity ID to a component value in a big hashmap. And I have the SQL based ones that map a component to a table (and it auto creates the table if it doesn't exist).

A query for entities with multiple components is just the intersection of the separate entity ID sets. I thought this would be a bottleneck but it isn't. Probably because I don't actually perform full queries very often.

I have to think about your system for a while…

Interesting read… I think i have gotten the idea behind your approach. However i am lacking of knowledge/creativity/coffe on how to implement functions like

[java]public Set<Entity> getChangedEntities() {} [/java]

without doing a full iteration over all Entities… There’s where the magic happens :wink:



Neverless, thanks for sharing that approch, it definately makes me courious to try something similar. The ComponentHandler is acutally a great idea.

@zzuegg said:
I have to think about your system for a while...
Interesting read.. I think i have gotten the idea behind your approach. However i am lacking of knowledge/creativity/coffe on how to implement functions like
[java]public Set&lt;Entity&gt; getChangedEntities() {} [/java]
without doing a full iteration over all Entities.. There's where the magic happens ;)

Neverless, thanks for sharing that approch, it definately makes me courious to try something similar. The ComponentHandler is acutally a great idea.


Yeah, applyChanges() is the trickiest code in the whole thing. The sets of changed, added, and removed entities just naturally fall out of it, though.

It iterates over the queue of change events and applies them to the current set of entities (in that EntitySet)... if an entity is updated then it is checked at the end to see if it still matches the set (maybe it no longer meets the filters or no longer has the requisite components). If it no longer matches then it is removed and added to the "removed" set. If a component is seen for an entity we don't have yet then an entity wrapper is created with the info we have so far (we might have further change events that set other components on that entity so we don't want to waste time querying them). At the end of the loop, these "potential adds" are checked for completeness and filtering... if they pass then they are added to the entity set and also added to the "added" set. Any other entity with changed components is added to the "changed" set.

The tricky part is that if any of that is done incorrectly then the whole thing comes crashing down around you. It is a critical point of failure and I haven't simplified my own implementation enough that I'm willing to diagram it properly. It's still a bit spaghetti like and I'm unwilling to refactor it because it's working... and it's a critical piece of code for everything. :)

Interesting read, this thread. Im curious, what would be a practical use for an ES ? I.e. would it be suited for handling creeps in an RTS game (where you would have 10000+ units)?

http://hub.jmonkeyengine.org/groups/development-discussion-jme3/forum/topic/entitysystem-how-to-represent-entities-in-the-scenegraph



here you have more fresh topic.



practical use of ES would be everything,

Thanks. Sounds like a topic I could get into, if only I had the time. :slight_smile: Keep up the good work, will follow this thread and read through the one you linked.

Entity Systems can help you solve a high number of problems. Take a look at these links so you can learn more from ES:



Entity Systems are the future of MMOG development – Part 1 – T-machine.org



Entity System 1: Java/Android – T-machine.org



http://entity-systems.wikidot.com/



Entity System: RDBMS-Beta (a new example with source) – T-machine.org



http://slick.cokeandcode.com/wiki/doku.php?id=entity_tutorial



http://www.richardlord.net/blog/what-is-an-entity-framework



http://www.richardlord.net/blog/why-use-an-entity-framework



http://www.altdevblogaday.com/2011/08/24/the-game-entity-–-part-iv-game-systems/

@normen said: Settable in the Component, already failed to make it properly ;)<br /> Ok I guess its time to put this here at least once, we compiled this list of "entity system rules" in the core team some time.<br /> <br /> 1) Entities are only an id value<br /> - Ideally entities are just a "long" id, a class may exist that allows simple access to a specific entities components, e.g. new Entity(1000).getComponent(MyComponent.class) would grab the MyComponent from entity 1000.<br /> <br /> 2) Components are *only* data<br /> - Components only contain data, and absolutely no code, not even helper methods. Components are best implemented as classes with fields and *only* getters and *one* constructor that sets all values. If this becomes cumbersome, you have an indicator that you should either separate the component data into multiple components or even entities. Each time you need data you get a component from an entity and get an immutable copy. To change the component, you create a new component that you set on the entity. This brings "free" thread safety for asynchronous / threaded access and messaging.<br /> <br /> 3) Systems can be anything and use the entities components as their data.<br /> - Systems can introduce other programming paradigms "under the hood", as long as they interact with the "ES parts" of the application only via components.<br /> <br /> 4) One component per type per entity<br /> - this isn't required by the ES per se and it could be convenient sometimes though not doing it gives a much cleaner result and forces one to do the right separation into more components and entities at the right times.<br /> <br /> 5) Don't stick system-specific data into a Component, abstract it to a general level or store it completely in the system<br /> - e.g. one might feel like storing a Spatial inside a VisualComponent or something, instead the system should handle these based on components that only contain generally compatible data (e.g. a model name string).<br /> <br /> 6) Use references instead of lists<br /> - Instead of having an InventoryComponent with a list of items in the inventory, have an InventoryItemComponent that only contains the container entity id and set it *on the inventory item*. This way a) any item can only be in one inventory at any time and b) you can easily get all items in a certain container by querying by the InventoryItemComponent.<br /> <br /> 7) Never subclass Component classes. I went down that road and<br /> everything fell apart. It is more apparent when you put behaviour in<br /> the Components, but since behaviour should all be in a System, then<br /> there should be no need to subclass.<br /> <br /> 8 ) Personally, I find it dangerous to pass entity IDs around as naked longs and be able to instantiate an Entity directly from that. I would argue that it is preferable that Entity objects (whatever convenience interface is defined) always come from the entity system. And that entity IDs be immutable opaque objects (that contain a long) such that one avoids the C/C++ ptr++ style accidents and hacks.<br /> <br /> ... more to come I guess..<br /> <br /> ---- EXAMPLE ----<br /> Note that in this example rule 8 isn't really obeyed. One might want to hide the longs completely and only make the Entity classes available and exchangeable.<br /> <br /> This is an example of a simple in-memory Entity database:<br /> [java]public final class Entities {<br /> <br /> private static long idx = 0;<br /> //TODO: DB<br /> private static ConcurrentHashMap<Long, ConcurrentHashMap<Class, Object>> components = new ConcurrentHashMap<Long, ConcurrentHashMap<Class, Object>>();<br /> <br /> public static Entity getNewEntity() {<br /> return new Entity(idx++);<br /> }<br /> <br /> public static <T extends Object> T getComponent(long entityId, Class<T> controlType) {<br /> ConcurrentHashMap<Class, Object> entityMap = components.get(entityId);<br /> if (entityMap == null) {<br /> return null;<br /> } else {<br /> return (T) entityMap.get(controlType);<br /> }<br /> }<br /> <br /> public static void setComponent(long entityId, Object component) {<br /> ConcurrentHashMap<Class, Object> entityMap = components.get(entityId);<br /> if (entityMap == null) {<br /> entityMap = new ConcurrentHashMap<Class, Object>();<br /> components.put(entityId, entityMap);<br /> }<br /> entityMap.put(component.getClass(), component);<br /> }<br /> <br /> public static void clearComponent(long entityId, Class componentClass) {<br /> ConcurrentHashMap<Class, Object> entityMap = components.get(entityId);<br /> if (entityMap == null) {<br /> return;<br /> }<br /> entityMap.remove(componentClass);<br /> }<br /> <br /> public static List<Entity> getEntities(Class component) {<br /> LinkedList<Entity> list = new LinkedList<Entity>();<br /> for (Iterator<Entry<Long, ConcurrentHashMap<Class, Object>>> it = components.entrySet().iterator(); it.hasNext();) {<br /> Entry<Long, ConcurrentHashMap<Class, Object>> entry = it.next();<br /> if (entry.getValue().containsKey(component)) {<br /> list.add(new Entity(entry.getKey()));<br /> }<br /> }<br /> return list;<br /> }<br /> <br /> public static List<Entity> getEntities(Object component) {<br /> LinkedList<Entity> list = new LinkedList<Entity>();<br /> for (Iterator<Entry<Long, ConcurrentHashMap<Class, Object>>> it = components.entrySet().iterator(); it.hasNext();) {<br /> Entry<Long, ConcurrentHashMap<Class, Object>> entry = it.next();<br /> if (entry.getValue().containsKey(component.getClass())) {<br /> Object curComponent = entry.getValue().get(component.getClass());<br /> if (curComponent.equals(component)) {<br /> list.add(new Entity(entry.getKey()));<br /> }<br /> }<br /> }<br /> return list;<br /> }<br /> }[/java]<br /> <br /> And the corresponding Entity class for access to the components:<br /> [java]public final class Entity {<br /> <br /> private long id;<br /> <br /> public Entity(long id) {<br /> this.id = id;<br /> }<br /> <br /> public <T extends Object> T getComponent(Class<T> controlType) {<br /> return Entities.getComponent(id, controlType);<br /> }<br /> <br /> public void setComponent(Object comp) {<br /> Entities.setComponent(id, comp);<br /> }<br /> <br /> public void clearComponent(Class componentType){<br /> Entities.clearComponent(id, componentType);<br /> }<br /> <br /> public long getId() {<br /> return id;<br /> }<br /> <br /> @Override<br /> public boolean equals(Object o) {<br /> if (o instanceof Entity) {<br /> Entity entity = (Entity) o;<br /> return entity.getId() == id;<br /> }<br /> return super.equals(o);<br /> }<br /> <br /> @Override<br /> public int hashCode() {<br /> return (int) id;<br /> }<br /> }<br /> [/java]<br /> <br /> A Component looks like this:<br /> [java]public final class PositionComponent {<br /> <br /> private Vector3f location;<br /> private Quaternion rotation;<br /> private long lastUpdate;<br /> <br /> public PositionComponent(Vector3f location, Quaternion rotation, long lastUpdate) {<br /> this.location = location.clone();<br /> this.rotation = rotation.clone();<br /> this.lastUpdate = lastUpdate;<br /> }<br /> <br /> public Vector3f getLocation() {<br /> return location;<br /> }<br /> <br /> public Quaternion getRotation() {<br /> return rotation;<br /> }<br /> <br /> public long getLastUpdate() {<br /> return lastUpdate;<br /> }<br /> }[/java]<br /> <br /> And a System using the whole shebang like this (in this case its a Control which only needs to be created once along with its spatial and then handles everything on its own):<br /> [java]public final class EntityControl extends AbstractControl {<br /> <br /> private static final Logger logger = Logger.getLogger(EntityControl.class.getName());<br /> private static final Map<String, Spatial> models = new ConcurrentHashMap<String, Spatial>();<br /> private Entity entity;<br /> private AssetManager manager;<br /> private String currentModelName;<br /> private Spatial currentModel;<br /> private long lastLocationUpdate;<br /> private float updateTime;<br /> private List<AnimControl> animControls;<br /> private List<AnimChannel> animChannels;<br /> <br /> public EntityControl(Entity entity, AssetManager manager) {<br /> this.entity = entity;<br /> this.manager = manager;<br /> }<br /> <br /> @Override<br /> public void setSpatial(Spatial spatial) {<br /> super.setSpatial(spatial);<br /> }<br /> <br /> @Override<br /> protected void controlUpdate(float tpf) {<br /> if (entity == null) {<br /> return;<br /> }<br /> if (!updateVisualRep()) {<br /> return;<br /> }<br /> if (!updateLocation(tpf)) {<br /> return;<br /> }<br /> if (!updateAnimation()) {<br /> return;<br /> }<br /> }<br /> <br /> private boolean updateVisualRep() {<br /> VisualRepComponent visRep = entity.getComponent(VisualRepComponent.class);<br /> if (visRep != null) {<br /> if (currentModelName != null && currentModelName.equals(visRep.getAssetName())) {<br /> return true;<br /> } else {<br /> if (currentModel != null) {<br /> setAnimControls(null);<br /> currentModel.removeFromParent();<br /> }<br /> currentModelName = visRep.getAssetName();<br /> currentModel = manager.loadModel(currentModelName);<br /> setAnimControls(currentModel);<br /> ((Node) spatial).attachChild(currentModel);<br /> }<br /> } else {<br /> //dispose ourselves if the entity has no VisualRepComponent anymore..<br /> setAnimControls(null);<br /> spatial.removeFromParent();<br /> entity.clearComponent(InSceneComponent.class);<br /> return false;<br /> }<br /> return true;<br /> }<br /> <br /> private boolean updateLocation(float tpf) {<br /> PositionComponent position = entity.getComponent(PositionComponent.class);<br /> MovementComponent movement = entity.getComponent(MovementComponent.class);<br /> if (movement != null && position != null) {<br /> spatial.setLocalTranslation(position.getLocation());<br /> spatial.setLocalRotation(position.getRotation());<br /> <br /> if (position.getLastUpdate() == lastLocationUpdate) {<br /> //TODO: interpolate<br /> }<br /> } else if (position != null) {<br /> spatial.setLocalTranslation(position.getLocation());<br /> spatial.setLocalRotation(position.getRotation());<br /> }<br /> return true;<br /> }<br /> <br /> private boolean updateAnimation() {<br /> MovementComponent movement = entity.getComponent(MovementComponent.class);<br /> if (movement != null && movement.getMovement().length() > 0) {<br /> setAnimation("walk");<br /> } else {<br /> setAnimation("idle");<br /> }<br /> return true;<br /> }<br /> <br /> private void setAnimation(String name) {<br /> if (animChannels != null) {<br /> for (Iterator<AnimChannel> it = animChannels.iterator(); it.hasNext();) {<br /> AnimChannel animChannel = it.next();<br /> if (animChannel.getAnimationName() == null || !animChannel.getAnimationName().equals(name)) {<br /> animChannel.setAnim(name);<br /> logger.log(Level.INFO, "Setting anim {0}", name);<br /> if (animChannel.getControl().getAnim(name) != null) {<br /> }<br /> }<br /> }<br /> }<br /> }<br /> <br /> private void setAnimControls(Spatial spatial) {<br /> if (spatial == null) {<br /> if (animControls != null) {<br /> for (Iterator<AnimControl> it = animControls.iterator(); it.hasNext();) {<br /> AnimControl animControl = it.next();<br /> animControl.clearChannels();<br /> }<br /> }<br /> animControls = null;<br /> animChannels = null;<br /> return;<br /> }<br /> SceneGraphVisitorAdapter visitor = new SceneGraphVisitorAdapter() {<br /> <br /> @Override<br /> public void visit(Geometry geom) {<br /> super.visit(geom);<br /> checkForAnimControl(geom);<br /> }<br /> <br /> @Override<br /> public void visit(Node geom) {<br /> super.visit(geom);<br /> checkForAnimControl(geom);<br /> }<br /> <br /> private void checkForAnimControl(Spatial geom) {<br /> AnimControl control = geom.getControl(AnimControl.class);<br /> if (control == null) {<br /> return;<br /> }<br /> if (animControls == null) {<br /> animControls = new LinkedList<AnimControl>();<br /> }<br /> if (animChannels == null) {<br /> animChannels = new LinkedList<AnimChannel>();<br /> }<br /> animControls.add(control);<br /> animChannels.add(control.createChannel());<br /> }<br /> };<br /> spatial.depthFirstTraversal(visitor);<br /> }<br /> <br /> @Override<br /> protected void controlRender(RenderManager rm, ViewPort vp) {<br /> }<br /> <br /> public Control cloneForSpatial(Spatial spatial) {<br /> throw new UnsupportedOperationException("Not supported");<br /> }<br /> }<br /> [/java]

Thank you Normen and thanks for sharing :mrgreen:

Great example of an Entity System, was going crazy with OOP design, so hard to implement, love working with this type of design (Entity System), works well with threading and so much more flexible!