Game Development Community

AI Vehicles not getting onReachDestination callback

by Tim Hutcheson · in Torque Game Engine · 10/07/2005 (12:19 pm) · 14 replies

I have been using AI path following succesfully with my bots but I just created a new manner of starting up certain bots. The idea here is to use the name given in GameConnection::setPlayerName() in a version of the createCar script. The name is held in the global $playerName and the car of that name already has been declared in the mission file. So the connecting player picks up his car from the garage, so to speak, if and only if it exists. Fine. that all works great. But the car is set up this way so that the player can both control the car in the normal way or assign it to follow a pre-existing path. And that works as well.

The problem is the bots AIWheeledVehicle script never receives the onReachDesitnation callback after reaching the first node in the mission path list. I've breakpointed the C++ code to verify that the callback is being made, and it is.

But obviously I haven't initialized something right. If any one can help I would be grateful.

Here's my creatCar method:

function GameConnection::createCar(%this, %spawnPoint)
{
   if (%this.car > 0)  {
      error( "Attempting to create an angus ghost!" );
   }

   %carID = $playerName.getID();
   %car = %carID;
   MissionCleanup.add(%car);

   // Player setup...
   %car.setShapeName(%this.name);    // Use predefined car of clients avatar name
   %car.client = %this;
   %this.setControlObject(%car);
   
   // Update the camera to start with the player
   %this.camera.setTransform(%car.getEyeTransform());

   // Give the client control of the player
   %this.car = %car;
}

#1
10/07/2005 (12:29 pm)
The only thing I can think of is that either the onReachDestination is attached to the wrong datablock.

Actually, thinking about it further, you'd have to create a different object based on wether it's player controlled or AI controlled. So I'm guessing %car is the wrong type?
#2
10/07/2005 (12:37 pm)
Hmmm. The datablocks declared in the mission file are AIWheeledVehicle types and I have verified that I can control them by keyboard and have them follow a path. They just stop at the first node due to missing callback. But I'll follow up on that idea. Wait...

I forgot to assign it to the path in the first place....hehe. I just set the destination...

thanks
#3
10/07/2005 (1:06 pm)
Hehe, glad you figured it out!
#4
10/07/2005 (1:12 pm)
Rats! That didn't fix it but my createCar now looks like this:

function GameConnection::createCar(%this, %spawnPoint)
{
   if (%this.car > 0)  {
      error( "Attempting to create an angus ghost!" );
   }
   %carID = $playerName.getID();
   %car = %carID;
   MissionCleanup.add(%car);

   %car.setShapeName(%this.name);
   %car.client = %this;

   %car.path = "MissionGroup/Paths/Path6";   // <== my mission path...for example
   %car.curentNode = 0;   

   %this.setControlObject(%car);
   
   // Update the camera to start with the player
   %this.camera.setTransform(%car.getEyeTransform());

   // Give the client control of the player
   %this.car = %car;
}
#5
10/07/2005 (1:34 pm)
Arggh! Resistant bug. I've even stepped through the onReachDestination callback into the guts of the execute function and the object that is being called back on is the correct WheeledVehicle (same name, mass, etc.)

Help....
#6
10/07/2005 (1:48 pm)
You need to set. %car.setMoveTolerance("Closeness to node here, i use 10"); <--- This one got me big time.
#7
10/07/2005 (2:03 pm)
Actually I use 1.0 and I'm hoping to get it down to about .25 with improved steering algorithms. I just didn't include that in my code clip above. How are you doing?
#8
10/07/2005 (2:44 pm)
Arrrg! Darned automatic variables. I misspelled currentNode (as curentNode) all over the place. But that didn't fix the problem either.
#9
10/07/2005 (3:24 pm)
Problem fixed! There was a difference between the datablock declaration for each of the garaged bots in the mission file and the namespace declaration on the onReachDesination callback routine itself. Pretty obvious connection between the problem and the solution but because of the bunch of different types of vehicles involved, I just didn't see it.
#10
07/05/2006 (4:37 pm)
Hi, my name is Glennon. I am working on getting the AI to follow a path and also be able to use a task queue for a racing game. I am having this same problem as you described here. Could you please let me know how to get the callback to work? Shouldnt this function automatically be called upon reaching the next path marker?

I am so confused with this...

heres the code... bear with me on this one...

//-----------------------------------------------------------------------------
// Demo Pathed AIWheeledVehicle.
//-----------------------------------------------------------------------------

datablock WheeledVehicleData(DemoRacer : DefaultCar)
{
//maxInv[CrossbowAmmo] = 500000; // Lots of ammo for the demo
//demo = true;
className = "AI_Car";
};

function DemoRacer::onReachDestination(%this,%obj)
{
// Moves to the next node on the path.
// Override for all player. Normally we'd override this for only
// a specific player datablock or class of players.
if (%obj.path !$= "") {
if (%obj.currentNode == %obj.targetNode)
%this.onEndOfPath(%obj,%obj.path);
else
%obj.moveToNextNode();
}
}

function DemoRacer::onEndOfPath(%this,%obj,%path)
{
%obj.nextTask();
}

function DemoRacer::onEndSequence(%this,%obj,%slot)
{
echo("Sequence Done!");
%obj.stopThread(%slot);
%obj.nextTask();
}


//-----------------------------------------------------------------------------
// AIWheeledVehicle static functions
//-----------------------------------------------------------------------------

function AIWheeledVehicle::spawn(%name,%spawnPoint)
{
// Create the demo player object
%player = new AIWheeledVehicle() {
dataBlock = DemoRacer;
path = "";
};
MissionCleanup.add(%player);
%player.setShapeName(%name);
%player.setTransform(%spawnPoint);
return %player;
}

function AIWheeledVehicle::spawnOnPath(%name,%path)
{
// Spawn a player and place him on the first node of the path
if (!isObject(%path))
return;
%node = %path.getObject(0);
//%player = AIWheeledVehicle::spawn(%name,%node.getTransform());
%player = AIWheeledVehicle::spawn(%name, pickAISpawnPoint());
return %player;
}


//-----------------------------------------------------------------------------
// AIWheeledVehicle methods
//-----------------------------------------------------------------------------

function AIWheeledVehicle::followPath(%this,%path,%node)
{
// Start the player following a path
%this.stopThread(0);
if (!isObject(%path)) {
%this.path = "";
return;
}
//If we run out of nodes, go back to the first one
if (%node > %path.getCount() - 1)
%this.targetNode = %this.currentNode;
//If we dont run out of nodes, set the target node to %node
else
%this.targetNode = %node;
//If the path is already set, then move to the targetNode
if (%this.path $= %path)
%this.moveToNode(%this.targetNode);
//If the path is not set, set it to %path, then move to the targetNode
else {
%this.path = %path;
%this.moveToNode(%this.targetNode);
}
}

function AIWheeledVehicle::moveToNextNode(%this)
{
error("Hey! We are moving to the next node!!!");
if (%this.targetNode < 0 || %this.currentNode < %this.targetNode) {
if (%this.currentNode < %this.path.getCount() - 1)
%this.moveToNode(%this.currentNode + 1);
else
%this.moveToNode(0);
}
else
if (%this.currentNode == 0)
%this.moveToNode(%this.path.getCount() - 1);
else
%this.moveToNode(%this.currentNode - 1);
}

function AIWheeledVehicle::moveToNode(%this,%index)
{
error("Moving to the target node: " @ %index);
// Move to the given path node index
%this.currentNode = %index;
%node = %this.path.getObject(%index);
%this.setMoveDestination(%node.getTransform(), %index == %this.targetNode);
%this.onReachDestination(%this);
}


//-----------------------------------------------------------------------------
//Task Queue
//-----------------------------------------------------------------------------

function AIWheeledVehicle::pushTask(%this,%method)
{
if (%this.taskIndex $= "") {
%this.taskIndex = 0;
%this.taskCurrent = -1;
}
%this.task[%this.taskIndex] = %method;
%this.taskIndex++;
if (%this.taskCurrent == -1)
%this.executeTask(%this.taskIndex - 1);
}

function AIWheeledVehicle::clearTasks(%this)
{
%this.taskIndex = 0;
%this.taskCurrent = -1;
}

function AIWheeledVehicle::nextTask(%this)
{
if (%this.taskCurrent != -1)
if (%this.taskCurrent < %this.taskIndex - 1)
%this.executeTask(%this.taskCurrent++);
else
%this.taskCurrent = -1;
}

function AIWheeledVehicle::executeTask(%this,%index)
{
%this.taskCurrent = %index;
eval(%this.getId() @ "." @ %this.task[%index] @ ";");
}



//functions to be called thru the queue

function AIWheeledVehicle::wait(%this,%time)
{
%this.schedule(%time * 1000,"nextTask");
}

function AIWheeledVehicle::done(%this,%time)
{
%this.schedule(0,"delete");
}


//Testing function... this is called from the game.cs
function AIWheeledVehicle::test()
{
%player = AIWheeledVehicle::spawnOnPath("Botter","CarPath");
//%player.mountImage(CrossbowImage,0);
//%player.setInventory(CrossbowAmmo,1000);

%player.pushTask("followPath(\"CarPath\", 0)");
//%player.pushTask("aimAt(\"MissionGroup/Room6/target\")");
%player.pushTask("wait(100)");
%player.pushTask("moveToNextNode()");
//%player.pushTask("fire(true)");
%player.pushTask("wait(10)");
//%player.pushTask("fire(false)");
//%player.pushTask("playThread(0,\"celwave\")");
%player.pushTask("done()");
}




//This is a little function I rigged up to automatically choose a AI spawnpoint



function pickAISpawnPoint()
{
%groupName = "MissionGroup/AIDropPoints/Follower";
%group = nameToID(%groupName);

if (%group != -1) {
%count = %group.getCount();
if (%count != 0) {
%index = getRandom(%count-1);
%spawn = %group.getObject(%index);
return %spawn.getTransform();
}
else
error("No spawn points found in " @ %groupName);
}
else
error("Missing spawn points group " @ %groupName);

// Could be no spawn points, in which case we'll stick the
// player at the center of the world.
return "0 0 300 1 0 0 0";
}

--------------------------------------------------------------------------------------------------------------------------------

OK thats the end of the AI code. When I run the game, the car just executes the followPath and never moves from that task. The next wait task is ever executed. The bot just keeps circling the first node in the path.
Here is the mission file code:

new Path(CarPath) {
isLooping = "1";

new Marker(Point1) {
position = "-451.367 425.081 116.382";
rotation = "1 0 0 0";
scale = "1 1 1";
seqNum = "1";
type = "Normal";
msToNext = "1000";
smoothingType = "Linear";
onGroup = "Default Value";
active = "0";
speed = "40";
wait = 5;
};
...
new Marker(Point19) {
position = "-248.08 727.902 117.217";
rotation = "1 0 0 0";
scale = "1 1 1";
seqNum = "19";
type = "Normal";
msToNext = "1000";
smoothingType = "Linear";
active = "0";
speed = "40";
wait = 8;
};
};


------------------------------------------------------------------------------------------------------

This is only a portion of the mission file. Please help me out! Im so lost on this. >.<

Thank you for your time and energy!
Glennon
#11
07/05/2006 (5:32 pm)
I dont think anyone is gonna study the code right off.
should start with a description next time.

lets cover the basics:
Quote:You need to set. %car.setMoveTolerance("Closeness to node here, i use 10"); <--- This one got me big time.
are you Sure this is not the problem?
do you know how to check the current tolerance set for this ai?
if not you need to know how to dump the object properties via the console can you do that?

this is probably a move tolerance problem.

and finally are you sure your code is compiling proper?
you delete the binary output and force it to recompile? check the load via the logfile?
#12
07/05/2006 (7:21 pm)
Glennon, you have set the classname to AI_Car in your datablock; so you need an onReachDestination function for that classname.

So change


function DemoRacer::onReachDestination(%this,%obj)
{
....

to


function AI_Car::onReachDestination(%this,%obj)
{
...

and maybe it will work.

Edit: Btw, this is a useful feature because it means that you can have a bunch of DemoRacers which each have their own distinct callback functions and behaviors simply by specifying unique classnames in each datablock and adding corresponding callbacks for each.

At least that's how I remember it.
#13
07/07/2006 (10:36 am)
I got it! Thanks for the help guys, it was the className and the moveTolerance! =P It always ends up being some little thing like that with me. Now I can move on and do greater things. ^_^

Thanks again!
#14
03/05/2007 (5:47 am)
Hi guys, Am new to this engine, How do I add an AI car & as well as a player controlled car. Thanks in advance.