How does ZayES compare with Ashley?

I’m tempted to try ZayES, but I’m a bit scared by the lack of documentation and the immutability of created objects…

How does it compare with Ashley?

Thanks!

Components are immutable so that you can properly manage the sets. I’m not sure how ashley handles this but it tends to be a MUCH heavier framework and inserts itself into every part of your process. Else… they will prefer immutable components also as there would be no other way to properly filter in entity sets.

Immutable components is not a requirement in Zay-ES. But if you do use mutable components then it must handle its own threading and can no longer participate properly in component value filtering. (ie: you can have a set that includes the component and that will work but you can’t have a set that filters on values of the component because the framework won’t know about them.)

Immutable components are an advantage in an ES because you get multithreading for free that way.

There is documentation on the JME wiki about entity systems that was directly written about Zay-ES. It’s a very light-weight framework so other than the ES learning curve itself, Zay-ES should be pretty easy to pick up. One of these days I’ll give it the same treatment I gave Lemur… though it’s interesting that Zay-ES already seems to have waaaay more users than Lemur, even undocumented.

It’s a much more straight-forward framework than something like Ashley because it doesn’t inject itself all over the place. You want data from it… you ask for data. You want to set data, you set data. No special “EntitySystem” callback mechanisms or any of that. Your systems are whatever you want them to be.

In my heavily biased opinion, Zay-ES is a more “pure” ES implementation… which was precisely its design goal. I took great pains to figure out how to optimize it without breaking the purity. Frameworks like Ashley make different choices.

Hopefully other Zay-ES users can chime in.

Edit: fixed a typo where immutable should have been mutable. An important typo. :wink:

4 Likes

Thanks! Although probably not reccommended, can I mix “traditional logic POJO” and ZayES?

For example, I want the hitpoint of my game entities to be an ES, but the rest (position, speed etc) is on a Java object?

Sure… though you lost a lot of benefits that way.

Well that question is not really Zay es specific,

What you often do in an ES system is to have normal java pojos doing the processing. For example the largest use case for that in my game would be the ship computers.

In the es there is a component specifying the computer’s existence and a few properties, like processing speed.
The Computer system basically then maintains a filter and a map uuid of entity with computer to a luaj runtime object.

This way I have a clean ES state of the game, but can still use extensive oo based librarys for processing the data they represent.

(i’m a user of my own Empire-ES :P, but zay es and my system are actually behaving pretty much 100% the same way)

I wouldn’t recommand it.

ECS can give headache on it’s own. Mixing it with traditional OOP will brake the contracts that help you keeping things simple and stupid. You wil probably find it strange and unpredicatable where ECS is definitly not.

I recommand to do as much ECS as possible, or none.

1 Like

So let’s suppose I am on a dungeon and I have an “HitPoint component” with value 100. Then I wear a magic ring that gives me a boost of hitpoints (component “hitpoint booster”). Then I drink a magic potion that gives me a temporary boost of hitpoint.

Does this scenario require “nesting” of components? So that the magic ring is like the potion, except for that the latter has a decay?

How should the original HitPoint component keep track of all the boosting components?

This has been covered in other threads a few times… even recently.

The ‘buffs’ would be their own entities attached to the player. You’d have one system somewhere that was responsible for adjusting player hitpoints up/down depending on damage entities or buff entities attached to the player.

Then you can decay those entities just like anything else… either to stop the effect of the potion or let the fire burn out on that fire damage.

1 Like

And note, the beauty of the ES is that this is not even specific to players. It would work for anything with buff/damage entities and hitpoints. So you could even light a chair on fire if it had a Hitpoints component.

1 Like

I would highly suggest to study first the asteroid panic from @pspeed. There is a thread Asteroid Panic: Why is Spatial in HashMap instead a Component of mine where I did try to understand that implementation. I asked a lot of stupid questions and got a lot of good and very detailed answers there. With that help it took me about 2 days to write my own simple clone of invaders with ES :smile:

And this is as well a helpful thread ES Design Question with a lot of good advices/hints/explanations.

Just one remark on this all or none. I think on the same abstraction level, yes absolute. But on a different abstraction level not necessarily true in my opinion. On a different abstraction level you can have also more “traditional” code. Game logic, AI and the like.

Well that’s true. I’ve found myself overkilling the “ECS at al cost” rule many times, like when I wanted to manage IA behaviors as component, or the terrains node as entites.

An ECS can effectively communicate with a physic engine, an IA behavior tree or state machine, a terrain generator. It actually is communicating with the viewing managed by jMonkey in our case.

But all those are now systems for the ECS :smile:

        node.setUserData("eid", myEntityID);

I thought it was a cool idead to attach an entity to a spatial, so that on collision (managed by the scene graph) I can pull the game logic. My first attempt didn’t go well… I’ll do something else :stuck_out_tongue:

GRAVE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IllegalArgumentException: Unsupported type: com.simsilica.es.EntityId
at com.jme3.scene.UserData.getObjectType(UserData.java:124)
at com.jme3.scene.Spatial.setUserData(Spatial.java:1357)

EDIT: maybe this will work?

node.setUserData("eid", myEntityID.getId());

EDIT2: Ahem… is there a way to get the EntityID from the “entity ID”? :confused:

spatial.setUserData("EntityId", entity.getId().getId());

Works for me. user data only accept primitive values :

public static byte getObjectType(Object type) {
    if (type instanceof Integer) {
        return 0;
    } else if (type instanceof Float) {
        return 1;
    } else if (type instanceof Boolean) {
        return 2;
    } else if (type instanceof String) {
        return 3;
    } else if (type instanceof Long) {
        return 4;
    } else {
        throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName());
    }
}

Thanks! But how do I retrieve the EntityID… from the entity ID? :stuck_out_tongue:

By the way, setUserData accepts Savable. So you can put virtually anything on a spatial :smile:

But I don’t want to extend EntityID to make it savable… hope there is a better way (besides managing the scene graph from the entity system)

EDIT: to clarify, I’d like to use something like this:

EntityData.getEntityByID(long id)

…so that I can retrieve the EntityID which was attached to the aforementioned Spatial.

EntityId id = new EntityId(spatial.getUserData("EntityId"));
entityData.getEntity(id, MyComp1.class, MyComp2.class);

You have to know that you can’t retreive an Entity, expecting an object with all its components attached. The object Entity represent a bucket, filled with the component you ask like in my exemple.

If you want to get a specific component of an entity, it may be simplier to write :

MyComp c = entityData.getComponent(eid, MyComp.class);

Knowing that it could be null if no such component is attached on that entity

Nope, for now I just need the EntityId. Added the method on my fork:

@pspeed probably this is an antipattern for some reason, however should I make a PR? :wink:

Why would you want the EntitData to save EntityId objects and provide them back?

EntityId implements comparable and overrides hashCode() and equals(). Just build a new EntityId from your long value ^^

???

EntityId entityId = new EntityId(someId);

…that’s how you get an EntityId from an entity ID. Don’t know why you need to add a method for it.

EntityId instances or not NOR SHALL THEY EVER BE considered ==. Just don’t do it. You will screw yourself up 10000 ways.