Advertisement

AI Action queue

Started by May 16, 2011 01:53 AM
1 comment, last by nhold 13 years, 6 months ago
One aspect I've been mulling over for a while now is how to approach the issue of an action queue, and would appreciate some input.

If all an AI has to do is move, one would just store each movement from A -> B in a queue of coordinates.
Such as if we assume starting at (0,0) and moving to (2,4) the queue would contain a class/struct containing two integers, such as
(0,1)->(0,2)->(1,3)->(2,4)

On the other hand, how would one approach the issue of an AI which has to perform multiple actions.
Such as if I wish to cross the room and pull a lever, storing coordinates would only accommodate the move, but the pull ing of the lever would need a seperate instruction, lest the agent assume I want them to walk on the lever.

The system I've been considering is to simulate the command structure of assembly instructions in a kind of semi-script for storage purposes.
For example,
#define MOVE 0
#define KILL 1
#define PULL 2

Then create a struct containing (instruction, x, y)

This however limits the agent to picking up only one object from the position it moves to. What if the map tile contains a small pile of goods? Do we pick up everything or just the one at the top of the list?
Likewise if we set a coordinate to attack, by the time we get there the target may have moved.

The alternative to deal with this I've been considering is keeping this format, but extending it to
(instruction, x, y, object)
whereby the object is a pointer to something. We can assume then if the pointer is not NULL, if it's an object we know which object on the tile to pick up, and if it's a creature we can verify its position before we swing a sword at empty space.

The difficulty I have with using a pointer for this purpose however is I feel it's just getting a little too open to possible bugs, or implementation issues.

The last solution I'm considering is creating an abstract class, "Action" with one element in it, an expandable function execute(), which will perform whatever the action does.
I can then, in its child class constructors, pass various things I might needs, such as a pointer to the performer, pointer to the target, coordinates, objects, etc.
That way I can have a queue of <Action> with each action taking different constructor parameters, then just performing an action->execute() to the top of the queue before popping it off.

Any thoughts?
Simple thought - I think it really depends on the capabilities you want to give your agent - if you want to make him realistic, I don't think that he must have pointer to his enemy... Also, you can save some memory in your command struture by having something like (intstruction, parameter1, parameter2). When instruction is move, then it will go to (parameter1, parameter2), when it is to kill, it would be kill enemy, which ID(or pointer) is parameter 1 and so on, so you wouldn't have to store unneceserry variables.

Edit: I forgot to mention then you could give each object(enemies, agent, pickups) ID's and store them in array.
Advertisement

The last solution I'm considering is creating an abstract class, "Action" with one element in it, an expandable function execute(), which will perform whatever the action does.
I can then, in its child class constructors, pass various things I might needs, such as a pointer to the performer, pointer to the target, coordinates, objects, etc.
That way I can have a queue of <Action> with each action taking different constructor parameters, then just performing an action->execute() to the top of the queue before popping it off.

Any thoughts?


This is the same solution that I came up with in my game, except instead of popping off the action as soon as execute finishes it is done it is more of an update function so you can do anything inside of it for however long you need to until it gets finished THEN it gets popped off. If it only needs one then it will only do one, if it needs to continually do things for a while it can do that too.

For example in SC2 (Or any RTS) if you right click on an enemy unit when you have an enemy unit selected it follows the enemy until it is within range, then it attacks.

In an action that would be a FollowEnemy action, which would need some-way to get the enemies position if it still exists. Then during the update function will move closer and check if it is close enough to attack(Or maybe do this check at the beginning, however it would work better) which would then push the attack action on top, which stops the movement and attack animation plays etc, etc. then pop it off the front when it finishes and sends the attack message to the enemy, sending it back to the FollowEnemy action, then if the player gives an over-riding action like move to a point on the map it would pop all actions and push it on top.

In your example you would have FindLever and PullLever action which could be used in conjunction with each-other.

FindLever would search in the game-world for a specific or nearby lever depending on data sent to it.
If it found the lever, or a lever it would then add a PullLever action and set itself to finished. (popping itself off)
Then PullLever could then move towards the lever (utilising some path-finding function) until it was in range (utilising the update function correctly) which would then execute the proper pull lever logic (with animations and everything).

As you can see the update function compared to execute gives actions much more robustness. Sorry if anything is unclear, this is my main point:

Don't just have an execute once and abandon function in actions, add on the functionality for an action to last until a condition is met and then let it tell the entity it is finished.

Engineering Manager at Deloitte Australia

This topic is closed to new replies.

Advertisement