Game Development Community

Adding AI - in theory (Newbie)

by Infinitum3D · in Torque Game Engine · 05/27/2008 (10:05 am) · 20 replies

I've done the AI Guard and AI Patrol tuts. In theory, however, could I just copy the aiPlayer.cs and rename it aiOrc.cs and aiElf.cs and aiHuman.cs and change the dts with each, or can the dts be changed at run time.

In theory, what is the best/easiest/most efficient way of going about this?

I know this is a very vague post, but I'm just looking for ideas.

Thanks

Tony

#1
05/27/2008 (12:38 pm)
Yes, it is Vague... By changing a name in a file, you need to search through each script that calls that file or loads that shape, then change it to that name too.
#2
05/27/2008 (2:25 pm)
Right. I knew that, I just forgot to mention it.

so if I want to spawn an elf, and I write a function like

function SpawnAiElf()
{
%aiElf = aiElf::spawn();
return %aiElf;
}

I should be able to call this function elsewhere in script to span an elf, if I changed the aiPlayer.cs to aiElf.cs and changed all references from aiPlayer to aiElf, right?

Or am I completely off?

Tony
#3
05/27/2008 (3:04 pm)
You might want to check this thread:
http://garagegames.com/mg/forums/result.thread.php?qt=66321
#4
05/27/2008 (3:08 pm)
You would do better to read the scripts and locate how the data definition is presented.

basically, you would define [N] configurations.
each of these would fill out a datablock defining each model configuration.

if you do this correctly, you will find an opportunity to share code between them.
if you just copy paste the files you will find alot of redundant code that needs to be refactored to present a cleaner solution.

so, find the spawn function, and drill down to the actual data definition used by the spawn call.
then, simply present more data definitions

the aiPlayer.cs will contain some code that can be used for all of your data definitions.
#5
05/27/2008 (3:09 pm)
Yea, what Hans said.
#6
05/27/2008 (5:34 pm)
Thank you all for your help. I guess I'm just trying to figure out the absolute basics of How and Why.

I know each character\NPC type needs a datablock. Should I put multiple datablocks in a single .cs file or would it be better to have separate .cs files for each type? What are the pros\cons of each way? Or to put it a different way, How do real programmers do it and why?

Should all my spawn functions be on the same .cs or in different places? Again, how and why?

I've tried to find .plan files that tell how others do it, but no one really explains why they do it a certain way.

I'd like to hear as many opinions as possible. This is a real learning experience for me.

Thanks everyone!

Tony
#7
05/27/2008 (5:35 pm)
For each new ai, add
datablock PlayerData([i]aiName[/i] : PlayerBody)
{
   shapeFile = "~/data/shapes/[i]file.dts[/i]";
   shootingDelay = 2000;
};

function [i]aiName[/i]::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 [i]aiName[/i]::onEndOfPath(%this,%obj,%path)
{
   %obj.nextTask();
}

function [i]aiName[/i]::onEndSequence(%this,%obj,%slot)
{
   echo("Sequence Done!");
   %obj.stopThread(%slot);
   %obj.nextTask();
}
#8
05/28/2008 (7:50 am)
Thanks Dan! What Dan has given is a stripped down version of aiPlayer.cs

I'm going to try to break it down section by section and line by line. I'll need help.

Deconstructing aiPlayer.cs

datablock PlayerData(aiName : PlayerBody)

//-- this datablock will contain all the information needed --//
//--to create an AI (NPC) character --//
//--aiName will become the name of the character type --//
//--in our case as follows --//
//--datablock PlayerData(ElfGirl : PlayerBody) --//

WHY DOES IT SAY PLAYERDATA AND PLAYERBODY IF IT DOESN'T HAVE ANYTHING TO DO WITH THE PLAYER? (sorry about the caps. I'm using my cellphone to edit this and I can't find the brackets on my keypad)

{
shapeFile = "~/data/shapes/file.dts";

//--change file.dts to the appropriate .dts file --//
//--in our case as follows --//
//--shapeFile ="~/data/shapes/tge_elf/elf.dts --//

shootingDelay = 2000;
//--NOT SURE WHAT SHOOTINGDELAY MEANS? --//
};

function aiName::onReachDestination(%this,%obj)

//--this function tells the AI character to move --//
//--along a path from node to node --//
//--again, change aiName --//
//--in our case as follows --//
//--function ElfGirl::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 checks to see if the AI character has reached --//
//--the end of the path, if it is the end of the path, --//
//--then it calls (goes to) the onEndOfPath function --//

%this.onEndOfPath(%obj,%obj.path);
else

//--if it is not the end of the path (hence the 'else') --//
//--then the AI character will move to --//
//--the next node on the path --//

%obj.moveToNextNode();
}
}

Why do some end with }; (bracket and semi-colon) while others don't? Typo?

function aiName::onEndOfPath(%this,%obj,%path)

//--again, aiName gets changed to the --//
//--appropriate name for this character --//
//--in our case as follows --//
//--function ElfGirl::onEndOfPath(%this,%obj,%path) --//

{
%obj.nextTask();

//--when this function is called, the --//
//--program moves on to the next task --//
}

function aiName::onEndSequence(%this,%obj,%slot)

//--and once again, change aiName --//
//--function ElfGirl::onEndSequence(%this,%obj,%slot) --//

{
echo("Sequence Done!");

//--echo should appear in the console --//
//--this tells you that the function has been run --//

%obj.stopThread(%slot);
//--this seems self explanitory --//
//--when the sequence is finished, the thread stops --//

%obj.nextTask();
//--when the thread stops, --//
//--the program goes on to the next task --//
}



//-----------------



OK, now I need some programming help (or more correctly 'scripting' help) with some of
the syntax.

I know that % sets a local variable (and $ is a global variable), however, what does
(%this, %obj)
mean? Does this refer to this specific function? And does
obj tell it that the function affects an object?

And what does the : and :: (colon/double colon) mean?

How do you know when to use %this,%obj? and why does it sometimes have more, like
%path,%node,%slot? Obviously path, node, and slot tell it to reference a path a node
and a slot, but I don't know what a slot is. Does it just take experience? I mean, I
guess I don't understand the concepts (theory).

As you can tell, I'm a true scripting newbie. I can cut/copy/paste/steal code with the best,
but now I want to actually understand what I'm doing :)

Thanks for any help explaining this!!!

~Tony
#9
05/28/2008 (10:04 am)
Quote:
Why do some end with }; (bracket and semi-colon) while others don't? Typo?

functions do not end with a semicolon, but object declarations do. eg...

datablack PlayerData( .... )
{
   // assign fields
};

That is creating an object, so it ends with a semicolon. The "datablock" keyword is what you use instead of "new" to create datablock objects.

Quote:
what does (%this, %obj) mean? Does this refer to this specific function? And does
obj tell it that the function affects an object?

%this is always passed to "member" functions. That is, functions that are members of a class/object namespace. eg...

function [b]aiName[/b]::onEndOfPath(%this,%obj,%path)
aiName is the namespace, and %this is the object id of the "aiName" object onEndOfPath was called on.

More specifically %this is a PlayerDatablock object and %obj is the AIPlayer object. Callbacks are often called on the object's datablock and the object is passed as an argument because this allows two instances of the same object class to behave different by only having different datablocks.

FYI, you can use {code} ..... {/code} to display code in an easier to read format. Replace curly with straight braces.
#10
05/28/2008 (11:24 am)
Thank you James.

I still don't understand why you need both a PlayerDatablock object (%this) AND an aiPlayerData object (%obj) in the same function. If its for the Player, why have the aiPlayer (%obj) and if its for the aiPlayer, why have the Player (%this)?

for example, in the following

function aiName::onReachDestination(%this,%obj)

that obviously is for aiPlayer (hence aiName), so why is the PlayerData object (%this) needed?

Wait... a 'Datablock object' is different than an 'object'. This has something to do with callbacks in a namespace, right?

Tony
#11
05/28/2008 (11:38 am)
To quote the new book I just got in the mail..
Quote: The engine does not supply a new datablock class for AIPlayer. Instead, we use the PlayerData datablock to initialize instances of AIPlayer.
Multiplayer Gaming and Engine Coding for the Torque Game Engine - Edward F. Maurina III

The code in your example IS being called on the PlayerDatablock object, because the name of the PlayerDatablock that is assigned to a specific AIPlayer IS what defines how it behaves. aiName is a PlayerDatablock.
#12
05/28/2008 (1:13 pm)
OK, so even though it doesn't affect the player, it BEHAVES like a player, and therefore uses the playerDatablock?

I guess I can follow that logic...

Going back to the post by Dan, it looks like we still need to add a Spawn function

I.e.

function aiName::spawn(%name,%spawnpoint)
{
%player = new Player()
etc.etc.etc

Is that right? What details make up the etc. etc. etc.? and why?

I guess all that is really needed is
%player = (%Name,%spawnPoint)
return %player;

Is that right?

Thanks!

Tony
#13
05/29/2008 (7:12 am)
Thanks for everyone's help! I'm trying it out. I followed Dan's example and created a datablock and the three functions.

To spawn an elf, I tried this function;

function aiElf::spawn(%Name,%spawnPoint)
{
 %player = new AiPlayer()
      dataBlock = aiElf;

return %player;
}


and I call it this way;

SpawnaiElf("Bob", "201 -743 108");

I'm getting a console error between datablock = aiElf; and return %player;

Something is wrong with my spawn function... I must be leaving something out, but the same function is used in aiPlayer.cs... Wait. I see a mistake. I don't have {} around datablock = aiElf;

I'll see if it works...


UPDATE: I'm still getting a console error. It compiles, but now it's "unable to find function SpawnaiElf".


Is my function wrong or my call script?

Thanks again,

~Tony
#14
05/29/2008 (7:25 am)
HHHHHAAAAAA!!!!!

IT WORKS!

I changed the name of the function to spawnaiElf and it works. Simple mistake. I think I understand things better.

function SpawnaiElf(%Name,%spawnPoint)
{
 %player = new AiPlayer()
  {
      dataBlock = aiElf;
  };
  %player.setShapeName(%name);
   %player.setTransform(%spawnPoint);
return %player;
}


SpawnaiElf("Bob", "201 -743 108.8 0 0 1 160");


THANK YOU ALL SO MUCH!!!!

Tony
#15
05/29/2008 (7:26 am)
Just a follow-up of my entire SpawnElf.cs

//--SPAWNING ELVES--//

datablock PlayerData(aiElf : PlayerBody)
{
   shapeFile = "~/data/shapes/tge_elf/elf.dts";
   shootingDelay = 2000;
};

function aiElf::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 aiElf::onEndOfPath(%this,%obj,%path)
{
   %obj.nextTask();
}

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

function SpawnaiElf(%Name,%spawnPoint)
{
 %player = new AiPlayer()
  {
      dataBlock = aiElf;
  };
  %player.setShapeName(%name);
   %player.setTransform(%spawnPoint);
return %player;
}

SpawnaiElf("Bob", "201 -743 108.8 0 0 1 160");

I'll make a new .cs for each, and exec them all.

Thanks!

Tony
#16
05/31/2008 (11:55 am)
OK, so I can load and spawn Elves, Orcs, and humans. I'm trying to use my newfound knowledge to modify the RPGDialog resource.

The RPGDialog resource basically hijacks the aiPlayer data as follows;

function 

SpawnNPC(%Name,%Script,%Portrait,%startQuestion,%location)
{
   %player = [b]AIPlayer::spawn[/b](%Name,%location);
   %player.RPGDialogScript = %Script;
   %player.RPGDialogPortrait = %Portrait;
   %player.RPGDialogStartQuestion = %startQuestion;
   %player.RPGDialogBusy = false;
   %player.RPGDialogBusyText = 'Sorry but I\'m busy talking to %1 right now.';
   %player.RPGDialogTalkingTo = 0;
   %player.mountImage(CrossbowImage,0);
   %player.setInventory(CrossbowAmmo,10);

   
   return %player;
}

and it spawns like this
SpawnNPC("NPC", script1, "female1.png", 0, "187 -686 108.8 0 0 1 160");

I tried replacing AIPlayer::spawn with my own aiElf::spawn

function 

SpawnaiElf(%Name,%Script,%Portrait,%startQuestion,%location)
{
   %player = aiElf::spawn(%Name,%location);
   %player.RPGDialogScript = %Script;
   %player.RPGDialogPortrait = %Portrait;
   %player.RPGDialogStartQuestion = %startQuestion;
   %player.RPGDialogBusy = false;
   %player.RPGDialogBusyText = 'Sorry but I\'m busy talking to %1 right 

now.';
   %player.RPGDialogTalkingTo = 0;
   %player.mountImage(CrossbowImage,0);
   %player.setInventory(CrossbowAmmo,10);

   
   return %player;
}
but when I try to spawn
SpawnaiElf("aiElf", script1, "female1.png", 0, "187 -686 108.8 0 0 1 160");
I get a console error
unable to find function aiElf::spawn

isn't that the function I just wrote?

update:

Turns out there is another function in [baiPlayer.cs[/b] that is needed

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

I've copied that and changed all the aiPlayer's to aiElf's
No compilation errors.

I have my datablock, my spawn function, and my spawn call. Everything works as needed.

Thanks again to everyone for their help!!!



Thanks!

Tony
#17
05/31/2008 (12:35 pm)
Just to follow up, here is the complete working code to spawn elves which can use the RPGDialog resource.
(Note, you have to have the RPGDialog resource already added, this just incorporates it.)

//--SPAWNING ELVES--//


//--datablock defines which .dts is being used
datablock PlayerData(aiElf : PlayerBody)
{
   shapeFile = "~/data/shapes/tge_elf/elf.dts";
   shootingDelay = 2000;
};

//--this is a function to have the AI follow a path
function aiElf::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();
   }
}

//--another path function
function aiElf::onEndOfPath(%this,%obj,%path)
{
   %obj.nextTask();
}

//--another path function
function aiElf::onEndSequence(%this,%obj,%slot)
{
   echo("Sequence Done!");
   %obj.stopThread(%slot);
   %obj.nextTask();
}

//--the all important Spawn function
function aiElf::spawn(%name,%location)
{
   // Create the aiElf player object
   %player = new AIPlayer() {
      dataBlock = aiElf;
      path = "";
   };
//MissionCleanup.add(%player);
   %player.setShapeName(%name);
   %player.setTransform(%location);
 return %player;
}

//--this incorporates the RPGDialog function with the AI spawn
function SpawnaiElf(%Name,%Script,%Portrait,%startQuestion,%location)
{

   %player = aiElf::spawn(%Name,%location);
   %player.RPGDialogScript = %Script;
   %player.RPGDialogPortrait = %Portrait;
   %player.RPGDialogStartQuestion = %startQuestion;
   %player.RPGDialogBusy = false;
   %player.RPGDialogBusyText = 'Sorry but I\'m busy talking to %1 right now.';
   %player.RPGDialogTalkingTo = 0;
   %player.mountImage(CrossbowImage,0);
   %player.setInventory(CrossbowAmmo,10);

   
   return %player;
}

//--this is the call that spawns the AI
SpawnaiElf("aiElf", script1, "femalePic1.png", 0, "200 -740 108.8 0 0 1 160");

So in theory, you need a datablock to create the AI, and a spawn function to define the spawn parameters, and you need to call the function. To incorporate the RPGDialog resource, you need a 2nd function that pulls the spawn function and adds the RPGDialog parameters.

I'm sure there is a way to combine the Spawn function and the RPGDialog functions together, but I don't know how to do it. This works for me.

And once again, thanks to all who helped!!!


~Tony
#18
05/31/2008 (1:28 pm)
Just curious have you solved the, spawning in different missions problem?
#19
05/31/2008 (3:28 pm)
Edward,

Sorry, but no. I haven't even tried it yet. Do you mean different AI spawning in different missions, or the Player spawning in different missions?

~Tony
#20
03/23/2009 (3:38 pm)
Wow this has helped me heaps. Thanks tony.