I’ve got the two tanks now. One is a member of team green and the other is a member of team red. They both walk to different locations and both use the same BT.
There where a few problems but nothing extreme. The following picture will be the last picture of the game world with the nodes drawn. I add this picture as a demonstration of my success in adding another entity and have it use the BT itself.
The picture above shows two blue squares one starts from the top right and the other from the bottom left. they each have walked to their team opponents’ base. The bases are the green and the red square and the blue dots represent the path they took.
The following content of this blog will be the functions and short descriptions of them. First there are descriptions and under them are the links to pastebin with copy-pasted code.
Starting from PrototypeState::Enter(): It is very similar to how it looked last time I posted it, but the difference is a new function being called twice, TankStateEnter(point,TEAM). I set the two tanks up in them and assigns each a starting position and a team color.
TankStateEnter(point,TEAM): It allocates memory for a new tank, give the tank a team and the location of the enemy base and push it back into an std::vector. The tankIter is an std::vector<Tank*>::iterator. and this little one is a potential for undefined behavior! this is ok FOR NOW since I set it before the game loop and the two times I do it will not change any thing.. However, if I use this function during runtime – imagine: in the midst of battle, a new tank must be spawned, spawning a new one would reset the tankIter and make it start over from the beginning. Say there are 100 tanks. The tankIter is at the 50:th tank and the BT is moving it around. Next frame, tankIter gets reset and the next tank to be guided by the BT is not the 51:st tank but the first tank again. If that wouldn’t crash the program, it would make the rest of the 50 tanks miss updates. I WILL fix this potential problem before I proceed with anything new.
Jumping to the next relevant function: FirstBTFindBase::RunTheTree() it just calls the child’s of the root node, which is the sequence node, initialize function and its update function. The initialize function only resets the currentChild pointer to its children to point to the first element of the children std::vector. I didn’t think one line of code was worth copy-pasting.
Next function is the update function in the sequence node. This is totally untouched by me, it is exactly as I found it in the BTSK. What is going on here is that the sequence try if all of its children, one by one, is returning the success status enum. in this case all the children are leaf nodes and only either checks a bool or makes something happen. In larger, more complex trees, the children might be sequences or other types of non leaf nodes and have children of their own.
Anyway, if any child returns fail or running (not complete with task) the sequence returns this status to its own parent and the next frame it starts over again. When all children have returned success, the sequence can finally also succeed and make his parents proud.
One last function before my own implementations are described. The tick function. it initializes the current child if necessary (mine is just an empty function) if its status is NOT running. It then accesses the current child’s status update function. if that does not become running it terminates the child (again my terminate function is also empty) and returns the current status.
Now we enter my custom nodes’ update functions! They all have that same blackboard as set by the ServiceLocator in PrototypeState::Enter(). The blackBoard has friend access to the game world to be able to check everything that is going on in there. (some “friend”, huh?) It can also directly manipulate things in the game world. (be careful….) ok so here it will become obvious that the tankIter has to be set somewhere else than when a new tank is created.
First out is ConditionArrived::update(). It checks to see if the tank is at its target location. The IsArrived() function of BlackBoard looks very much like most of the blackboard functions used in the tree as of now. I perform a check on tankIter to see if it has reached its end, in which case it is time to start over. It might be redundant to keep checking this in every function that uses the tankIter but I have used it as a safety percussion while debugging the code.
Next child, the ConditionBaseWalkable. the update function, very straight forward. check to see if the node at the position of the enemy base is accessible. The two BlackBoard functions GetGoalPosition() and IsBaseNodeAccessible() are presented and described in that order.
Get GoalPosition, return the current tank its enemyBasePosition sf::Vector2f.
IsBaseNodeAccessible, just what it sounds like. By getting the graphobject from the game world, it investigates if the node in the position is accessible.
Ok the resent ones have been a little dull so I will refresh your sore eyes with some more code; The first action node, ActionFindPathAndGo. this one is a little more interesting because it does two things, find path and give the tank its path to walk. (I have a discussion about this a few posts ago, sometimes you might want to find the path but not immediately start walking it)
I first check to see if the searching bool is true in which case the node returns running. (the bool is true when the tank has a path and while it is walking the path.) If this is not the case, I get the position of the tank and the tank’s enemyBasePosition. Those are used to find a path and if the path is found, the tank’s searching bool is set to true (to stop the tree from starting a new search while the tank is walking) and the tank’s pathlist is filled.
I am only going to paste the ActionFindPathAndGo::update() because I have limited pastebin pastes, BUT I am HAPPY to show anybody any of the functions in this function, just ask!
And now, the final but most important node, ActionSetArrived. It sets the bool arrived in tank. This is important because this is the initial test condition and now that it is true, the first leaf node of the tree will fail, avoiding unwanted attempts to find paths and walk them.
Last function for today: the Tank::update(). Here is where the bool searching is reset to false when any path is completely walked:
Thanks and good night, I will return in a couple of days to plan ahead and continue the progress.