Rudimentary behaviour tree visualization
This commit is contained in:
@@ -24,6 +24,7 @@ struct BzAIBTNode {
|
||||
} delay;
|
||||
struct {
|
||||
BzAIBTActionFn fn;
|
||||
const char *name;
|
||||
} action;
|
||||
} as;
|
||||
|
||||
@@ -55,6 +56,37 @@ size_t bzAIBTGetNodeStateSize() {
|
||||
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) {
|
||||
BZ_ASSERT(nodePool);
|
||||
BZ_ASSERT(bzObjectPoolGetObjectSize(nodePool) == bzAIBTGetNodeSize());
|
||||
@@ -135,15 +167,58 @@ BzAIBTNode *bzAIBTDecorDelay(BzObjectPool *nodePool, BzAIBTNode *parent, f32 ms)
|
||||
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);
|
||||
node->as.action.fn = fn;
|
||||
node->as.action.name = name;
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
BZ_ASSERT(desc->pool);
|
||||
@@ -205,7 +280,7 @@ void bzAIBTStateRelease(BzAIBTState *state, BzAIBTNodeState *nodeState) {
|
||||
}
|
||||
|
||||
bool nodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state) {
|
||||
return state && state->node == node;
|
||||
return bzAIBTNodeMatchesState(node, state);
|
||||
}
|
||||
BzAIBTNodeState *getNextNodeState(const BzAIBTNode *node, BzAIBTNodeState *nodeState) {
|
||||
if (nodeState && nodeMatchesState(node, nodeState))
|
||||
|
||||
@@ -55,6 +55,8 @@ typedef struct BzAIBTStateDesc {
|
||||
size_t bzAIBTGetNodeSize();
|
||||
size_t bzAIBTGetNodeStateSize();
|
||||
|
||||
const char *bzAIBTNodeTypeToStr(BzAIBTNodeType type);
|
||||
|
||||
BzAIBTNode *bzAIBTMakeRoot(BzObjectPool *nodePool);
|
||||
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 *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);
|
||||
void bzAIBTDestroyState(BzAIBTState *state);
|
||||
|
||||
@@ -5,6 +5,7 @@ BzObjectPool *nodePool = NULL;
|
||||
BzObjectPool *nodeStatePool = NULL;
|
||||
|
||||
BzAIBTNode *printBT = NULL;
|
||||
BzAIBTState agentState;
|
||||
|
||||
BzAIBTStatus printAction(void *data) {
|
||||
bzLogInfo("Hello, world!");
|
||||
@@ -12,6 +13,7 @@ BzAIBTStatus printAction(void *data) {
|
||||
}
|
||||
|
||||
bool init(int *game) {
|
||||
rlImGuiSetup(true);
|
||||
nodePool = bzObjectPoolCreate(&(BzObjectPoolDesc) {
|
||||
.objectSize = bzAIBTGetNodeSize(),
|
||||
});
|
||||
@@ -29,40 +31,95 @@ bool init(int *game) {
|
||||
BzAIBTNode *seq = bzAIBTCompSequence(nodePool, node, false);
|
||||
|
||||
bzAIBTDecorDelay(nodePool, seq, 1.0f);
|
||||
bzAIBTAction(nodePool, seq, printAction);
|
||||
bzAIBTAction(nodePool, seq, printAction, "printAction");
|
||||
|
||||
BzAIBTState state = bzAIBTCreateState(&(BzAIBTStateDesc) {
|
||||
agentState = bzAIBTCreateState(&(BzAIBTStateDesc) {
|
||||
.root = printBT,
|
||||
.pool = nodeStatePool,
|
||||
.userData = NULL
|
||||
});
|
||||
|
||||
BzAIBTStatus status = BZ_AIBT_RUNNING;
|
||||
|
||||
i32 count = 0;
|
||||
while (status == BZ_AIBT_RUNNING) {
|
||||
status = bzAIBTExecute(&state, 0.2f);
|
||||
count++;
|
||||
assert(status != BZ_AIBT_ERROR);
|
||||
}
|
||||
bzLogInfo("Iter: %d", count);
|
||||
return true;
|
||||
}
|
||||
void deinit(int *game) {
|
||||
|
||||
bzObjectPoolDestroy(nodePool);
|
||||
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) {
|
||||
ClearBackground(WHITE);
|
||||
|
||||
BzAIBTStatus status = bzAIBTExecute(&agentState, dt);
|
||||
|
||||
rlImGuiBegin();
|
||||
igRenderBT(&agentState);
|
||||
igShowDemoWindow(NULL);
|
||||
rlImGuiEnd();
|
||||
}
|
||||
|
||||
bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) {
|
||||
appDesc->init = (BzAppInitFunc) init;
|
||||
appDesc->deinit = (BzAppDeinitFunc ) deinit;
|
||||
appDesc->render = (BzAppRenderFunc) render;
|
||||
|
||||
init(NULL);
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user