Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System with an optional component in its query does not fire when that component is disabled using ecs_enable_component #314

Open
jfaust-fy opened this issue Feb 18, 2021 · 2 comments
Labels
bug Something isn't working

Comments

@jfaust-fy
Copy link

jfaust-fy commented Feb 18, 2021

Describe the bug
[edit] I was wrong in thinking the example was working - I was checking the wrong thing.

I'm not sure if this is intended behavior or not. If I modify example 50 to add an optional component to the system like this:

#include <disable_component.h>

typedef struct {
    float x, y;
} Position;

typedef struct {
    float x, y;
} Position2;

void PrintPosition(ecs_iter_t *it) {
    ecs_world_t *world = it->world;

    Position *p = ecs_column(it, Position, 2);

    for (int i = 0; i < it->count; i ++) {
        if (p) {
            printf("%s: {%f, %f}\n", 
                ecs_get_name(world, it->entities[i]), p[i].x, p[i].y);
        } else {
            printf("%s: no position\n", 
                ecs_get_name(world, it->entities[i]));
        }
    }
}

int main(int argc, char *argv[]) {
    ecs_world_t *world = ecs_init_w_args(argc, argv);

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Position2);

    ECS_SYSTEM(world, PrintPosition, 0, Position2, ?Position);

    ECS_ENTITY(world, e1, 0);
    ECS_ENTITY(world, e2, 0);
    ECS_ENTITY(world, e3, 0);

    ecs_set(world, e1, Position, {10, 20});
    ecs_set(world, e2, Position, {30, 40});
    ecs_set(world, e3, Position, {50, 60});

    ecs_set(world, e1, Position2, {10, 20});
    ecs_set(world, e2, Position2, {30, 40});
    ecs_set(world, e3, Position2, {50, 60});

    printf("e1 enabled: %d\n", ecs_is_component_enabled(world, e1, Position));
    printf("e2 enabled: %d\n", ecs_is_component_enabled(world, e2, Position));
    printf("e3 enabled: %d\n", ecs_is_component_enabled(world, e3, Position));

    printf("\n1st run: all components enabled\n");
    ecs_run(world, PrintPosition, 0, NULL);

    /* Enable component of entity 1, disable component of entity 2 */
    ecs_enable_component(world, e1, Position, true);
    ecs_enable_component(world, e2, Position, false);

    /* e2 will now show up as disabled */
    printf("\n");
    printf("e1 enabled: %d\n", ecs_is_component_enabled(world, e1, Position));
    printf("e2 enabled: %d\n", ecs_is_component_enabled(world, e2, Position));
    printf("e3 enabled: %d\n", ecs_is_component_enabled(world, e3, Position));    

    printf("\n2nd run: e2 is disabled\n");
    ecs_run(world, PrintPosition, 0, NULL);

    /* Print types. Both e1 and e2 will have DISABLED|Position. This does not
     * actually mean that both are disabled. Instead it means that both entities
     * have a bitset that tracks whether the component is disabled. */
    char *type = NULL;
    printf("\n");
    printf("e1 type: %s\n", type = ecs_type_str(world, ecs_get_type(world, e1))); free(type);
    printf("e2 type: %s\n", type = ecs_type_str(world, ecs_get_type(world, e2))); free(type);
    printf("e3 type: %s\n", type = ecs_type_str(world, ecs_get_type(world, e3))); free(type);

    ecs_fini(world);
}

The result is:

e1 enabled: 1
e2 enabled: 1
e3 enabled: 1

1st run: all components enabled
e1: {10.000000, 20.000000}
e2: {30.000000, 40.000000}
e3: {50.000000, 60.000000}

e1 enabled: 1
e2 enabled: 0
e3 enabled: 1

2nd run: e2 is disabled
e3: {50.000000, 60.000000}
e1: {10.000000, 20.000000}

e1 type: Name,Position,Position2,DISABLED|Position
e2 type: Name,Position,Position2,DISABLED|Position
e3 type: Name,Position,Position2

I would expect the 2nd run to include e2: no position.

Is this the expected behavior when disabling components? I was assuming they'd act the same as ecs_remove() in this case.

@jfaust-fy jfaust-fy added the bug Something isn't working label Feb 18, 2021
@SanderMertens SanderMertens added this to To do in v2.4 via automation Feb 19, 2021
@SanderMertens
Copy link
Owner

The reason this happens is because disabled components are still stored in the same array as enabled components. Each array is made available once to the system, and so when you have a query with an optional component, you will get a pointer to that array from ecs_column, even though some values in that array are disabled.

I agree that this is not ideal. To fix this, queries that iterate an optional column with disabled values should iterate the column twice: once for entities with enabled values and once for entities with disabled values. In the latter case I could make ecs_column return NULL, which would make this behavior transparent for the iterator.

I'll start working on a design for this.

@jfaust-fy
Copy link
Author

FYI it's not pressing for me, just saw the change & thought it might be better for a case I have. I'd say in the short term maybe just a note in the docs about it would help.

@SanderMertens SanderMertens removed this from To do in v2.4 Mar 17, 2021
@SanderMertens SanderMertens added this to To do in v3 via automation Mar 17, 2021
@SanderMertens SanderMertens removed this from To do in v3 Aug 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants