From 8043d56af98060e504b212b777de872ad028160a Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Thu, 11 Jan 2024 07:48:49 +0100 Subject: [PATCH] Add cleanup for compsites in BT + refactor --- engine/breeze/ai/behaviour_tree.c | 47 ++++++++++++++++++++++--------- engine/tests/btree_test.c | 2 +- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/engine/breeze/ai/behaviour_tree.c b/engine/breeze/ai/behaviour_tree.c index 0da8d6f..4eea827 100644 --- a/engine/breeze/ai/behaviour_tree.c +++ b/engine/breeze/ai/behaviour_tree.c @@ -345,7 +345,8 @@ static BzBTNodeState *getNodeState(const BzBTNode *node, BzBTExecState *state) { } return NULL; } -static BzBTNodeState *ensureValidNodeState(const BzBTNode *node, BzObjectPool *pool, BzBTNodeState *existing) { +static BzBTNodeState *ensureValidNodeState(const BzBTNode *node, BzBTNodeState *existing, + BzObjectPool *pool) { if (existing) return existing; @@ -356,12 +357,24 @@ static BzBTNodeState *ensureValidNodeState(const BzBTNode *node, BzObjectPool *p bzMemSet(&existing->as, 0, sizeof(existing->as)); return existing; } -static void releaseNodeState(BzObjectPool *pool, BzBTNodeState *nodeState) { +static void releaseNodeState(BzBTNodeState *nodeState, BzObjectPool *pool) { bzObjectPoolRelease(pool, nodeState); } +static inline void bzBTClearNodeState(const BzBTNode *node, BzBTExecState *state, + BzObjectPool *pool) { + BzBTNodeState *nodeState = getNodeState(node, state); + if (nodeState == NULL) return; + releaseNodeState(nodeState, pool); + + const BzBTNode *first = node->first; + while (first) { + bzBTClearNodeState(first, state, pool); + first = first->next; + } +} static inline BzBTExecReturn bzBTExecuteNode(const BzBTNode *node, f32 dt, - BzBTExecState *nodeState, BzObjectPool *statePool); + BzBTExecState *state, BzObjectPool *statePool); static inline BzBTExecReturn bzBTExecuteComposite(const BzBTNode *node, f32 dt, BzBTExecState *state, BzObjectPool *statePool) { BzBTNodeState *nodeState = getNodeState(node, state); @@ -445,14 +458,22 @@ static inline BzBTExecReturn bzBTExecuteComposite(const BzBTNode *node, f32 dt, bool finished = execReturn.status == BZ_BT_SUCCESS || execReturn.status == BZ_BT_FAIL; + + // Clean up, if exited prematurely + const BzBTNode *pChild = child; + while (finished && pChild) { + bzBTClearNodeState(pChild, state, statePool); + pChild = pChild->next; + } + if (finished) { BZ_ASSERT(execReturn.state.first == NULL); - if (nodeState) releaseNodeState(statePool, nodeState); + if (nodeState) releaseNodeState(nodeState, statePool); return execReturn; } BZ_ASSERT(execReturn.status == BZ_BT_RUNNING); // Parallels don't utilize state but still require it for correct tree traversal. - nodeState = ensureValidNodeState(node, statePool, nodeState); + nodeState = ensureValidNodeState(node, nodeState, statePool); nodeState->as.composite.running = child; execStatePushFront(&execReturn.state, nodeState); @@ -469,11 +490,11 @@ static inline BzBTExecReturn bzBTExecuteDecorator(const BzBTNode *node, f32 dt, switch (node->type) { case BZ_BT_DECOR_REPEAT: - nodeState = ensureValidNodeState(node, statePool, nodeState); + nodeState = ensureValidNodeState(node, nodeState, statePool); execStatePushBack(&execReturn.state, nodeState); break; case BZ_BT_DECOR_DELAY: - nodeState = ensureValidNodeState(node, statePool, nodeState); + nodeState = ensureValidNodeState(node, nodeState, statePool); nodeState->as.delay.elapsed += dt; execStatePushBack(&execReturn.state, nodeState); if (nodeState->as.delay.elapsed < node->as.delay.ms) { @@ -481,7 +502,7 @@ static inline BzBTExecReturn bzBTExecuteDecorator(const BzBTNode *node, f32 dt, return execReturn; } BZ_ASSERT(nodeState == execStatePopFront(&execReturn.state)); - releaseNodeState(statePool, nodeState); + releaseNodeState(nodeState, statePool); break; default: break; @@ -542,7 +563,7 @@ static inline BzBTExecReturn bzBTExecuteDecorator(const BzBTNode *node, f32 dt, nodeState->as.repeat.iter++; if (nodeState->as.repeat.iter >= node->as.repeat.n) { BZ_ASSERT(nodeState == execStatePopFront(&execReturn.state)); - releaseNodeState(statePool, nodeState); + releaseNodeState(nodeState, statePool); execReturn.status = childReturn.status; break; } @@ -554,14 +575,14 @@ static inline BzBTExecReturn bzBTExecuteDecorator(const BzBTNode *node, f32 dt, return execReturn; } static inline BzBTExecReturn bzBTExecuteNode(const BzBTNode *node, f32 dt, - BzBTExecState *nodeState, BzObjectPool *statePool) { + BzBTExecState *state, BzObjectPool *statePool) { BzBTExecReturn execReturn = { .status = BZ_BT_ERROR }; switch (node->type) { case BZ_BT_COMP_SELECTOR: case BZ_BT_COMP_SEQUENCE: case BZ_BT_COMP_P_SELECTOR: case BZ_BT_COMP_P_SEQUENCE: - execReturn = bzBTExecuteComposite(node, dt, nodeState, statePool); + execReturn = bzBTExecuteComposite(node, dt, state, statePool); break; case BZ_BT_DECOR_DUMMY: case BZ_BT_DECOR_SUCCESS: @@ -571,11 +592,11 @@ static inline BzBTExecReturn bzBTExecuteNode(const BzBTNode *node, f32 dt, case BZ_BT_DECOR_UNTIL_FAIL: case BZ_BT_DECOR_REPEAT: case BZ_BT_DECOR_DELAY: - execReturn = bzBTExecuteDecorator(node, dt, nodeState, statePool); + execReturn = bzBTExecuteDecorator(node, dt, state, statePool); break; case BZ_BT_ACTION: BZ_ASSERT(node->as.action.fn); - execReturn.status = node->as.action.fn(nodeState->userData, dt); + execReturn.status = node->as.action.fn(state->userData, dt); break; } return execReturn; diff --git a/engine/tests/btree_test.c b/engine/tests/btree_test.c index a200f7d..756819e 100644 --- a/engine/tests/btree_test.c +++ b/engine/tests/btree_test.c @@ -26,7 +26,7 @@ bool init(int *game) { // delay 1s // print "Hello, world!" printBT = bzBTMakeRoot(nodePool); - BzBTNode *pseq = bzBTCompSequence(nodePool, printBT, true); + BzBTNode *pseq = bzBTCompSelector(nodePool, printBT, true); bzBTDecorDelay(nodePool, pseq, 2.0f); BzBTNode *node = bzBTDecorRepeat(nodePool, pseq, 5);