Created
April 2, 2024 08:06
-
-
Save ArnCarveris/ff3fc75889123ad3866ef54ae3a72f69 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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