Ralith / hecs

A handy ECS
Apache License 2.0
967 stars 82 forks source link

Better understanding or maybe Documentation? #313

Closed genusistimelord closed 1 year ago

genusistimelord commented 1 year ago

So I am currently playing around with this Library and I really do not understand how the Queues work fully. This includes using the With and Without and Satisfies Structures.

I made a little Test demo that outputs things in a Set way But it would be nice to know things like

what does With's Q for and R for exactly, Does setting () tell it to not return anything but allow results to go through Etc.. Maybe some more Complex Examples and better documentation on how these would work and to work to get different things.

anyways It would be nice if you could better explain how these work in the documentation and maybe here. Would also be great If some sort of Discord or chat channel Existed. Anyways here is the Code Id like help with Explaining and maybe even telling me if this is the Correct way to do it.

fn main() {
    // initialize tracing
    let mut world = World::new();

    let a = world.spawn((Widget, "a".to_string(), Data(2), "abc"));
    let b = world.spawn((Widget, "b".to_string(), Data(1), Parent(a)));
    let c = world.spawn((Widget, "c".to_string(), Data(1), Parent(a), Hidden));
    let d = world.spawn((Widget, "d".to_string(), Data(1), Parent(b)));

    // This should Only Return b
    for child in world
       .query::<Without<(&Widget, &Parent), &Hidden>>()
        .iter()
        .filter(|(_entity, (_, parent))| **parent == Parent(a))
        .map(|(entity, _)| entity)
    {
        let name = world.get::<&String>(child).unwrap();

        println!("Child without Hidden: {}", name.as_str());
    }

    // This should only return a
    for child in world
        .query::<Without<&Widget, Or<&Parent, &Hidden>>>()
        .iter()
        .map(|(entity, _)| entity)
    {
        let name = world.get::<&String>(child).unwrap();

        println!("Child without Hidden and Parent: {}", name.as_str());
    }

    // This should only ever return C
    for child in world
        .query::<With<(&Widget, &Parent), &Hidden>>()
        .iter()
        .filter(|(_entity, (_, parent, _))| **parent == Parent(a))
        .map(|(entity, _)| entity)
    {
        let name = world.get::<&String>(child).unwrap();

        println!("Child hidden with parent: {}", name.as_str());
    }

    //d should never return.
}

After Some Help from my friend Tom I have come to the conclusion that these are more like

Without<ThingToReturn, ThingsThatCantExist> With<ThingsToReturn, ThingsThatMustExist>

Not really sure how Satisfies really works.

Ralith commented 1 year ago

Have you read the API docs for those types?

genusistimelord commented 1 year ago

yes I have and they did not make sense to me tbh.

https://docs.rs/hecs/latest/hecs/struct.With.html if this is what your talking about. as when I look at this it doesn't really tell me what Q and R are for. so its a trail and error game to figure it out.

Also with Satisfies I am guessing it just a boolean to determine if something Exists. Can it be mixed with With and Without?

Ralith commented 1 year ago

it doesn't really tell me what Q and R are for

The example illustrates a query where Q is evaluated, but restricted to entities that match R. It also cites a similar illustration in QueryBorrow::with.

Can it be mixed with With and Without?

If a type variable is introduced like Q: Query, that means it can accept any type that implements Query. All of With, Without, and Satisfies implement Query.

genusistimelord commented 1 year ago

sorry I just did not understand the example itself. So i had to make a little test that printed off what it did find in each query. Just saying it would be nice to just have it Say on there either in the description or a comment above the example explaining what it is doing. Also adding the assert kind of works but maybe adding another assert that fails due to lets say c not existing in the queue would help make it more obvious. It is just a different way of thinking on my part sorry.

I can Expand the docs if you would like and add more examples for things. just I gotta figure out how to use each part. could you show me an example of how to use Satisfies with With and Without on how it might be used to determine if something should be shown or not maybe.

Ralith commented 1 year ago

Thanks for the feedback; I agree that referring to the type parameters by name improves clarity here.

I don't think we specifically need examples of combining these in particular. You can always use any type that implements Query in any type variable that requires Query, just like with any other Rust generics. For example, With<Without<Satisfies<Q>, R>, S> gives you a query that yields a bool indicating whether an entity matches Q for each entity that matches S and does not match R.

genusistimelord commented 1 year ago

Ahh nice. Yeah, the more clarity there is the better and easier it becomes to learn.