Game Development Community

Walkabout Navigation Toolkit help thread

by Daniel Buckmaster · in General Add-On Discussion · 11/08/2012 (3:57 pm) · 223 replies

This is the official Walkabout help thread. If you need a hand installing or using Walkabout, please ask away! But before you do, I'd really appreciate if you could follow a couple of guidelines:

  • Re-read the installation instructions! Seriously, I know how often I run into some ridiculous issue, then realise I'd missed a step or misread a line. It helps me and you!
  • If you're reporting an error in compilation, please tell me the errors you get and what files they're in, if you can!
  • If you're reporting a bug, please provide steps I can take to reproduce it!
Feature requests, comments and any other feedback are welcome here as well! If you have questions you'd prefer to keep between you and me, feel free to contact me directly.

Known issues

  • Release 1 Patch 3: no navmeshes appear in the browser list in the nav editor. Fix
  • Engine version > 3.5.1: project generator scripts are incorrect. Fix. Also, be sure not to include the Recast module in your project configuration!
  • On platforms that are not Windows, you may need to replace FLT_MAX with F32_MAX. In fact this is probably a good idea in Windows as well.

About the author

Studying mechatronic engineering and computer science at the University of Sydney. Game development is probably my most time-consuming hobby!

#161
03/07/2014 (4:45 am)
Erk, sorry about that. findCover should already be in public. It's getCover that needs to move, as the compiler warning said. I mistyped!
#162
03/07/2014 (5:42 am)
ok i'll try that, thanks again dan...
#163
03/07/2014 (6:38 am)
Ok dan got it to compile with no errors, thank you very much, I moved it to here;

void repath();
   /// Get cover we are moving to.
   CoverPoint *getCover() { return mCoverData.cover; }
   
   bool findCover(const Point3F &from, F32 radius);

hope thats a good spot, now for scripting.....
#164
03/07/2014 (11:21 am)
Dan, you said if I knew the handle I could access the size of the coverpoint in question... I have each coverpoint named A thru N, how can I obtain the name of the coverpoint?? I tried getName to no aval!! also heres my idea for setting the pose accordingly...

//Get Player position...
      %position = LocalClientConnection.getControlObject().getPosition();
      // Try to take cover....
      %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);
      //Get CoverPoint Name
      %coverName = %this.getName();
      %cover = %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);  
      if(%cover != -1) 
      {  
         %pose = %coverName.size;  
         echo("Cover:" @%cover);
         echo("CoverName:" @%coverName);
         echo("Pose:" @%pose);
      }

if i can get the size to workout then i plan on using this to set the poses...

function AIPlayer::SetCoverPose(%obj)
{
   // If cover pose is Stand, we set AIPlayer pose to 0
   // If cover pose is Crouch, we set AIPlayer pose to 1
   // If cover pose is Prone, we set AIPlayer pose to 2
   // Default cover pose is stand.....
   switch$(%obj.action)
   {
      case "Stand":
         %obj.setAIPose(0);
      case "Crouch":
         %obj.setAIPose(1);
      case "Prone":
         %obj.setAIPose(2);
      default:
         %obj.setAIPose(0);
   }
}

If this is all wrong or a bad idea plz by all means tell me....
#165
03/12/2014 (7:22 pm)
OK need help bad, tried everything to get %cover.size to no aval???
#166
03/13/2014 (5:27 am)
Sorry Donnie, I meant to get back to you! There's some stuff going on in that fragment you posted. I've added some of my own comments.

// Ok this is fine. Get the player's current position.
      %position = LocalClientConnection.getControlObject().getPosition();
      // This will make the AIPlayer immediately run to cover, and return
      // the cover point that was found.
      %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);
      // Okay, now this line, which seems like you want to get the name
      // of the cover point, actually gets the AIPlayer's name, because
      // %this is the AIPlayer this method is called on. Same as in the line
      // above.
      %coverName = %this.getName();
      // Okay, this is closer - this time you capture the cover object
      // that was found by the search. But this call makes the first call
      // redundant!
      %cover = %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);  
      // Yep, good, make sure that cover was actually found.
      if(%cover != -1) 
      {
         // Okay, so currently coverName is set to the name of the AIPlayer.
         // You probably want to use %cover here, since that refers to the
         // actual cover object.
         %pose = %coverName.size;  
         echo("Cover:" @%cover);
         echo("CoverName:" @%coverName);
         echo("Pose:" @%pose);
      }

#167
03/13/2014 (7:16 am)
Hey dan, thank you for explaining, %cover is the only thing that echos anything..... this is the console!

Cover:1
Pose:
Cover Size:

this is what i have in code??

%cover = %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);  
      if(%cover != -1) 
      {  
         %pose = %cover.size;  
         echo("Cover:" @%cover);
         echo("Pose:" @%pose);
         echo("Cover Size:" @%cover.size);
      }
#168
03/13/2014 (8:34 am)
Hey, just for giggles, try this:
%cover = %this.findCover(%position, $AISK_WALKABOUT_COVER_RADIUS);    
      if(%cover != -1)   
      {
         // %cover should be an object, right?
         if(!isObject(%cover))
             echo(" !! %cover is not an object");
         %pose = %cover.size;    
         echo("Cover:" @%cover);  
         echo("Pose:" @%pose);  
         echo("Cover Size:" @%cover.size);  
      }
That way if findCover() isn't returning an object it will be readily apparent. It looks like it's returning "true" instead of the object to hide behind.
#169
03/13/2014 (8:49 am)
Hey Richard, your right, findcover is not returning an object??? ok now im confused.... lol
#170
03/13/2014 (1:49 pm)
i tried everything, getting coverpoint to set ai pose will be great when we get it working. I cant say enough about this kit dan, awsome as ever and thank you so much for your hard work on it....
#171
03/13/2014 (3:13 pm)
Donnie, can you try changing $AISK_WALK... to 0? As in try to find cover with 0 radius. What does it return? I suspect the changes to findCover aren't working, and it's returning 1 (true) instead of an object.
#172
03/13/2014 (8:06 pm)
Hey Dan, I set the global to 0 and it still isnt returning an object. Cover still returns as a 1 and the bot didnt seek cover..... Set my global back up and he seeks cover and still not returning the coverpoint as an object...
#173
03/14/2014 (9:42 pm)
Ok, so this is a bit long and involved: looked a bit into http://www.stevefsp.org/projects/rcndoc/prod/group__crowd.html, but that didn't seem terribly suited to the engine without some hefty reworks.

Experimented with

DefineConsoleFunction(WalkaboutIgnore, void, (S32 objid, bool _ignore), (0, true),
   "@brief Flag this object as not generating a navmesh result.")
{
   SceneObject *obj;
   if(!Sim::findObject(objid, obj))
      return;

      obj->mPathfindingIgnore = _ignore;
}
+
static void buildCallback(SceneObject* object,void *key)
{
   SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
   if (!object->mPathfindingIgnore)
	   object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
}
+
getContainer()->findObjects(box, StaticObjectType | DynamicShapeObjectType, buildCallback, &info);

With a trigger once every few cycles per-bot using https://github.com/Azaezel/Torque3D/commits/AI_manager in a 64MB queue (meaning it waits 64 seconds * botcount between bot decisions to keep from flooding the place.). UNfortunately, due to the frequency of the redraw, even at 1 unit density, it ended up leaking like a sieve.
#174
03/14/2014 (9:43 pm)
At present, ended up settling for retooling our old racing ai code like so:
bool AIPlayer::getAIMove(Move *movePtr)
{...
         movePtr->x = newMove.x;
         movePtr->y = newMove.y;
		 
		 mNextDodge--;
		 if ((mDodge)&&(mNextDodge <=0))
		 {
			 mNextDodge = mDodgeCycle;
			 Point3F avoidanceOffset = avoidCollisions();
			 if (!avoidanceOffset.isZero())
			 {
				 mMoveState = ModeMove;
				 movePtr->x += avoidanceOffset.x * mDodgeCycle;
				 movePtr->y += avoidanceOffset.y * mDodgeCycle;
			 }
		 }
+
Point3F AIPlayer::avoidCollisions()
{
	disableCollision();
	F32 thetaScale = TickSec * mDodgeCycle;		//think mDodgeCycle ticks ahead

	Point3F start = getBoxCenter();
	Point3F feeler,temp;

	F32 velocity = getVelocity().len() * thetaScale;
	if (velocity <= mMoveTolerance) velocity = mMoveTolerance; //avoid divnull
	Point3F ForwardRot,LeftRot,RightRot;
	getTransform().getColumn(0,&LeftRot);
	getTransform().getColumn(1,&ForwardRot);
	RightRot = -LeftRot;

    Point3F frontFeeler = (ForwardRot * velocity) + start;
	frontFeeler.z = start.z;

	Point3F leftFeeler = (LeftRot * velocity) + start;
    leftFeeler.z = start.z;

	Point3F rightFeeler = (RightRot * velocity) + start;
    rightFeeler.z = start.z;

	// Find closest intersection with objects
	bool intersectionFound = false;
	RayInfo closestRay;
	FeelerState closestFeelerState = Open;

	RayInfo rayInfo_1;
	closestRay.distance = rayInfo_1.distance = (frontFeeler-start).len();

	if (gServerContainer.castRay( start, frontFeeler, AIPLAYER_LOSMASK , &rayInfo_1 ))
	{
		if (rayInfo_1.distance > closestRay.distance)
		{
			closestRay = rayInfo_1;
			intersectionFound = true;
			closestFeelerState = FrontFeeler;
		}
	}
	RayInfo rayInfo_2;
	rayInfo_2.distance = (leftFeeler-start).len();
	if (gServerContainer.castRay( start, leftFeeler, AIPLAYER_LOSMASK , &rayInfo_2 ))
	{
		intersectionFound = true;
		if (rayInfo_2.distance < closestRay.distance)
		{
			closestRay = rayInfo_2;
			closestFeelerState = LeftFeeler;
		}		
	}
	RayInfo rayInfo_3;
	rayInfo_3.distance = (rightFeeler-start).len();
	if (gServerContainer.castRay( start, rightFeeler, AIPLAYER_LOSMASK , &rayInfo_3 ))
	{
		intersectionFound = true;
		if (rayInfo_3.distance < closestRay.distance)
		{
			closestRay = rayInfo_3;
			closestFeelerState = RightFeeler;
		}		
	}

	//determine rotated offset to pad our movement by
	Point3F offset;
	if (!intersectionFound)
	{
		offset = Point3F(0.0,0.0,0.0);
	}
	else
	{
		mMoveState = ModeMove;
		switch (closestFeelerState){
			case FrontFeeler:
				offset = Point3F(0.0f, closestRay.distance,0.0);
				break;
			case LeftFeeler:
				if (closestRay.distance >0)
				{
					offset = Point3F(-closestRay.distance, rayInfo_1.distance/2,0.0);
					break;
				}
			case RightFeeler:
				if (closestRay.distance >0)
				{
					offset = Point3F(closestRay.distance, rayInfo_1.distance/2,0.0);
					break;
				}
		}
	}
	enableCollision();
	return offset;
}
+
function AIData::onCollision(%this, %obj, %col, %vec, %vecLen)
{
	Parent::onCollision(%this, %obj, %col, %vec, %vecLen);
	
	%pos = %obj.getPosition(); 
	%offset = vectorScale(%vec, %vecLen * -1);
	%pos = vectorAdd(%pos,%offset);
	%obj.setMoveDestination(%pos);
}

Now I'm thinking a more proper methodology would be more along the lines of either treating players as tempobstacles (http://digestingduck.blogspot.com/2011/04/temporary-obstacle-progress.html), or an additional variant on walkaboutupateall that takes a vector of objects and flags a series as dirty to be redrawn in a second step). Anyone look into other resolutions for dynamic avoidance?
#175
03/14/2014 (9:58 pm)
Yes, using temp obstacles is definitely the ideal solution. I do wonder if there's value in an alternative system that allows regular dynamic objects to be reflected in the navmesh build. This might be nice in the case of, for example, a vehicle that parks and can be jumped on top of.
#176
03/15/2014 (1:28 am)
As far as it goes, there is a:

dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)

and

dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref)
pair. Coming up blank at present on how to tie that back into the engine, however.
#177
03/16/2014 (4:04 am)
Donnie: that suggests to me that the changes I had you make to findCover aren't taking effect. Can you use Visual Studio to set a breakpoint in the findCover console function and step through it? Would be helpful to know what goes on.
#178
03/16/2014 (8:01 am)
Will do Dan and report back....
#179
03/16/2014 (10:47 am)
Hey Dan, I noticed when I step thru the code in VS it shows a bunch of missing rtti file??? dont know if that matters or not and it is enabled but also in the do not look for f:\dd\vctools\crt_bld\self_x86\crt\prebuild\eh\rtti.cpp...... My knowledge is very limited in c++ but im trying...lol I set a break on findcover and step thru the program but I dont see any variables in the output window, it just goes thru the program as I step??? what should I be looking for exactly?? Sorry for being a pain and thank you for your help and patience....
#180
03/16/2014 (11:11 am)
Dan could it be this??

static void findCoverCallback(SceneObject *obj, void *key)
{
   CoverPoint *p = dynamic_cast<CoverPoint*>(obj);

dynamic_cast does depend on rtti file and I dont have it...