T3D/Tutorials/SimpleFPSTutorial/Part8

From TDN

SIMPLE FPS TUTORIAL for Torque 3D



Part Seven: Create a Custom GameType

Part EIGHT




Create A Custom AIPlayer:

Firstly a note on semantics: Any time you see mention in these tutorials of "AIPlayer", "Ai" or "bot" - it's all refering to the same thing; a "Computer Controlled Player". In this series of tutorials all "bots" are hostile to the player, or at least that is how we will make them.

Looking inside "game/art/datablocks" you will see the file "AiPlayer.cs". This works in exactly the same way as our custom TutorialPlayer.cs, deriving it's variables from the stock player.cs file, and overriding it anywhere that there is a duplicate entry.

Let's make our own version of the Aiplayer file, and call it "TutorialBot.cs". We want to call the new PlayerData "TutorialBot" and derive it form the stock player. We also want to add our custom "runSurfaceAngle" to prevent the bot from escaping over the walls of our game area, and give it the ability to carry an inventory of our custom weapons. We add a new variable called "shootingDelay", something that we will use later to schedule it's combat logic and also turn reduce. Finally, we reduce the amount of damage it can sustain before death, making it not as tough as the player (though in our final test game it will be much more numerous).

datablock PlayerData(TutorialBot : DefaultPlayerData)
{
   shootingDelay = 500;
   
   maxDamage = 50;// bots are half as tough as the human player
   runSurfaceAngle  = 50;//yorks - was 70

   maxInv[semiauto] = 1;
   maxInv[semiautoAmmo] = 20;
   maxInv[fullauto] = 1;
   maxInv[fullautoAmmo] = 30;
};


Now exec that in "game/art/datablocks/datablockExec.cs" with all our other custom scripts. Just like our custom player script, which derives from the stock "player.cs" file, we need to load "TutorialBot.cs" after "player.cs".

//custom datablocks
exec("./weapons/weaponEffects.cs");// <---- this needs to be loaded BEFORE the weapons that use it
exec("./weapons/semiauto.cs");
exec("./weapons/fullauto.cs");
exec("./TutorialPlayer.cs");//  <---- this needs to be loaded AFTER player.cs
exec("./TutorialBot.cs");//  <---- this needs to be loaded AFTER player.cs


Now we move over to the Ai server scripts in "game/scripts/server/AiPlayer.cs". Looking in here you can work out that the spawning system first requires a scriptObject to load, which in turn checks for an Ai in-game and spawns one only if none exists and only at a path. We really want to be able to spawn a bot out of thin air without any other aspects convoluting the process - so we're going to create a function that spawns straight away at a specific XYZ location which we can change every time. The easiest way to get an accurate XYZ location in-game is to reference an object's placement in the world, like a spawnSphere.

In "game/scripts/server" create a new script file called "TutorialBot.cs". Firstly, copy and paste from "AiPlayer.cs" all the functions relating to "DemoPlayer" (not the functions for AiPlayer) and then change every instance of "DemoPlayer" to "TutorialBot". Your new "TutorialBot.cs" should now look something like this:

// AIPlayer callbacks
// The AIPlayer class implements the following callbacks:
//
//    PlayerData::onStuck(%this,%obj)
//    PlayerData::onUnStuck(%this,%obj)
//    PlayerData::onStop(%this,%obj)
//    PlayerData::onMove(%this,%obj)
//    PlayerData::onReachDestination(%this,%obj)
//    PlayerData::onTargetEnterLOS(%this,%obj)
//    PlayerData::onTargetExitLOS(%this,%obj)
//    PlayerData::onAdd(%this,%obj)
//
// Since the AIPlayer doesn't implement it's own datablock, these callbacks
// all take place in the PlayerData namespace.
//-----------------------------------------------------------------------------
function TutorialBot::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 TutorialBot::onEndOfPath(%this,%obj,%path)
{
   %obj.nextTask();
}

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


Now we're going to create our custom spawn function. When spawning, the script command is "new", as in "create a new one of this object/item/Ai/Player/etc". We shall call the function "spawnBot" and give it 3 parameters of "%name", "%spawnpoint" and "%weapon".

function AIPlayer::spawnBot(%name, %spawnPoint, %weapon)
{
	%player = new AiPlayer(%name)
	{
		dataBlock = TutorialBot;
		path = "";
	};
	MissionCleanup.add(%player);
   %player.setShapeName(%name);
   %player.setTransform(%spawnPoint.getTransform());
   echo(%name @ " has spawned at " @ %spawnpoint);

   %player.equipBot(%weapon);
   
   return %player;
}


The "name" is only really to help us reference the bot through later scripts without having to constantly check what new "ID number" the game engine has given it after each time it spawns. We also reference the "name" in the "namespace of the newly created AiPlayer. "new AiPlayer(%name)". The "spawnPoint" is the object that already exists in the game world, that it will reference for it's starting location. The "weapon" will choose which weapon and ammo the bot starts with. And to avoid clogging the "spawnBot" function up, we'll make a new function for this called "equipBot". You can see the function being called in "spawnBot", and see that it is passing the "%weapon" variable to the new function.

Below the "spawnBot" function create the new one. We'll use the names of the weapons "semiauto" and "fullauto", and check which one to equip the bot with. We'll also set the range for these weapons here, so that the player will have something to check distance of a target against to see if he can shoot it or not. More on that later.

function AIPlayer::equipBot(%this, %weapon)
{
	if(%weapon $="semiauto")
	{
		%weaponAmmo = "semiautoAmmo";
		%weaponImage = "semiautoImage";
		%this.range = 200;
	}
	
	if(%weapon $="fullauto")
	{
		%weaponAmmo = "fullautoAmmo";
		%weaponImage = "fullautoImage";
		%this.range = 50;
	}
	
	//etc for adding more weapons

	%this.setInventory(%weapon, 1);
	%this.setInventory(%weaponAmmo, %this.maxInventory(%weaponAmmo));
   
	%this.mountimage(%weaponImage, 0);
}


So, we check if the weapon name is one we know, get the weapon and ammo types for it (and the maximum range of it's projectiles), and then equip the bot with the items in it's inventory. Finally we mount the weaponImage on the bot so he's holding and using the weapon.

Save and exec the file in "game/scripts/server/ScriptExec.cs", which should now look like this:

//custom scripts
exec("./semiauto.cs");
exec("./fullauto.cs");
exec("./gameSFPST.cs"); // Overrides GameCore with Simple FPS Tutorial functionality.
exec("./TutorialBot.cs");


Now, let's see if it works in-game. Boot up Torque3D and load up your Tutorial level. Go into camera mode (CTRL C) and find the second rock along our path (left, and then at the first right corner). Open up the World Editor (F11) and place a new spawnSphere just to the left of the rock. "Scene Tree-> Library-> Scripted-> Misc-> SpawnSphereMarker". From the World Editor toolbar, press the "toggle button for object transforms and world transforms" (mouseover the buttons for info). The default is "world transform" and change that to "object transform". Select the "rotate selection" button below the "Object Editor" tab. Reselect the new spawnSphere if you have to and rotate it 90 degrees toward the direction that the player will come from. Now press the "Object Editor" tab's "Move Selection" button and the familiar XYZ arrows will reappear, though this time they have rotated 90 degrees, matching the "object rotation". It's doubtful that you got an exact 90 degrees rotation, so in teh "Object Inspector", under the "Transform" section manually type the "rotation" to be " 0 0 1 90". Now rename this SpawnSphere "botspawn1".

botspawn

Click the "Play Game" icon to close the World Editor, and bring up the console (this may be a different key depending on your keyboards country settings, but in the US it should be the "tilde" key, whilst in the UK it is "apostrophe" which shares a key with "@"). With the console up, type in the "aiplayer::spawnbot" function, using the name "bot1", the spawnsphere "botspawn1" and for now weapon "fullauto". Hit return and hopefully, a new custom Ai bot has been created.

aiplayer::spawnbot(bot1, botspawn1, fullauto);


You might notice that he's a little dark against the shadowed wall behind him (a guy wearing a black leather coat against a dark background). So we shall make a final change to the level design. Select "theSun" in "missionGroup", and click the dark box at "ambient". Change the RGB setting as follows, noting that you can only type 4 characters into each box. Press "select" when finished.

R   .278
G   .314
B   .357


That makes the shadows less black, but now makes everything else rather bright. Select coloured box next to "color" in the "Object Inspector" (with the sun still selected of course). Add again change the RGB values manually.

R   .769
G   .702
B   .702


And now you should have something a little easier on the retina. Check out the difference.

bot and backgrounds

Finally, toggle back to the player (CTRL C) and go pick up the "semiauto" weapon and ammo. Now go around the corner and walk up to "bot1". Walk right up to him and have a good look, then, shoot him and keep shooting until he dies. Upon death he will throw all the items in his inventory away, so the player can now pick them up. These items are not "static" like the ones which we placed in the level area, and will not respawn once picked up. They will also fade out and delete themselves after a certain number of seconds (find item.cs for more info on that).

Go ahead and pick up the "fullauto" weapon and it's ammo. You can't pick up healthKitPacks unless you are injured so don't worry about that for now.

Our newly created custom Ai might have been armed to the teeth, but with no combat functions he wasn't dangerous. In a real game, you'd want the enemy bots to shoot back.



Part Nine: Simple Ai Combat Scripts