How to keep bots from getting stuck
by Trent Reimer · 02/21/2004 (8:52 pm) · 2 comments
This is a simple approach to keeping your bots moving smoothly through the mission. It requires a little more work when setting up the path markers for the mission but once they are set up properly your bots will never be lost.
First you will need to design your mission in such a way that there are no spots on the mission where a bot cannot "see" a path marker if it looks around. You do not need to oversaturate the mission with markers - simply ensure that there are no blind spots. This may take a few tries if you have rolling terrain but watching the bots in action will quickly point out where the problem areas are. Keep in mind that something as simple as a gentle rise in ground can block visibility.
Secondly you'll need a repeating function which checks to see if the bot(s) is/are stuck. The GameBeavers code base uses a function called "think" to check all the bots every half second. You certainly wouldn't want to call it more frequently than that as it triggers a good number of checks. You may decide on a different function so the following is the heart of the matter. This logic will appear in whatever function you choose.
OK, now for the functions which the above code calls. You can put these in "aiPlayer.cs" (which is usually in the "server/scripts" directory of your game or mod folder). These next 2 functions are taken from resources already submitted by other coders to GG.
And finally these last two functions detect "visible" path markers and instruct the bot to move accordingly. You may want to consider the typemasks used to detect obstacles. If your mission has other possible obstacles to navigate you will want to add them to that part - it will be commented below.
Hope it helps. Feel free to post improvements so the rest of us can view them too.
First you will need to design your mission in such a way that there are no spots on the mission where a bot cannot "see" a path marker if it looks around. You do not need to oversaturate the mission with markers - simply ensure that there are no blind spots. This may take a few tries if you have rolling terrain but watching the bots in action will quickly point out where the problem areas are. Keep in mind that something as simple as a gentle rise in ground can block visibility.
Secondly you'll need a repeating function which checks to see if the bot(s) is/are stuck. The GameBeavers code base uses a function called "think" to check all the bots every half second. You certainly wouldn't want to call it more frequently than that as it triggers a good number of checks. You may decide on a different function so the following is the heart of the matter. This logic will appear in whatever function you choose.
// "%bot" is the bot in question. "oldposition" is a property set for
// the bot each time through the function so we can measure its progress
// from one iteration to the next.
%botPosition = %bot.getTransform();
%botMovement = vectorDist(%bot.oldposition, %botPosition);
%bot.oldposition = %botPosition; // ready for the next iteration
if(%botMovement < 2) // hmm, not moving much
{
%this.shakeStep(%index);
%moveBot.joinPath();
} else { // proceed along the path
if(isObject(%moveBot.path))
{
if (%moveBot.currentNode < 0
|| %moveBot.currentNode > %moveBot.path.getCount())
{
%moveBot.currentNode = 1;
}
%moveBot.moveToNode(%moveBot.currentNode);
}
}OK, now for the functions which the above code calls. You can put these in "aiPlayer.cs" (which is usually in the "server/scripts" directory of your game or mod folder). These next 2 functions are taken from resources already submitted by other coders to GG.
// get the bot to shake loose of minor obstacles
function AIPlayer::shakeStep(%this)
{
%xrand = getRandom(-15,50);
%yrand = getRandom(-15,45);
%newLoc = %this.getTransform();
//adjust the x pos too by 80% of the bot's index number
%newLoc = setWord(%newLoc, 0, (getWord(%newLoc, 0) + (%xrand)));
%newLoc = setWord(%newLoc, 1, (getWord(%newLoc, 1) + (%yrand)));
%this.setMoveDestination(%newLoc);
}
// move the bot along the path, the bot also looks where it's going
function AIPlayer::moveToNode(%this,%index)
{
// Move to the given path node index
%this.currentNode = %index;
%node = %this.path.getObject(%index);
%this.setMoveDestination(%node.getTransform());
%this.setAimLocation(%node.getPosition());
}And finally these last two functions detect "visible" path markers and instruct the bot to move accordingly. You may want to consider the typemasks used to detect obstacles. If your mission has other possible obstacles to navigate you will want to add them to that part - it will be commented below.
// join the closest visible path node
function AIPlayer::joinPath(%this)
{
if(isObject(%this.path))
{
%nodeCount = %this.path.getCount();
if(%this.currentNode < 0) %this.currentNode = 1;
// check if the selected marker is visible
if(%this.checkLOSToMarker(%this.path.getObject(%this.currentNode)))
{
%this.moveToNode(%this.currentNode);
return;
} else {
// first try a limited range search to be nice to the CPU
if(%this.currentNode + 3 >= %nodeCount) %topNode = %nodeCount - 1;
else %topNode = %this.currentNode + 3;
for(%i = %topNode; %i >= 0; %i--)
{
if(%this.checkLOSToMarker(%this.path.getObject(%i)))
{
%this.currentNode = %i;
%this.moveToNode(%this.currentNode);
return;
}
}
// if we're still here it's time for a full path search
for(%i = %nodeCount - 1; %i >= 0; %i--)
{
if(%this.checkLOSToMarker(%this.path.getObject(%i)))
{
%this.currentNode = %i;
%this.moveToNode(%this.currentNode);
return;
}
}
// if we're still here there's problem with the mission layout
echo("mission error - path node visibility error");
}
}
else echo("mission error - no path to follow");
}
// check if the selected path node is visible
function AIPlayer::checkLOSToMarker(%this, %obj)
{
if(!isObject(%obj)) return false;
%eyeTrans = %this.getEyePoint();
%eyeEnd = %obj.getTransform();
// you may think of other typemasks which represent obstacles
%searchResult = containerRayCast(%eyeTrans, %eyeEnd,
$TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType, %this);
%foundObject = firstWord(%searchResult);
if(!%foundObject) return true; // path is clear
else return false;
}Hope it helps. Feel free to post improvements so the rest of us can view them too.
About the author
Recent Blogs
• Plan for Trent Reimer• Plan for Trent Reimer
• Plan for Trent Reimer
• Plan for Trent Reimer
#2
This works too, don't get me wrong :-). I've been trying to figure out interior navigation so I can stick that into a resource.
07/09/2007 (4:01 am)
Killer Kork has an approach feature that works pretty well. When the AI has an obstacle in the way to a destination, the AI casts a horizontal line out at his position and checks 40 pooints at 0.3 meters apart. For every point, the AI checks to see if a rayCast running from that point to the destination doesn't hit that obstacle. If it does (not clear path), try another spot. If it doesn't hit anything, move to that point along the horizontal line and then go to the destination.This works too, don't get me wrong :-). I've been trying to figure out interior navigation so I can stick that into a resource.

Torque Owner Banshee