Rudimentary behaviour tree visualization

This commit is contained in:
2024-01-09 22:01:16 +01:00
parent e4cc00d3a7
commit a6b79fb14c
3 changed files with 171 additions and 20 deletions

View File

@@ -24,6 +24,7 @@ struct BzAIBTNode {
} delay; } delay;
struct { struct {
BzAIBTActionFn fn; BzAIBTActionFn fn;
const char *name;
} action; } action;
} as; } as;
@@ -55,6 +56,37 @@ size_t bzAIBTGetNodeStateSize() {
return sizeof(BzAIBTNodeState); return sizeof(BzAIBTNodeState);
} }
const char *bzAIBTNodeTypeToStr(BzAIBTNodeType type) {
switch (type) {
case BZ_AIBT_COMP_SELECTOR:
return "SELECTOR";
case BZ_AIBT_COMP_SEQUENCE:
return "SEQUENCE";
case BZ_AIBT_COMP_PARALLEL_SELECTOR:
return "PARALLEL_SELECTOR";
case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
return "PARALLEL_SEQUENCE";
case BZ_AIBT_DECOR_DUMMY:
return "DUMMY";
case BZ_AIBT_DECOR_SUCCESS:
return "SUCCESS";
case BZ_AIBT_DECOR_FAIL:
return "FAIL";
case BZ_AIBT_DECOR_INVERT:
return "INVERT";
case BZ_AIBT_DECOR_UNTIL_SUCCESS:
return "UNTIL_SUCCESS";
case BZ_AIBT_DECOR_UNTIL_FAIL:
return "UNTIL_FAIL";
case BZ_AIBT_DECOR_REPEAT:
return "REPEAT";
case BZ_AIBT_DECOR_DELAY:
return "DELAY";
case BZ_AIBT_ACTION:
return "ACTION";
}
}
static BzAIBTNode *bzAIBTNodeMake(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTNodeType type) { static BzAIBTNode *bzAIBTNodeMake(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTNodeType type) {
BZ_ASSERT(nodePool); BZ_ASSERT(nodePool);
BZ_ASSERT(bzObjectPoolGetObjectSize(nodePool) == bzAIBTGetNodeSize()); BZ_ASSERT(bzObjectPoolGetObjectSize(nodePool) == bzAIBTGetNodeSize());
@@ -135,15 +167,58 @@ BzAIBTNode *bzAIBTDecorDelay(BzObjectPool *nodePool, BzAIBTNode *parent, f32 ms)
return node; return node;
} }
BzAIBTNode *bzAIBTAction(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTActionFn fn) { BzAIBTNode *bzAIBTAction(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTActionFn fn,
const char *name) {
BzAIBTNode *node = bzAIBTNodeMake(nodePool, parent, BZ_AIBT_ACTION); BzAIBTNode *node = bzAIBTNodeMake(nodePool, parent, BZ_AIBT_ACTION);
node->as.action.fn = fn; node->as.action.fn = fn;
node->as.action.name = name;
return node; return node;
} }
BzAIBTNodeType bzAIBTGetNodeType(BzAIBTNode *node) { i32 bzAIBTDecorGetRepeat(const BzAIBTNode *node) {
BZ_ASSERT(node->type == BZ_AIBT_DECOR_REPEAT);
return node->as.repeat.n;
}
f32 bzAIBTDecorGetDelay(const BzAIBTNode *node) {
BZ_ASSERT(node->type == BZ_AIBT_DECOR_DELAY);
return node->as.delay.ms;
}
BzAIBTActionFn bzAIBTActionGetFn(const BzAIBTNode *node) {
BZ_ASSERT(node->type == BZ_AIBT_ACTION);
return node->as.action.fn;
}
const char *bzAIBTActionGetName(const BzAIBTNode *node) {
BZ_ASSERT(node->type == BZ_AIBT_ACTION);
return node->as.action.name;
}
BzAIBTNodeType bzAIBTGetNodeType(const BzAIBTNode *node) {
return node->type; return node->type;
} }
BzAIBTNode *bzAIBTNodeChild(const BzAIBTNode *node) {
return node->first;
}
BzAIBTNode *bzAIBTNodeNext(const BzAIBTNode *node) {
return node->next;
}
const BzAIBTNodeState *bzAIBTNodeStateNext(const BzAIBTNodeState *state) {
BZ_ASSERT(state);
return state->next;
}
bool bzAIBTNodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state) {
return state && state->node == node;
}
i32 bzAIBTRepeatStateGetIter(const BzAIBTNodeState *state) {
BZ_ASSERT(state->node && state->node->type == BZ_AIBT_DECOR_REPEAT);
return state->as.repeat.iter;
}
f32 bzAIBTDelayStateGetElapsed(const BzAIBTNodeState *state) {
BZ_ASSERT(state->node && state->node->type == BZ_AIBT_DECOR_DELAY);
return state->as.delay.elapsed;
}
BzAIBTState bzAIBTCreateState(const BzAIBTStateDesc *desc) { BzAIBTState bzAIBTCreateState(const BzAIBTStateDesc *desc) {
BZ_ASSERT(desc->pool); BZ_ASSERT(desc->pool);
@@ -205,7 +280,7 @@ void bzAIBTStateRelease(BzAIBTState *state, BzAIBTNodeState *nodeState) {
} }
bool nodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state) { bool nodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state) {
return state && state->node == node; return bzAIBTNodeMatchesState(node, state);
} }
BzAIBTNodeState *getNextNodeState(const BzAIBTNode *node, BzAIBTNodeState *nodeState) { BzAIBTNodeState *getNextNodeState(const BzAIBTNode *node, BzAIBTNodeState *nodeState) {
if (nodeState && nodeMatchesState(node, nodeState)) if (nodeState && nodeMatchesState(node, nodeState))

View File

@@ -55,6 +55,8 @@ typedef struct BzAIBTStateDesc {
size_t bzAIBTGetNodeSize(); size_t bzAIBTGetNodeSize();
size_t bzAIBTGetNodeStateSize(); size_t bzAIBTGetNodeStateSize();
const char *bzAIBTNodeTypeToStr(BzAIBTNodeType type);
BzAIBTNode *bzAIBTMakeRoot(BzObjectPool *nodePool); BzAIBTNode *bzAIBTMakeRoot(BzObjectPool *nodePool);
void bzAIBTDestroyRoot(BzObjectPool *nodePool, BzAIBTNode *node); void bzAIBTDestroyRoot(BzObjectPool *nodePool, BzAIBTNode *node);
@@ -70,9 +72,26 @@ BzAIBTNode *bzAIBTDecorUntilFail(BzObjectPool *nodePool, BzAIBTNode *parent);
BzAIBTNode *bzAIBTDecorRepeat(BzObjectPool *nodePool, BzAIBTNode *parent, i32 n); BzAIBTNode *bzAIBTDecorRepeat(BzObjectPool *nodePool, BzAIBTNode *parent, i32 n);
BzAIBTNode *bzAIBTDecorDelay(BzObjectPool *nodePool, BzAIBTNode *parent, f32 ms); BzAIBTNode *bzAIBTDecorDelay(BzObjectPool *nodePool, BzAIBTNode *parent, f32 ms);
BzAIBTNode *bzAIBTAction(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTActionFn fn); BzAIBTNode *bzAIBTAction(BzObjectPool *nodePool, BzAIBTNode *parent, BzAIBTActionFn fn,
const char *name);
BzAIBTNodeType bzAIBTGetNodeType(BzAIBTNode *node); // Reflection data
i32 bzAIBTDecorGetRepeat(const BzAIBTNode *node);
f32 bzAIBTDecorGetDelay(const BzAIBTNode *node);
BzAIBTActionFn bzAIBTActionGetFn(const BzAIBTNode *node);
const char *bzAIBTActionGetName(const BzAIBTNode *node);
BzAIBTNodeType bzAIBTGetNodeType(const BzAIBTNode *node);
BzAIBTNode *bzAIBTNodeChild(const BzAIBTNode *node);
BzAIBTNode *bzAIBTNodeNext(const BzAIBTNode *node);
const BzAIBTNodeState *bzAIBTNodeStateNext(const BzAIBTNodeState *state);
bool bzAIBTNodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state);
i32 bzAIBTRepeatStateGetIter(const BzAIBTNodeState *state);
f32 bzAIBTDelayStateGetElapsed(const BzAIBTNodeState *state);
BzAIBTState bzAIBTCreateState(const BzAIBTStateDesc *desc); BzAIBTState bzAIBTCreateState(const BzAIBTStateDesc *desc);
void bzAIBTDestroyState(BzAIBTState *state); void bzAIBTDestroyState(BzAIBTState *state);

View File

@@ -5,6 +5,7 @@ BzObjectPool *nodePool = NULL;
BzObjectPool *nodeStatePool = NULL; BzObjectPool *nodeStatePool = NULL;
BzAIBTNode *printBT = NULL; BzAIBTNode *printBT = NULL;
BzAIBTState agentState;
BzAIBTStatus printAction(void *data) { BzAIBTStatus printAction(void *data) {
bzLogInfo("Hello, world!"); bzLogInfo("Hello, world!");
@@ -12,6 +13,7 @@ BzAIBTStatus printAction(void *data) {
} }
bool init(int *game) { bool init(int *game) {
rlImGuiSetup(true);
nodePool = bzObjectPoolCreate(&(BzObjectPoolDesc) { nodePool = bzObjectPoolCreate(&(BzObjectPoolDesc) {
.objectSize = bzAIBTGetNodeSize(), .objectSize = bzAIBTGetNodeSize(),
}); });
@@ -29,40 +31,95 @@ bool init(int *game) {
BzAIBTNode *seq = bzAIBTCompSequence(nodePool, node, false); BzAIBTNode *seq = bzAIBTCompSequence(nodePool, node, false);
bzAIBTDecorDelay(nodePool, seq, 1.0f); bzAIBTDecorDelay(nodePool, seq, 1.0f);
bzAIBTAction(nodePool, seq, printAction); bzAIBTAction(nodePool, seq, printAction, "printAction");
BzAIBTState state = bzAIBTCreateState(&(BzAIBTStateDesc) { agentState = bzAIBTCreateState(&(BzAIBTStateDesc) {
.root = printBT, .root = printBT,
.pool = nodeStatePool, .pool = nodeStatePool,
.userData = NULL .userData = NULL
}); });
BzAIBTStatus status = BZ_AIBT_RUNNING; return true;
}
i32 count = 0; void deinit(int *game) {
while (status == BZ_AIBT_RUNNING) {
status = bzAIBTExecute(&state, 0.2f);
count++;
assert(status != BZ_AIBT_ERROR);
}
bzLogInfo("Iter: %d", count);
bzObjectPoolDestroy(nodePool); bzObjectPoolDestroy(nodePool);
bzObjectPoolDestroy(nodeStatePool); bzObjectPoolDestroy(nodeStatePool);
}
return true; void igRenderBTNode(const BzAIBTNode *node, const BzAIBTNodeState *state, i32 depth) {
const BzAIBTNode *child = bzAIBTNodeChild(node);
BzAIBTNodeType type = bzAIBTGetNodeType(node);
char extraInfo[128];
extraInfo[0] = '\0';
bool hasState = bzAIBTNodeMatchesState(node, state);
switch (type) {
case BZ_AIBT_DECOR_REPEAT:
if (hasState) {
snprintf(extraInfo, sizeof(extraInfo), "(%d < %d)",
bzAIBTRepeatStateGetIter(state),
bzAIBTDecorGetRepeat(node));
} else {
snprintf(extraInfo, sizeof(extraInfo), "(%d)", bzAIBTDecorGetRepeat(node));
}
break;
case BZ_AIBT_DECOR_DELAY:
if (hasState) {
snprintf(extraInfo, sizeof(extraInfo), "(%.2f < %.2fms)",
bzAIBTDelayStateGetElapsed(state),
bzAIBTDecorGetDelay(node));
} else {
snprintf(extraInfo, sizeof(extraInfo), "(%.2fms)", bzAIBTDecorGetDelay(node));
}
break;
case BZ_AIBT_ACTION:
snprintf(extraInfo, sizeof(extraInfo), "(%s:%p)",
bzAIBTActionGetName(node),
bzAIBTActionGetFn(node));
break;
default:
break;
}
if (hasState)
state = bzAIBTNodeStateNext(state);
ImVec4 color = {1.0f, 1.0f, 1.0f, 1.0f};
if (hasState)
color = (ImVec4) {1.0f, 1.0f, 0.5f, 1.0f};
igTextColored(color, "%*s%s %s", depth * 4, "",
bzAIBTNodeTypeToStr(type), extraInfo);
while (child) {
igRenderBTNode(child, state, depth + 1);
child = bzAIBTNodeNext(child);
}
}
void igRenderBT(BzAIBTState *state) {
const BzAIBTNode *root = state->root;
if (igBegin("BehaviourTree", NULL, 0)) {
igRenderBTNode(root, state->first, 0);
}
igEnd();
} }
void render(float dt, int *game) { void render(float dt, int *game) {
ClearBackground(WHITE); ClearBackground(WHITE);
BzAIBTStatus status = bzAIBTExecute(&agentState, dt);
rlImGuiBegin();
igRenderBT(&agentState);
igShowDemoWindow(NULL);
rlImGuiEnd();
} }
bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) {
appDesc->init = (BzAppInitFunc) init; appDesc->init = (BzAppInitFunc) init;
appDesc->deinit = (BzAppDeinitFunc ) deinit;
appDesc->render = (BzAppRenderFunc) render; appDesc->render = (BzAppRenderFunc) render;
init(NULL); return true;
return false;
} }