AI script Framework Question
by Areal Person · in Technical Issues · 03/06/2006 (7:25 am) · 17 replies
Hi, I'm new to Torque script but I know how to program.
Could someone please give me a simple file & function overview of how to set up two AIPlayers in a common framework so I can clone the general concept to do RAD prototype on adding multiple bots in that framework.
What basic files and functions do I create/use ? multiple AIPlayer.cs ? What general objects would I use in script ?
Here's my problem. When I try to use two different aiplayers its like the second code module (.cs) is not being loaded.
I copyed AIPlayer from the kork example and renamed them bot_1.cs & bot_2.cs
I load each one from the game.cs. like this exec("./bot_1.cs"); etc. It will work
For either one but not for both ? there's only one bot running around my new paths
I set up.
Each AIPlayer has its on datablock defines like datablock playerdata (bot_1 : botShape)
And the other functions that you would find. in AIPlayer some are static functions. is this a problem ?
Is there only suspose to be one AIPlayer file calling other bot files ? Do I put all the code
For AI Players in the player.cs file ? or what ?
Thanks for the help.
I'm trying to break out of the static model.
Could someone please give me a simple file & function overview of how to set up two AIPlayers in a common framework so I can clone the general concept to do RAD prototype on adding multiple bots in that framework.
What basic files and functions do I create/use ? multiple AIPlayer.cs ? What general objects would I use in script ?
Here's my problem. When I try to use two different aiplayers its like the second code module (.cs) is not being loaded.
I copyed AIPlayer from the kork example and renamed them bot_1.cs & bot_2.cs
I load each one from the game.cs. like this exec("./bot_1.cs"); etc. It will work
For either one but not for both ? there's only one bot running around my new paths
I set up.
Each AIPlayer has its on datablock defines like datablock playerdata (bot_1 : botShape)
And the other functions that you would find. in AIPlayer some are static functions. is this a problem ?
Is there only suspose to be one AIPlayer file calling other bot files ? Do I put all the code
For AI Players in the player.cs file ? or what ?
Thanks for the help.
I'm trying to break out of the static model.
#2
I have searched, and looked and asked. Been told to look at toher resorces, nothing... Eather no one has figured it out, it its imposable with to do with just scripts, or they dont wish to tell us.
I have new shapefiles defined, i have tryed the IF statment from anther thread (at work dont have bookmark), i have tryed 2 AIplayer type scripts. I can ever, only, get one to work.
I know its posable, but i cant find anywere where someone has done it and passed it back to the counmminity, and the article on TDN only goes over commands for ONE AIplayer.
This question comes up alot.
03/06/2006 (7:51 am)
I would also like to see a working AIplayer that has more than one "bot" or "npc" running, with unique shape, path, and name.I have searched, and looked and asked. Been told to look at toher resorces, nothing... Eather no one has figured it out, it its imposable with to do with just scripts, or they dont wish to tell us.
I have new shapefiles defined, i have tryed the IF statment from anther thread (at work dont have bookmark), i have tryed 2 AIplayer type scripts. I can ever, only, get one to work.
I know its posable, but i cant find anywere where someone has done it and passed it back to the counmminity, and the article on TDN only goes over commands for ONE AIplayer.
This question comes up alot.
#3
If you want different looks and behavior, inherit Demoplayer and change want you want...
Something like this:
datablock PlayerData(DemoBot1 : DemoPlayer)
{
shapeFile = "~/data/shapes/bot/bot1.dts";
}
And change function AIPlayer::spawn(%name,%spawnPoint) to call the datablock you want.
03/06/2006 (7:56 am)
If all the bots are to look and behave the same, then it's quite simple... $num=0;
function AIPlayer::spawn(%name,%spawnPoint){
%nameID = "bot" @ $num;
%player = new AiPlayer(%nameID)
{
dataBlock = DemoPlayer;
};
//echo(%nameID);
$num++;
MissionCleanup.add(%player);
%player.setShapeName(%name);
%player.setTransform(%spawnPoint);
return %player;
}
function AIPlayer::AIpickSpawnPoint()
{
// generate random spawn location
%numX = getRandom(-100,100);
%numY = getRandom(-100,100);
%loc = %numX SPC %numY SPC "160 0 0 1 0";
%player = AIPlayer::spawn(%name,%loc);
return %player;
}
$numofbots = 50;
function AIManager::spawn(%this)
{
for(%i=0;%i<$numofbots;%i++) {
%player = AIPlayer::AIpickSpawnPoint();
//assign the bot to a path or use setDestination
//%player.followPath("MissionGroup/Paths/Path1",-1);
}
return %player;
}If you want different looks and behavior, inherit Demoplayer and change want you want...
Something like this:
datablock PlayerData(DemoBot1 : DemoPlayer)
{
shapeFile = "~/data/shapes/bot/bot1.dts";
}
And change function AIPlayer::spawn(%name,%spawnPoint) to call the datablock you want.
#4
Only talking about script, not the C++ modifications.
I currently use a different datablock for each bot_? in it's own .cs file.
I have one for bot_1 in the bot_1.cs file and one for bot_2 in the bot_2 .cs file. but for some reason the code in the second file (no matter which one) is not being executed. each of these these bot_? files implement the static AIPlayer functions.
like this... E.G. BOT_1.cs
datablock PlayerData( bot_1 : ManShape )
{
// TO DO: Add extra stuff here to manage new A.I. functionality...
patrol = true;
attack = false;
};
function bot_1::onReachDestination( %this, %obj )
{
// Moves to the next node on the path.
if( %obj.path !$= "" )
{
if( %obj.currentNode == %obj.targetNode )
%this.onEndOfPath( %obj, %obj.path );
else
%obj.moveToNextNode();
}
else
echo( " bot_1::onReachDestination warning - Path is blank!" );
}
//-----------------------------------------------------------------------------
// AIPlayer static functions
//-----------------------------------------------------------------------------
function AIPlayer::spawn( %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = bot_1;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
ect..., etc...
//************************************************************
//************************************************************
like this... E.G. BOT_2.cs
//************************************************************
//************************************************************
datablock PlayerData( bot_2 : AlienShape )
{
// TO DO: Add extra stuff here to manage new A.I. functionality...
patrol = true;
attack = false;
};
etc... AS ABOVE FOR bot_2
...
...
//-----------------------------------------------------------------------------
// AIPlayer static functions
//-----------------------------------------------------------------------------
function AIPlayer::spawn( %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = bot_2;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
Is this the correct file structure ? What am I missing ?
Thanks.
03/06/2006 (7:59 am)
NO The bots need to use different resources in there own directory, there different charcters.Only talking about script, not the C++ modifications.
I currently use a different datablock for each bot_? in it's own .cs file.
I have one for bot_1 in the bot_1.cs file and one for bot_2 in the bot_2 .cs file. but for some reason the code in the second file (no matter which one) is not being executed. each of these these bot_? files implement the static AIPlayer functions.
like this... E.G. BOT_1.cs
datablock PlayerData( bot_1 : ManShape )
{
// TO DO: Add extra stuff here to manage new A.I. functionality...
patrol = true;
attack = false;
};
function bot_1::onReachDestination( %this, %obj )
{
// Moves to the next node on the path.
if( %obj.path !$= "" )
{
if( %obj.currentNode == %obj.targetNode )
%this.onEndOfPath( %obj, %obj.path );
else
%obj.moveToNextNode();
}
else
echo( " bot_1::onReachDestination warning - Path is blank!" );
}
//-----------------------------------------------------------------------------
// AIPlayer static functions
//-----------------------------------------------------------------------------
function AIPlayer::spawn( %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = bot_1;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
ect..., etc...
//************************************************************
//************************************************************
like this... E.G. BOT_2.cs
//************************************************************
//************************************************************
datablock PlayerData( bot_2 : AlienShape )
{
// TO DO: Add extra stuff here to manage new A.I. functionality...
patrol = true;
attack = false;
};
etc... AS ABOVE FOR bot_2
...
...
//-----------------------------------------------------------------------------
// AIPlayer static functions
//-----------------------------------------------------------------------------
function AIPlayer::spawn( %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = bot_2;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
Is this the correct file structure ? What am I missing ?
Thanks.
#5
AIplayer = Unique path + Unique Name + Unique shape x How ever many (Unique:shapes, paths, names) i want.
03/06/2006 (8:01 am)
Thank you for that. but what i have been looking for is this:AIplayer = Unique path + Unique Name + Unique shape x How ever many (Unique:shapes, paths, names) i want.
#6
thanks for the help
03/06/2006 (8:09 am)
Ok, there are two of us people having the same problem. I hope we can get it figured out.thanks for the help
#7
03/06/2006 (8:10 am)
Exactly, Allyn. Let's get Kork, GreenGuy, and Fluffy in the engine as AI using the same properties except Shape and Footsteps, etal... That is exactly the kind of TDN/HowTo article we need....it's almost like TGE has been left 'behind' for TGB...and TSE. Plenty of TDN's on TGB/T2D...almost on an hourly basis. Must be 'law of dimished returns' working...
#8
an example like that to follow.
when can we get it ?
Thanks
03/06/2006 (8:14 am)
Yes, yes, thats what I need ! I think I could break out of the static model if I hadan example like that to follow.
when can we get it ?
Thanks
#9
As far as the footsteps and whatnot, i allready know that that is controled by the "PlayerData" that points to that "players" data block, you can infact , even make each AI just like the player (main) data block to make it unique, adding or removing what that "NPC" needs, this would include footsteps, sounds ETC.. Just copy the playerdata block and give it a unique name.
Then, make each AI point to that datablock as far as the shape file...
Thats the easey part. The part that i cant get to work is the Unique name, and the unique path to follow.
If i could figure this out. Then i would wright a TDN article with images...
But i canot wright what i do not understand.
The AIguard, and patrol only use ONE AI (or bot or NPC if you will).
Im at a loss.
03/06/2006 (8:23 am)
Thats what i would love to wright, but i cant becouse somthing is missing. Somthing is not working. I am by no means a good scriptor. But i think this funcitionality is somthing that people want, and ask for alot.As far as the footsteps and whatnot, i allready know that that is controled by the "PlayerData" that points to that "players" data block, you can infact , even make each AI just like the player (main) data block to make it unique, adding or removing what that "NPC" needs, this would include footsteps, sounds ETC.. Just copy the playerdata block and give it a unique name.
Then, make each AI point to that datablock as far as the shape file...
// Kork (A.K.A Fluffy)
datablock PlayerData(DemoPlayer : KorkBody) //points to the player.cs entry of the same name making
// the shape and carteristics unique.
{
shootingDelay = 2000;
};
// the green guy
datablock PlayerData(DemoPlayer : GreenGuyBody) //points to the player.cs entry of the same name making
// the shape and carteristics unique.
{
shootingDelay = 2000;
};
// a duck
datablock PlayerData(DemoPlayer : DuckBody) //points to the player.cs entry of the same name making
// the shape and carteristics unique.
{
shootingDelay = 2000;
};Thats the easey part. The part that i cant get to work is the Unique name, and the unique path to follow.
If i could figure this out. Then i would wright a TDN article with images...
But i canot wright what i do not understand.
The AIguard, and patrol only use ONE AI (or bot or NPC if you will).
Im at a loss.
#10
03/06/2006 (8:32 am)
So, it sounds as if I'm partially correct, in that you need to duplicate the PlayerBody/DemoPlayer/whatever... script and give the methods the new NameSpace, then use inheritence to glom onto the Parent?? Allyn, in the above codeBlocks, don't you have the Parent and child NameSpaces backwards?? Wouldn't you want the DuckBody/GreenGuyBody/KorkBody as the first parameter?? As that looks, it's like the DemoPlayer is overwritten twice after the first, and ends up acting as the DuckBody??
#11
I believe that the AIManager directly calls "AIPlayer::spawn." You probably will need a spawn function for each of the characters, instead of one generic one for all of them, and you will need to call each of them too.
03/06/2006 (9:03 am)
Both of your bots probably should not define those "AIPlayer::" functions. Because they both have the same name, one will probably overwrite the other or give an error when it's defined. I believe that the AIManager directly calls "AIPlayer::spawn." You probably will need a spawn function for each of the characters, instead of one generic one for all of them, and you will need to call each of them too.
#12
So, something like this might work?:
function AIPlayer::spawn( %db, %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = %db;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
AIPlayer::spawn(bot_1, "Rex1", "camera.getPosition()");
AIPlayer::spawn(bot_2, "Rex2", "camera.getPosition()");
...or is it:
bot_1::spawn(bot_1, "Rex1", "camera.getPosition()");
bot_2::spawn(bot_2, "Rex2", "camera.getPosition()");
....giving two separate shapes, with unique names, & running the same methods??
You have a very good way of explaining things, Drew, enough to make sense of it for an 'artist'....plus chatting, via Messenger, helps a lot!
03/06/2006 (10:12 am)
Thanks, Drew!So, something like this might work?:
function AIPlayer::spawn( %db, %name, %spawnPoint )
{
// Create the A.I. driven bot object...
%player = new AIPlayer()
{
dataBlock = %db;
path = "";
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%player.setTransform( %spawnPoint );
return %player;
}
AIPlayer::spawn(bot_1, "Rex1", "camera.getPosition()");
AIPlayer::spawn(bot_2, "Rex2", "camera.getPosition()");
...or is it:
bot_1::spawn(bot_1, "Rex1", "camera.getPosition()");
bot_2::spawn(bot_2, "Rex2", "camera.getPosition()");
....giving two separate shapes, with unique names, & running the same methods??
You have a very good way of explaining things, Drew, enough to make sense of it for an 'artist'....plus chatting, via Messenger, helps a lot!
#13
I'll give you an update when I get it figured out.
This has helped me.
Thanks again.
UPDATE:
Ok, I think i got a simple framework figured out ! I will post detailed
updates on TDN as soon as I get the code done.
It's really gonna be just some generic wrapper functions (in script) that
promote clean configurations that will init or (bootstrap) the framework out
of the static model for multipul bot's of different types.
ok. thanks
03/06/2006 (12:10 pm)
Ok, Thanks, I'm setting up some feature.labsI'll give you an update when I get it figured out.
This has helped me.
Thanks again.
UPDATE:
Ok, I think i got a simple framework figured out ! I will post detailed
updates on TDN as soon as I get the code done.
It's really gonna be just some generic wrapper functions (in script) that
promote clean configurations that will init or (bootstrap) the framework out
of the static model for multipul bot's of different types.
ok. thanks
#14
That would explane why i allways only get a duck. Lol!
Im not sure how to make the unique, yet, use the same blocks for all the actions (movetowaypoint ETC..)
Like i said, im not a good coder.
Thats why i never got as far as unique name and unique path to follow.
I would love to see a niceley commented script Showing how this would be made.
I learn best from exampile.
03/06/2006 (12:52 pm)
""So, it sounds as if I'm partially correct, in that you need to duplicate the PlayerBody/DemoPlayer/whatever... script and give the methods the new NameSpace, then use inheritence to glom onto the Parent?? Allyn, in the above codeBlocks, don't you have the Parent and child NameSpaces backwards?? Wouldn't you want the DuckBody/GreenGuyBody/KorkBody as the first parameter?? As that looks, it's like the DemoPlayer is overwritten twice after the first, and ends up acting as the DuckBody??That would explane why i allways only get a duck. Lol!
Im not sure how to make the unique, yet, use the same blocks for all the actions (movetowaypoint ETC..)
Like i said, im not a good coder.
Thats why i never got as far as unique name and unique path to follow.
I would love to see a niceley commented script Showing how this would be made.
I learn best from exampile.
#15
03/06/2006 (1:03 pm)
BINGO !
#16
First, add a %datablock parameter to the AIPlayer::spawn and AIPlayer::SpawnOnPath functions. Then spawn the player with the passed in datablock:
Second, adjust the AIManager spawn function to choose which aiplayer to spawn. Here it simply chooses one at random:
Third, define your extra aiplayer datablocks. You can put these in different files if you want
You also need these, they could also be in separate files:
If you're trying this with stock TGE, be sure to copy the other player models into those directories :p
Next, run TGE and kill Kork a few times. He should randomly respawn with alternate player models. Technically, each of those models could be given unique behavior by modifying their functions, and the aiplayer functions that they all share.
EDIT: multiple copy+paste errors.
03/06/2006 (3:32 pm)
Here's an example that works in stock TGE. It's not exactly what you want, but it's probably good enough that you'll get the idea.First, add a %datablock parameter to the AIPlayer::spawn and AIPlayer::SpawnOnPath functions. Then spawn the player with the passed in datablock:
function AIPlayer::spawn(%datablock, %name,%spawnPoint)
{
// Create the demo player object
%player = new AiPlayer() {
dataBlock = %datablock;
path = "";
};
MissionCleanup.add(%player);
%player.setShapeName(%name);
%player.setTransform(%spawnPoint);
return %player;
}
function AIPlayer::spawnOnPath(%datablock, %name,%path)
{
// Spawn a player and place him on the first node of the path
if (!isObject(%path))
return;
%node = %path.getObject(0);
%player = AIPlayer::spawn(%datablock, %name,%node.getTransform());
return %player;
}Second, adjust the AIManager spawn function to choose which aiplayer to spawn. Here it simply chooses one at random:
function AIManager::spawn(%this)
{
%idx = getrandom(1, 3);
if(%idx == 1)
%datablock = DemoPlayer;
if(%idx == 2)
%datablock = BluePlayer;
if(%idx == 3)
%datablock = GreenPlayer;
%player = AIPlayer::spawnOnPath(%datablock, "Kork","MissionGroup/Paths/Path1");
%player.followPath("MissionGroup/Paths/Path1",-1);
%player.mountImage(CrossbowImage,0);
%player.setInventory(CrossbowAmmo,1000);
return %player;
}Third, define your extra aiplayer datablocks. You can put these in different files if you want
datablock PlayerData(DemoPlayer : PlayerBody)
{
shootingDelay = 2000;
};
exec("~/data/shapes/blueplayer/player.cs");
datablock PlayerData(BluePlayer : DemoPlayer)
{
shapeFile = "~/data/shapes/blueplayer/player.dts";
};
exec("~/data/shapes/greenplayer/player.cs");
datablock PlayerData(GreenPlayer : DemoPlayer)
{
shapeFile = "~/data/shapes/greenplayer/player.dts";
};You also need these, they could also be in separate files:
function BluePlayer::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 BluePlayer::onEndOfPath(%this,%obj,%path)
{
%obj.nextTask();
}
function BluePlayer::onEndSequence(%this,%obj,%slot)
{
echo("Sequence Done!");
%obj.stopThread(%slot);
%obj.nextTask();
}
function GreenPlayer::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 GreenPlayer::onEndOfPath(%this,%obj,%path)
{
%obj.nextTask();
}
function GreenPlayer::onEndSequence(%this,%obj,%slot)
{
echo("Sequence Done!");
%obj.stopThread(%slot);
%obj.nextTask();
}...In addition to the demoplayer functions that are already there.If you're trying this with stock TGE, be sure to copy the other player models into those directories :p
Next, run TGE and kill Kork a few times. He should randomly respawn with alternate player models. Technically, each of those models could be given unique behavior by modifying their functions, and the aiplayer functions that they all share.
EDIT: multiple copy+paste errors.
#17
thanks....
07/19/2006 (4:36 pm)
Excuse my ignorance but am a newbie..... so where do you put all that info?// what file? the reason am asking is because im suppose to build a game in 5 days...plz help......skibo187@msn.comthanks....
Torque Owner Rex
BrokeAss Games
...this type of question arrives often and instead of telling you to Search for something 'similar'; I esplane as much as I understand of it...'sounds' like you're using the same datablock for both spawns....?? Or as if you're using the OPP inheritence...which, I think only runs one layer below the Parent. You may have to add the functions under a className variable??? This is something I'm trying to figure out. I can get one type of AI working, but when I try to use inheritence to have another, I get the warnings about the Object not having that method...??
It would be nice to see a 'proper' scripting method for dropping two separate AIPlayers with different shapes and sound files.....I'm getting closer and closer!
Thanks, good luck.
...what is the 'Parent' of "botShape", if it has one[DemoPlayer, PlayerBody]?? Would be also be nice if this OPP 'inheritence' of Tscript were defined as to what's possible/not possible....;). Is this were nameSpaces & className come in???
For example, in the K. Finney second book, there is a chapter devoted to artificial enemies. The code did not work as described UNTIL I removed the className = NPC, and changed it to reflect the Parent-[Armor] of the PlayerData()]...then it worked.
I 'think' to fix this, make botShape a complete duplicate script as the Player, including methods; then use botShape as the Parent...or className?
I can get them spawning where I want, get them attacking me....I can even set each AI player's Skin to a different texture...just no different shapes...
I'm not asking for a free script or work, just an outline or explanation of how it works in the default state. It helps me to see how different methods work in the approach.