Game Development Community

dev|Pro Game Development Curriculum

Bot spawner object

by Jared Barger · 10/14/2004 (12:59 pm) · 11 comments

A short forward

I'm still very new to Torque and I'm just getting the hang of how a lot of it "flows". I already know of several points in

the following code that could, and should, be updated. That's one of the primary reasons I wanted to submit this in the first

place. It seems everyday I come across something new that could make my work better and easier to accomplish so I was hoping

for feedback on what could be improved and at the same time share something that could be useful to others.

One thing I have found a touch frustrating when trying to tackle all this mountain of knowledge about Torque is that a large

percentage of the tutorials provided are pretty much "drop in" tutorials. Cut and paste and it works. This is great if you're

just looking to add something to your game without having to take the time to do it yourself. It's not always the best way to

go if you want to learn how it works, and therefore expand upon it, though. To that ends I've tried to explain this and build

it up in pieces. That being said it will, no doubt, be rather long by the time I'm finished so I will be splitting it up into

sections as follows.

1. Building the basic spawner object
2. Randomize the spawner

Building the basic spawner object

The idea here is to get a physical object into the editor that we can drop into a level and use to spawn our bots.

GenericSpawner.cs
// GenericSpawner object and bot

datablock PlayerData(GenericBotShape: PlayerShape){
 shapeFile = "~/data/shapes/generic.dts";	// Whatever you want here
 category = "bot";
 aiPlayer = true;
};//GenericBotShape

datablock StaticShapeData(GenericSpawner){
 shapeFile = "~/data/shapes/markers/octahedron.dts";
 category = "spawner";
 
 botDataBlock = GenericBotShape;		// Point to what you want to spawn
};//GenericSpawner

function GenericSpawner::OnAdd(%this, %obj){
 AddSpawner(%this, %obj);
}//GenericSpawner::OnAdd

Alright, so I said it was going to be basic...
All this does is create a bot derived from PlayerShape (imo why not derive any mobile from PlayerShape) and create an object

that inserts that into your world.

Next we need to setup some generic support functions for our spawners.

SpawnFuncs.cs
// Spawner support functionality

// Execute our spawner objects here
exec("./GenericSpawner.cs");

$SpawnList[0] = 0; // To hold onto all our spawners. Sub 0 will equal the count.

// Add Spawner ----------------------------------------------
function AddSpawner(%this, %obj){
 $SpawnList[0]++;                      // Inc spawn count
 $SpawnList[$SpawnList[0]] = %obj;     // Point to spawner
 

 %obj.setHidden(true);                 // Hide the spawner for now
}//AddSpawner

// View Spawners --------------------------------------------
function ViewSpawners(%bool){		// Hide/show all the spawner objects
 if(%bool) Echo("Making all spawners visible");
 else Echo("Hiding all spawners");
  
 for(%i = 1; %i <= $SpawnList[0]; %i++){
  $SpawnList[%i].setHidden(!%bool);
 }//for
}//ViewSpawners

// Spawn ----------------------------------------------------
function Spawn(%spawner){
  // Get the datablock
  %dblock = %spawner.getDataBlock().botDataBlock;

  // Build the bot
  %bot = new AIPlayer(){
   datablock = %dblock;
   parent = %spawner;		// Tie it to its parent
  };//new AIPlayer
  

  // Add it to be deleted
  MissionCleanup.Add(%bot);
  
  // Put it where the spawner is
  %bot.setTransform(%spawner.getTransform());
  

  // Tie spanwer to the child
  %spawner.children[0]++;
  %spawner.children[%spawner.children[0]] = %bot;
  
  Echo("Spawned: " @ %bot @ " at " @ %bot.getPosition() @ " from " @ %spawner);
}//Spawn

// Spawn all -----------------------------------------------
function SpawnAll(){
 Echo("*** Performing global spawn ***");
 for(%i = 1; %i <= $SpawnList[0]; %i++){
  Spawn($SpawnList[%i]);
 }//for
 Echo("*** Global spawn of " @ %i @ " bots complete ***");
}//SpawnAll

Now all we need to do is execute SpawnFuncs.cs during the server startup. This will be ~/server/game.cs, ~/server/scripts/

game.cs, or (if you're using a setup from Ken's book, which if you don't have it yet then you should!) ~/server/server.cs depending on which "base" app

you're building off of. The parent and children variables are there because I wasn't sure if I would want to add respawning

for any reason. They are, for now, excess baggage though and could be removed if you didn't want to do any respawning. If you

do you would want to make sure to add some sort of KillSpawn() function at the beginning of Spawn();

Randomize the spawner

At this point the spawner pretty much sucks.It does get a bot into the world...but that's pretty much it. So lets spice

things up with a lil randomization.

First we're going to add some variables to our spawner object (GenericSpawner) in GenericSpawner.cs
datablock StaticShapeData(GenericSpawner){
 shapeFile = "~/data/shapes/markers/octahedron.dts";
 category = "spawner";

 spawnMin = 1;			// Min to spawn
 spawnMax = 10;			// Max to spawn
 
 botDataBlock[0] = 2;
 botDataBlock[1] = GenericBotShape;
 botDataBlock[2] = GenericBotShape2;
};//GenericSpawner

Ok, we've set the groundwork to have the spawner kick out a random number of bots and for it to potentially use more than one

datablock. Now let's update the bot datablock(s) in the same file.

datablock PlayerData(GenericBotShape: PlayerShape){
 shapeFile = "~/data/shapes/generic.dts";	// Whatever you want here
 category = "bot";
 aiPlayer = true;

 gender = "male";	
 defaultName = "Error";
 randomName = true;
};//GenericBotShape

datablock PlayerData(GenericBotShape2: GenericBotShape){
 shapeFile = "~/data/shapes/generic_female.dts";
 gender = "female";
};//GenericBotShape2

The variable names are pretty self explanatory imo so moving right along to SpawnFuncs.cs. First we need to make some changes

to the spawn function.

function Spawn(%spawner){
 // Figure out how many we want to spawn
 %count = GetRandom(%spawner.getDataBlock().spawnMin, %spawner.getDataBlock().spawnMax);
  
 if(%count == 0) return;

 for(%i = 1; %i <= %count; %i++){
  // Pick a datablock to use for the bot
  %block = GetRandom(1, %spawner.getDataBlock().botDataBlock[0]);
  %dblock = %spawner.getDataBlock().botDataBlock[%block];

  %bot = new AIPlayer(){
   datablock = %dblock;
   parent = %spawner;
   name = %dblock.defaultName;
  };//new AIPlayer
  
  // Get a random name if necessary
  if(%dblock.randomName == true){
   %tempName = GetFullNPCName(%dblock.gender);
   %bot.SetShapeName(%tempName);
  }//if

  // Add it to be deleted
  MissionCleanup.Add(%bot);
  
  // Set transform
  %bot.setTransform(NudgeSpawn(%spawner.getTransform()));	// Nudgespawn will keep them from standing
								// on top of each other (for the most part)
   }//for
}//Spawn

ALrighty. Now we need to add that nudgespawn function. I haven't been able to get this perfect but it's way better than

nothing. If anyone knows of a way to call this recursively until each bot is on the ground please pop that info up here.

Once again in SpawnFuncs.cs
// Nudge Spawn -----------------------------------------------------------------
function NudgeSpawn(%origTransform){
 // X & Y are to move the position
 %wordx = Nudge(GetWord(%origTransform, 0));
 %wordy = Nudge(GetWord(%origTransform, 1));
 
 %origTransform = SetWord(%origTransform, 0, %wordx);
 %origTransform = SetWord(%origTransform, 1, %wordy);
 
 return %origTransform;
}//NudgeSpawn

function Nudge(){
 %high = 10;	// Min/Max value we can move the spawn in any direction
 %low = -10;
 %val = GetRandom(%low, %high);
 return %val;
}//Nudge

These two little fuctions here have scooted our spawns around a bit. Assuming we aren't spawning a lot from one spawn point

then they're all doing alright and standing on the ground now. The other function we called was GetFullNPCName() so we'll

look at that next.

IMO load times are a trivial matter unless you're talking about a LOT of time (into the minutes) as most gamers are much more

concerned with how the game performs while they're actually using it rather than how long it takes to load a mission/scene. I

say that because this quick and dirty name generator has a large amount of string compares assuming you go with exactly how I

have coded it. You could change some datablock values to be integers rather than strings and that would speed things up some,

but make the code much less readable. It's your call. Also keep in mind that gender is only one of many naming options that

would be present in a full application. I used it here because it's the most obvious, but in reality a more verbose name gen

would have a lot more options (and thus the string compares) than this one.

function GetFullNPCName(%gender){
 %firstName = GetFirstName(%gender);
 %lastName = GetLastName();
 return %firstName SPC %lastName;
}//GetFullNPCName

function GetFirstName(%gender){
 if(strcmp(%gender, "male") == 0) return GetMaleFirstName();
 return "Error: Unknown gender";
}//GetFirstName

function GetMaleFirstName(){
 %names[0] = 2; // As before, sub zero has the string count in it
 %names[1] = "Joe";
 %names[2] = "Bob";
 %rnd = GetRandom(1, %names[0]);
 return %names[%rnd];
}//GetMaleFirstName

function GetLastName(){
 %names[0] = 2; // Same as the first name function, but not gender specific
 %names[1] = "Johnson";
 %names[2] = "Smith";
 %rnd = GetRandom(1, %names[0]);
 return %names[%rnd];
}//GetLastName

Now our spawners have a random amount of bots to spawn that are all assigned a random name and a random location around the

spawner. Adding a GuiShapeNameHud object to the play hud will now show your random names over the heads of the mobs. Also, if

you're short on naming ideas google "baby names" and you'll hit too many sites to count that have thousands of names listed.
I had intended to add more to this but it's getting rather long and it's getting late =P

Best of luck,
-Zann

About the author

Recent Blogs


#1
10/18/2004 (2:26 am)
I just got the spawners for my bots started 4 days ago.
You have written most of the features I was about to start on.
This is why I love the GGC.
Thanks so much!
The code I'm working with is not very standard, but I'll see what I can post back.

Ari Rule (BrokeAss Games)
#2
10/26/2004 (5:20 am)
the name of the resource sounds cool, so i put it in, but i guess i dont understand, what its for, does the object that gets spawn on the map take the place of "spawning on a path" ?

I was under the impression that i could spawn a bot with a click of an editor button.

I get nothing.
maybe i did somthing wrong
#3
10/27/2004 (12:16 pm)
Are you calling the SpawnAll function or calling spawn passing the ID of a spawner?
#4
11/24/2004 (1:12 am)
Hey Jared,
The code you provided is great except for one major problem.
I hope the problem is self inflicted, so that I can fix it.
The Nudge Spawn and Nudge functions do not seem to append the random position onto the position of the spawn object.
The spawner creates a perfectly spread out spawn... but the position of Nudge is relative to 0,0 and not to the position of the GenericSpawner object.
Here is the code I have put in, it should look familiar.
Any help is much appreciated.

I call it like this:

// Set transform
%bot.setTransform(NudgeSpawn(%spawner.getTransform()));

// Nudge Spawn -----------------------------------------------------------------
function NudgeSpawn(%origTransform){
// X & Y are to move the position
%wordx = Nudge(GetWord(%origTransform, 0));
%wordy = Nudge(GetWord(%origTransform, 1));

%origTransform = SetWord(%origTransform, 0, %wordx);
%origTransform = SetWord(%origTransform, 1, %wordy);

return %origTransform;
}//NudgeSpawn

function Nudge(){
%high = 10; // Min/Max value we can move the spawn in any direction
%low = -10;
%val = GetRandom(%low, %high);
echo(%val);
return %val;
}//Nudge
#5
11/24/2004 (1:27 am)
Ok, I fixed it.
It seems this wonderful code had an error, and I finally get to help by correcting it.
Nudge was not receiving a value and the value was not appended.
Here's my fix for Jared's awsome spawners.
Just replace the Nudge function with the one below.

function Nudge(%val){
%high = 10; // Min/Max value we can move the spawn in any direction
%low = -10;
%val = %val + GetRandom(%low, %high);
echo(%val);
return %val;
}//Nudge
#6
11/30/2004 (2:36 pm)
Hi,
I am new to the torque and am having a bit of trouble with this. I can't seem to get a player to spawn. All I get is an empty Generis Spawner object. I was wondering if someone coudl help me with this.
Thanks,
Drove171
#7
12/12/2004 (12:31 am)
@Drove171
Try SpawnAll(); in the console after you have placed a spawner.
Do you have AI allready?
#8
01/25/2006 (5:06 pm)
Excellent resource, I had almost no problems implementing it. However, I was wondering how I could get the bots to follow a path after they have been spawned. I'm currently using the followPath function from the Bot Path-Finding tutorial by Kevin Harris, but the bots will only move from the point where they are spawned to the first node in the path (out of three total). Anyone have any ideas?
#9
01/30/2006 (3:45 am)
Here's a fun piece of code.
You must have the RPGDialog resource installed.

Add this to the end of ~/server/scripts/RPGDialog.cs
//<ActionList>Spawn(spawner)
function Spawn(%spawner,%client,%sender,%npcFile)
{
   Spawn(%spawner);
   CloseDialog(%client,%sender,%npcFile);
}

Drop a bot spawner object and note the object number in the editor (like 4147).
Edit an NPC answer and add the Spawn() function to the actions list like: Spawn(4147)
Save the NPC script (in game).
When you talk to the NPC and get that answer the bot spawner object spawns bots.
Use trade/reward system for mission items, etc.
Instant missions, enjoy.
#10
04/25/2007 (7:49 pm)
I'm having a hard time putting this into TGEA/TSE. I keep getting this error in the console: unable to find playerdata::create Any ideas?? Thanks

edit::
Ok so i fixed the problem, it was rather simple but I suck at scripting so it took some time........


right bellow the ::onadd function in GenericSpawner.cs I put this:

function PlayerData::create(%block)
{
%obj = new AiPlayer() {
dataBlock = LightMaleHumanArmor; // datablock for player object
};
return(%obj);
}

That did the trick, btw nice tutorial.......keep up the good work Community!!
#11
12/09/2007 (7:37 am)
I put this in, but when I called the SpawnAll(); function in the concol it told me "input (0): unable to find function SpawnAll". And so no bots.