Rudimentary behaviour tree visualization
This commit is contained in:
@@ -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))
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user