Game Development Community

OnStuck and Dynamic Avoidance

by Steve Acaster · in Technical Issues · 04/02/2008 (5:28 pm) · 9 replies

I was wondering what solutions people use to stop AI from walking into each other and causing a traffic jam.

My pathfinding uses a node-net, but only for getting around obstacles in the path of the AI's goal. I don't want to create an open/closed list of available nodes. I can think of two 2 ways off the top of my head:

Idea One: wait till the collision occurs and call OnStuck.

//[psuedo-code]
Function OnStuck()
{
if(%obj.moving == true && %obj.getVelocity !> 0)
{
%obj.move/impulse_vector_90_degrees.distance = 3;
}
%this.schedule(500, another.OnStuck.check);
}
//[/psuedo-code]

Idea Two: have a loop constantly checking for possible collisions. I guess that would have to be some sort of raycast and then move aside if there's someone blocking the path.
//[psuedo-code]
Function CollisionCheck()
{
if(%obj.moving == true)
//cos there's no point doing a check if we are stationary
{
%raycast = line/container/angle (%this.getPosition, 10 units ahead, $typeMask::Playerbody, ignore%self)
   if (%raycast = hits.someone)
   {
   %this.move/impulse_vector_90_degrees.distance = 3;
   }
}
%this.schedule(500, loop.CollisionCheck);
}
//[/psuedo-code]

Well, those are my ideas. Has anyone got others or their working own code?

#1
04/02/2008 (7:09 pm)
You should check out KillerKork, it makes a net of raycasts in an area about 40 meters square to see if it's current path is blocked and to find a new route around.
#2
04/04/2008 (4:50 am)
One problem i notice with your collision check is that your typeMask is invalid. it should be
$TypeMasks::playerObjectType
//or
$TypeMasks::AIplayerObjectType
#3
04/05/2008 (8:05 am)
Bryce, that was just psuedo-code off the top my head - nothing I was actually trying to use. But well spotted.
#4
04/06/2008 (12:27 am)
Quote:
You should check out KillerKork, it makes a net of raycasts in an area about 40 meters square to see if it's current path is blocked and to find a new route around.

My concern with such a method is the huge resource drain from casting so many rays. For my own game I have the AI add its destination to a node block manager. So no two bots can try to reach the same spot. Instead they'll pick an adjacent node.

I also have the pathmanager remove nodes that are currently occupied by non moving bots. This prevents my A* routine from trying to give a path that intersects where an AI is standing idle.

Lastly I fixed the OnMoveStuck() throwback in the source to actually work if the bot hasn't moved more than .008 distance between a tick. This interrupts the current move, has the bot take a few steps backwards, blocks the spot it hit off the node list, and performs A* again to avoid whatever it had hit.

It's not perfect, but so far it is working well. If you play a production game like Two Worlds or Gothic you'll see all they do for OnStuck is walk to a random space behind the intended target, wait 1 second, then try to go back. They'll do this over infinitely.

What those games do seem to handle better is model on model collision. Somehow they make their models slippery feeling. In 10 minutes of trying with Two Worlds I could not take my player character and get it stuck on a single other character. I almost wonder if their collision boxes don't have a wedge on the front or something.. in any case its a lot harder for bots to get stuck on each other in those games for some reason.
#5
04/06/2008 (10:33 am)
========
Slippery AI
========

I managed to get my AI to be more non-stick and bounce off each other by altering the onCollision call. As this gets called automatically it shouldn't use up any extra resources.

//updated the code

function Demoplayer::onCollision(%this,%obj,%col)
{
   if (%obj.getState() $= "Dead")
      return;

   if (%col.getClassName() $= "AIPlayer" || %col.getClassName() $= "Player")
	{
%colpos = %col.getWorldBoxCenter();
%objpos = %obj.getWorldBoxCenter();
%imp = vectorSub(%objpos,%colpos);
%imp = vectorScale(%imp,1000);
%obj.applyImpulse(%col.getWorldBoxCenter(),%imp);
	}
}

The important bit is (%imp, 1000) - the actual force used to repel. By applying the impulse to the worldboxcenter it seems to prevent the AI from bouncing into the air.

Obviously change "Demoplayer", to your AI type and update all other AI types you may have to use it.

I found most of this code somewhere in the forums (can't remember where), and just changed a few bits to make it more suitable.

Here's a dodgy video of non-stick AI in action using first version code (hence there is a bouncing AI).
#6
04/06/2008 (9:08 pm)
Hmm, I might give that a try. I'd spent about a week working on impulse code but ended up being unhappy with it. I guess the difference is that in my game the AI is walking around and moving much slower. My models also tend to be...thicker. So the impulse needs to push them not only back but also to the side, and not be so forceful as to look unnatural.

The ideal solution for my game would be some sort of small constant repelling impulse, but that would require some engine modification I think.
#7
04/07/2008 (8:48 am)
After further practising;

The boundingbox values deal with collision between the AI and other models, Difs and the terrain. Large values to the boundingbox in the relevant AIPlayer or Player cs causes impacts to occur further from the model. The X and Y values for the boundingbox are world-centric (at least in TGE) meaning that they don't rotate with the model.

datablock PlayerData(demoplayer : PlayerBody)
{
//   boundingBox = "0.8 0.8 1.8";//nice for fitting through doors
   boundingBox = "1.2 1.2 2.3";//the original value
}

Higher values mean the AI has more trouble fitting through doorways and the sort. It also determines which bit of the model stands on the ground, so on rough terrain it can look like they are hovering a little.

Obviously this all comes from the models bounds at export. So I'm wondering if you can change the shape of the bounds box in the 3D modelling program (I use Blender - 'cos it's free). So instead of a cube you could have a more collision friendly octagan..... no idea if it'd work though, but worth a try.

=========================

Jason, you could modify my oncollision call above to produce an impulse vector at an angle from the object you collided with, (say 45 degrees) that might make it more slippery, that wouldn't require an engine hack.

Though my current understanding of vectors and trigonometry sucks (not even sure it that's how you spell it!).
#8
04/07/2008 (4:27 pm)
Yeah I did that and while it worked it looked odd having two bots walk gently into each other and then fiercly rebound back. I'd rather have a slow impulse push them apart as they walk towards each other. Impulses are single shot events and don't look right when dealing with a slower environment.

Quote:
Obviously this all comes from the models bounds at export.

I asked this question a week ago and was told the collision-1,2,3... of the exported models are ignored entirely in favor of the hard coded boundingBox variable :( I tested this by not including a collision box in my models and they still collided with things just fine.

It sucks the bounding box doesn't rotate with the player. It really makes it difficult for handling oddly shaped objects... But I guess I'll have to do some more testing.
#9
04/07/2008 (4:33 pm)
Quote:exported models are ignored entirely in favor of the hard coded boundingBox variable

There goes my 8 sided bounds idea, then.