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.
#include <https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h>
#include <https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c>
#include <https://gist.githubusercontent.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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment