Game Development Community

RTS Kit Pathfinding

by Robert Stewart · in RTS Starter Kit · 02/17/2006 (6:27 pm) · 11 replies

Well I have created a simple pathfinding feature for the RTS Kit, first I explain what it does.

Basically when you move a unit from Point A to Point B, it will send a ray and find any objects from A to B. If the ray collides it will find the side of the bounding box that the ray collided against, from there it will find the closest corner of the BB to the clicked position. Then it projects another ray from the units destination to the clicked position to find if any more objects are in the way.

This version was done completly in script and could benifit from been ported to the engine, I will probrally work on this fairly soon.

Here are the changes you have to make, + is the added/changed lines.


engine\game\rts\rtsunit.cc
if(goalDelta.len() < .1)
         {
            stopMove();
            mVelocity.set(0,0,0);
            setActionThread(PlayerData::RootAnim,true,false,false);
			+if (this->mNextPos.x <= 10000 && this->mNextPos.x >= -10000 && this->mNextPos.z != 154)
			+{
	        char bufs[48];
            +dSprintf(bufs, sizeof(bufs), "%0.2f %0.2f %0.2f", this->mNextPos.x, this->mNextPos.y, this->mNextPos.z);
			+Con::executef(this, 2, "NextNode", bufs);
			+}
			+else
			+{
            +Con::executef(this, 1, "onReachDestination");
			+}

engine\game\rts\rtsunit.h
S32 getTeam() const { return mTeam; }
   +Point3F getNextPos() const { return mNextPos; }

#1
02/17/2006 (6:28 pm)
Example\starter.fps\client\inputhandler.cs
Change you're onRightMouseDownTerrain to this.
function GuiRTSTSCtrl::onRightMouseDownTerrain(%this, %x, %y, %z)
{
if(%this.selectionIncludesTeam)
{
%searchMasks = ($TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType      | $TypeMasks::ForceFieldObjectType | $TypeMasks::PhysicalZoneObjectType | $TypeMasks::StaticTSObjectType);
%unitPos = $player.gettransform();
%clickPos = (%x SPC %y SPC %z);
%target = ContainerRayCast (%unitPos, %clickPos, %searchMasks);
   echo ("Current Unit's Positon" SPC %unitPos);
   echo ("Clicked Position" SPC %clickPos);
   echo ("RayCast Information" SPC %target);
if (%target)
{
   %unit = getWord(%target, 0);
   %type = %unit.getname();
   %xx = getWord(%target, 1);
   %yy = getWord(%target, 2);
   %zz = getWord(%target, 3);
   %spawnPoint = (%xx SPC %yy SPC 250);
   %nodePos = %xx SPC %yy;
   %unitCornerA = (getword(%unit.getworldbox(), 0) SPC getword(%unit.getworldbox(), 1));
   %unitCornerB = (getword(%unit.getworldbox(), 3) SPC getword(%unit.getworldbox(), 4));
   %unitCornerC = (getword(%unit.getworldbox(), 0) SPC getword(%unit.getworldbox(), 4));
   %unitCornerD = (getword(%unit.getworldbox(), 3) SPC getword(%unit.getworldbox(), 1));
   if (%xx == getword(%unit.getworldbox(), 0))
 {
  //commandToServer('IssueMove', getword(%unit.getworldbox(), 0) + 2, getword(%unit.getworldbox(), 1) - 1, %z);
   if(VectorDist(%clickPos, %unitCornerA) < VectorDist(%clickPos, %unitCornerC))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 0), getword(%unit.getworldbox(), 1) - 1, %z);
  echo ("Heading to corner A X");
$player.setnextpos(%x SPC %y SPC %z);
}
   if(VectorDist(%clickPos, %unitCornerA) > VectorDist(%clickPos, %unitCornerC))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 0), getword(%unit.getworldbox(), 4) + 1, %z);
$player.setnextpos(%x SPC %y SPC %z);
  echo ("Heading to corner C X");
}
 }

   if (%xx == getword(%unit.getworldbox(), 3)) 
 {
   if(VectorDist(%clickPos, %unitCornerB) < VectorDist(%clickPos, %unitCornerD))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 3), getword(%unit.getworldbox(), 4) + 1, %z);
  echo ("Heading to corner B X");
$player.setnextpos(%x SPC %y SPC %z);
}
   if(VectorDist(%clickPos, %unitCornerB) > VectorDist(%clickPos, %unitCornerD))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 3), getword(%unit.getworldbox(), 1) - 1, %z);
  echo ("Heading to corner D X");
$player.setnextpos(%x SPC %y SPC %z);
}
 }

   if (%yy == getword(%unit.getworldbox(), 1))
 {
   if(VectorDist(%clickPos, %unitCornerA) < VectorDist(%clickPos, %unitCornerD))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 0) - 1, getword(%unit.getworldbox(), 1), %z);
  echo ("Heading to corner A Y");
$player.setnextpos(%x SPC %y SPC %z);
}
   if(VectorDist(%clickPos, %unitCornerA) > VectorDist(%clickPos, %unitCornerD))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 3) + 1, getword(%unit.getworldbox(), 1), %z);
  echo ("Heading to corner D Y");
$player.setnextpos(%x SPC %y SPC %z);
}
 }

   if (%yy == getword(%unit.getworldbox(), 4))
 {
   if(VectorDist(%clickPos, %unitCornerB) < VectorDist(%clickPos, %unitCornerC))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 3) + 1, getword(%unit.getworldbox(), 4), %z);
  echo ("Heading to corner B Y");
$player.setnextpos(%x SPC %y SPC %z);
}
   if(VectorDist(%clickPos, %unitCornerB) > VectorDist(%clickPos, %unitCornerC))
{
  commandToServer('IssueMove', getword(%unit.getworldbox(), 0) - 1, getword(%unit.getworldbox(), 4), %z);
  echo ("Heading to corner C Y");
$player.setnextpos(%x SPC %y SPC %z);
}
 }


echo ("PathManager");

}
else
{
echo ("Normal");
commandToServer('IssueMove', %x, %y, %z);
}
}

Thats it, there are so many ways to implement pathfinding, I have created a few different types over the last week, this is the one I found to work best for me.

In it's current stage this is kinda like a hack, in an attempt to make it more permanent I have moved parts to the engine already, the next move should probrally be to move code from input handler to guiRTSCtrl.
#2
02/17/2006 (8:03 pm)
I'll try it out as soon as I get an inch of free time!
#3
02/18/2006 (7:28 am)
One thing you should know is that it works with only one unit, the unit is defined $player at startup. However it would be easy to apply this to all units in a selection, or to AI controlled players. Ill update this fairly quik with those addidtions.
#4
02/18/2006 (9:24 am)
Well to save energy it would probably be best to just have everybody follow the player that did the initial pathfinding.
#5
02/18/2006 (9:55 am)
You're right that is why I was only using one unit in my code, because I have a form of flocking in my* game.

*edit
#6
03/04/2006 (9:14 am)
Errors ahoy all about mNextPos; To fix them go to RTSunit.h

and put

Point3F                mNextPos;

under

Point3F                mAimLocation;      // Point to look at

you must of overlooked that
#7
03/04/2006 (9:32 am)
More problems the end of

function GuiRTSTSCtrl::onRightMouseDownTerrain(%this, %x, %y, %z){

needs one more }

and once I do that the raycheck always returns 0 did you add collision? because I don't have it right now.
#8
03/08/2006 (8:12 am)
Sorry for the long delay, please email me if you don't get a fast response.
My CPU fried and I'm not able to really look at my code right now, I'll get back to you in a couple days.
#9
03/08/2006 (5:16 pm)
Ok thanks
#10
04/05/2006 (10:00 am)
Has anyone been able to get this pathfinding to work? My units are still walking they're default straight line path.

I'm getting the following information in the console window

"
starter.RTS/client/scripts/inputHandler.cs (148): Unable to find object: '' attempting to call function 'getTransform'

Current Unit's Postion 1368
Clicked Position 15 25 249
RayCast Information 0
Normal
"

The line it's having trouble with is "%unitPos = $player.gettransform();". It can't seem to find $player.

Anyone have any ideas?

-Nick
#11
04/18/2006 (4:40 am)
I have the same problem i have corrected the errors stated by Master Treb and i have nothing working...