Skip to content

Instantly share code, notes, and snippets.

@ArnCarveris
Created April 2, 2024 08:06
Show Gist options
  • Save ArnCarveris/ff3fc75889123ad3866ef54ae3a72f69 to your computer and use it in GitHub Desktop.
Save ArnCarveris/ff3fc75889123ad3866ef54ae3a72f69 to your computer and use it in GitHub Desktop.

Revisions

  1. ArnCarveris created this gist Apr 2, 2024.
    318 changes: 318 additions & 0 deletions flecs_bt.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,318 @@
    #include <https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h>
    #include <https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c>
    #include <https://gist.github.com/ArnCarveris/535cf7c25007271bfa75d6396853a4a3/raw/99b57783a906995b4b89c4fb8d053c0ed2981603/flecs_godbolt.h>

    typedef struct {
    float x, y;
    } Position, Velocity;

    typedef struct {
    float time;
    } Timer;

    typedef struct {
    ecs_query_t *q;
    ecs_entity_t parent;
    ecs_iter_t it;
    int i;
    int j;
    char n;
    } ecs_enum_t;


    // Forward declare component so we can use it from functions other than main
    ECS_COMPONENT_DECLARE(Position);
    ECS_COMPONENT_DECLARE(Velocity);
    ECS_COMPONENT_DECLARE(Timer);


    ECS_TAG_DECLARE(IsAction);
    ECS_TAG_DECLARE(IsRunning);
    ECS_TAG_DECLARE(IsFailed);
    ECS_TAG_DECLARE(IsSucceeded);

    ecs_enum_t ecs_enum_new( ecs_world_t *ecs, ecs_query_t *q, ecs_entity_t e)
    {
    ecs_enum_t self;
    self.q = q;
    self.parent = e;
    self.it = ecs_query_iter(ecs, self.q);
    ecs_query_set_group(&self.it, e);
    self.i = 0;
    self.j = 0;
    self.n = 1;

    return self;
    }

    void ecs_enum_reset(ecs_enum_t* self)
    {
    self->it = ecs_query_iter(self->it.world, self->q);
    ecs_query_set_group(&self->it, self->parent);
    self->i = 0;
    self->j = 0;
    self->n = 1;
    }

    int ecs_enum_next(ecs_enum_t* self)
    {
    if (self->n)
    {
    self->n = 0;
    self->j = 0;

    if (!ecs_query_next(&self->it))
    return 0;
    }

    self->i = self->j++;

    if (self->i < self->it.count)
    return 1;

    self->n = 1;

    return ecs_enum_next(self);
    }

    ecs_entity_t ecs_enum_entity(ecs_enum_t* self)
    {
    return self->it.entities[self->i];
    }


    void print_tab(int tab)
    {
    for(int i = 0; i <= tab; ++i)
    printf(" ");
    }

    void print_entity(int tab, ecs_world_t *ecs, ecs_entity_t e) {
    char *path_str = ecs_get_fullpath(ecs, e);
    char *type_str = ecs_type_str(ecs, ecs_get_type(ecs, e));
    print_tab(tab);
    printf("#%d %s [%s] ", e, path_str, type_str);
    ecs_os_free(type_str);
    ecs_os_free(path_str);
    }

    void print_tree(int tab, ecs_world_t *ecs, ecs_query_t* q, ecs_entity_t e) {
    // Print hierarchical name of entity & the entity type
    print_entity(tab, ecs, e);
    printf("\n");
    ++tab;
    // Iterate children recursively
    for (ecs_enum_t i = ecs_enum_new(ecs, q, e); ecs_enum_next(&i);)
    {
    print_tree(tab, ecs, q, ecs_enum_entity(&i));
    }
    }


    void ecs_bt_remove_states(ecs_world_t *world, ecs_entity_t target, ecs_entity_t action)
    {
    ecs_remove_pair(world, target, IsRunning, action);
    ecs_remove_pair(world, target, IsFailed, action);
    ecs_remove_pair(world, target, IsSucceeded, action);
    }

    void ecs_bt_change_state(ecs_world_t *world, ecs_entity_t target, ecs_entity_t state)
    {
    ecs_entity_t action = ecs_get_target(world, target, IsRunning, 1);

    if (action == 0)
    action = ecs_get_target(world, target, IsRunning, 0);

    if (action == 0)
    return;

    ecs_remove_pair(world, target, IsRunning, action);
    ecs_add_pair(world, target, state, action);

    //printf("ecs_bt_change_state: #%d action: ", target); print_entity_name(world, action);
    //printf(" state: "); print_entity_name(world, state); printf("\n");
    }


    int ecs_bt_first(ecs_enum_t* self, ecs_world_t *world, ecs_entity_t target)
    {
    if (!ecs_enum_next(self))
    return 0;

    ecs_entity_t action = ecs_enum_entity(self);

    ecs_add_pair(world, target, EcsIsA, action);
    ecs_add_pair(world, target, IsRunning, action);

    return 1;
    }

    int ecs_bt_next(ecs_enum_t* self, ecs_world_t *world, ecs_entity_t target, ecs_entity_t action)
    {
    ecs_remove_pair(world, target, EcsIsA, action);
    ecs_bt_remove_states(world, target, action);
    if (!ecs_enum_next(self))
    return 0;

    action = ecs_enum_entity(self);

    ecs_add_pair(world, target, EcsIsA, action);
    ecs_add_pair(world, target, IsRunning, action);

    return 1;
    }

    void ecs_bt_parent(ecs_enum_t* self, ecs_world_t *world, ecs_entity_t target, ecs_entity_t state)
    {
    ecs_bt_remove_states(world, target, self->parent);
    ecs_add_pair(world, target, state, self->parent);
    }

    ecs_entity_t ecs_bt_sequence(ecs_enum_t* self, ecs_world_t *world, ecs_entity_t target)
    {
    ecs_entity_t state = IsFailed;

    if (self->n)
    {
    if (ecs_bt_first(self, world, target))
    state = IsRunning;
    }
    else
    {
    ecs_entity_t action = ecs_enum_entity(self);

    if (ecs_has_pair(world, target, EcsIsA, action))
    {
    if (ecs_has_pair(world, target, IsRunning, action))
    state = IsRunning;
    else if (ecs_has_pair(world, target, IsSucceeded, action))
    if (ecs_bt_next(self, world, target, action))
    state = IsRunning;
    else
    state = IsSucceeded;
    }
    else
    {
    if (ecs_bt_first(self, world, target))
    state = IsRunning;
    }
    }

    ecs_bt_parent(self, world, target, state);

    if (state == IsSucceeded)
    ecs_enum_reset(self);

    return state;
    }

    void print_bt(int tab, ecs_world_t* ecs, ecs_query_t* q, ecs_entity_t behaviour, ecs_entity_t agent, int ticks, float dt)
    {
    ecs_enum_t bt = ecs_enum_new(ecs, q, behaviour);

    for (int i = 0; i < ticks; ++i)
    {
    printf("\n -~> %d/%d \n", i+1, ticks);

    ecs_bt_sequence(&bt, ecs, agent);
    print_entity(tab, ecs, agent);
    ecs_progress(ecs, dt);
    }

    }

    void Move(ecs_iter_t *it) {
    Position *p = ecs_field(it, Position, 1);
    Velocity *v = ecs_field(it, Velocity, 2);

    for (int i = 0; i < it->count; i++) {
    p[i].x += v[i].x;
    p[i].y += v[i].y;
    printf("Move(%f, %f)\n", p[i].x, p[i].y);
    ecs_bt_change_state(it->real_world, it->entities[i], IsSucceeded);
    }
    }

    void Wait(ecs_iter_t *it) {
    Timer *t = ecs_field(it, Timer, 1);

    for (int i = 0; i < it->count; i++) {
    t[i].time -= it->delta_time;

    printf("Wait(%f)\n", t[i].time);

    if (t[i].time > 0)
    continue;

    ecs_bt_change_state(it->world, it->entities[i], IsSucceeded);

    //FIXME: it should be automatically removed on (IsA) bc `Timer` component is overriden
    ecs_remove(it->world, it->entities[i], Timer);
    }
    }


    int main(int argc, char *argv[]) {

    ecs_world_t *ecs = ecs_init_for_godbolt(argc, argv);

    ECS_COMPONENT_DEFINE(ecs, Position);
    ECS_COMPONENT_DEFINE(ecs, Velocity);
    ECS_COMPONENT_DEFINE(ecs, Timer);

    ECS_TAG_DEFINE(ecs, IsAction);
    ECS_TAG_DEFINE(ecs, IsRunning);
    ECS_TAG_DEFINE(ecs, IsFailed);
    ECS_TAG_DEFINE(ecs, IsSucceeded);

    ECS_SYSTEM(ecs, Move, EcsOnUpdate, Position, Velocity);
    ECS_SYSTEM(ecs, Wait, EcsOnUpdate, Timer);

    ecs_query_t* q = ecs_query(ecs, {
    .filter.terms = {
    { .id = ecs_pair(EcsChildOf, EcsWildcard) },
    { .id = EcsPrefab, .oper = EcsOptional }
    },
    .group_by_id = EcsChildOf,
    .order_by = flecs_entity_compare,
    });


    ecs_entity_t seq = ecs_new_prefab(ecs, "Seq");

    {
    ecs_entity_t move = ecs_new_prefab(ecs, "1Move");
    ecs_add_pair(ecs, move, EcsChildOf, seq);
    ecs_add_pair(ecs, Move, IsAction, move);
    ecs_set(ecs, move, Velocity, {1, 1});
    } {
    ecs_entity_t wait = ecs_new_prefab(ecs, "2Wait");
    ecs_add_pair(ecs, wait, EcsChildOf, seq);
    ecs_add_pair(ecs, Wait, IsAction, wait);
    ecs_set(ecs, wait, Timer, {1});
    ecs_override(ecs, wait, Timer);

    } {
    ecs_entity_t move = ecs_new_prefab(ecs, "3Move");
    ecs_add_pair(ecs, Move, IsAction, move);
    ecs_add_pair(ecs, move, EcsChildOf, seq);
    ecs_set(ecs, move, Velocity, {-3, 3});
    }

    printf("actions: \n");
    print_tree(0, ecs, q, Move);
    print_tree(0, ecs, q, Wait);
    printf("behaviours: \n");
    print_tree(0, ecs, q, seq);

    printf("agents: \n");
    ecs_entity_t agent = ecs_new_entity(ecs, "Agent");
    ecs_set(ecs, agent, Position, {10, 20});

    print_bt(0, ecs, q, seq, agent, 20, 0.5);

    printf("\nfini: \n");


    return ecs_fini(ecs);
    }