T3D/Tutorials/SimpleFPSTutorial/Part12

From TDN

SIMPLE FPS TUTORIAL for Torque 3D



Back to Part Eleven: GamePlay, Challenge, Variation and Set-Pieces

Part Twelve




Finale:

Note: Through the various parts of this Tutorial, you should now have learn how to use the editor proficiently for creating triggers, paths, markers, etc. In this final part, we shall despense with the guides point you to the exact location of every object in the editor, or the associated hotkeys to Editor and camera. You should be well versed in their use by now ... hopefully.

We want out Tutorial to end with a "set-piece". The player has fought to reach the end and will be expecting some sort of "final showdown". This could be anything from a "boss battle" against a new and more powerful enemy that the player has not previously encountered, to a "final assault" on crowded enemy positions, to a "last stand" against hordes of attacking bots.

Discounting the "boss battle" idea on the ground that we only have one type of Ai bot, let's make our "set-piece" a "final assault" on the enemies at the station01 model. The player has been able to see the station01 model since the start, and with it being the largest object visible in the Tutorial level, it was a natural focal point for the player to want/expect to end up at it.

Create a new simGroup to keep all of our set-piece objects, paths and triggers in. Call it "finale".

Place a "TutorialTrigger" just inside the entrance to the large circle, and scale it to cover the route. In the Object Inspector add a new DynamicField and value, and name them:

TriggerOff   --->   0


We're going to use this value to make the trigger activate only once, and ignore all further activations. In it's "enterCommand" add:

if(finalTrig1.triggerOff == true)
{
   echo("Trigger is off");
   return;
}
%checkclass = %obj.getClassName();
if(%checkclass $= "Player")
{
   echo("Activating FinalTrig1");
   finalTrig1.triggerOff = 1;
}


Have a quick test to make sure it all works. When the Player enters the trigger for the first time the console should print the second echo, and everytime the Player enters after that, the script will print the first echo and abort at the "return".

Finale Trigger1

Let's make some bot paths. Inside the station01 model, inside the shorter tower, create a new path called "finalPath1", uncheck "isLooping", and add a "path node" at the beginning of the overhead walkway/bridge and drag/drop it into "finalPath1". You want to create a path from here, along to the centre of the walkway/bridge and finishing standing "on top of the raised side which is facing the entrance to the large circle of the terrain". It's a little tight around the walkway, so use as many "path nodes" as you need and move them in the editor until they're in the right place, hovering just above the mesh of the model (if you set Object Placement to "Screen Center" it might help when creating them). Add them to the path and check that the route is good to get a bot through to stand on the edge of the walkway. When you're happy with it, call the first "marker" "fpath1start". The path goes in "finale" simGroup.

Sniper Path

Let's test this to make sure the bot get's to his position and does NOT fall off the edge. Add this script to the "finalTrig1" "enterCommand", inside our check for Player class function.

   if(!isObject(fbot1))
   {
      %finalbot1 = aiplayer::spawnBot(fbot1, fpath1start, semiauto);
      %finalbot1.followPath("MissionGroup/finale/finalPath1", 9);
   }


You may have to reset the "triggerOff" variable to 0 on "finalTrig1". If the bot has problems getting along the path or falls off at the end, alter the "markers" until it works as we want.

Sniper

We'll make 2 more paths, each coming from the different entrances to station01 model's two towers. In the lower tower, inside the doorway and to the side (so it can't be seen from where the player will be) place the first "path node" and create a path that comes out of the door and runs towards our "finalTrig1", at it's left side (as viewed from the trigger towards the station). Check the path is working correctly and call the first "marker" "fpath2start".

Pincer Move Path Left

Now do the same for the opposite tower, calling the path "finalpath3", the first "marker" "fpath3start" and bending the path around to the right side of "finalTrig1".

Pincer Move Path Right

Add the new spawn functions inside our usual check for the Player class on "finalTrig1" "enterCommand", right below the spawning function for "fbot1", inside the check for Player class function.

   if(!isObject(fbot2))
   {
      %finalbot2 = aiplayer::spawnBot(fbot2, fpath2start, semiauto);
      %finalbot2.followPath("MissionGroup/finale/finalPath2", 9);
   }

   if(!isObject(fbot3))
   {
      %finalbot3 = aiplayer::spawnBot(fbot3, fpath3start, fullauto);
      %finalbot3.followPath("MissionGroup/finale/finalPath3", 9);
   }


Save and take the chance to test (remember that you might have to reset the "TriggerOff" variable).

If you try and walk the Player through one of the station entrances, you'll notice that they're rather steep. Lower the station01 model a little (say -3.5 on the z-axis) until the Player can easily get inside. You don't have to lower the "path nodes", to be just above the new surface height, but you can if you want.

Create a new trigger between "finalTrig1" and the station01 model and scale it to create a long rectangle which touches the sides of the large circle in the terrain, and name it "finalTrig2". This trigger needs the ability to turn off, so once again, add the dynamic Field and variable.

scale   --->   150 10 5
triggerOff ---> 0


Finale Trigger2

In the "enterCommand" add:

if(finalTrig2.triggerOff == true)
{
   echo("Trigger2 is off");
   return;
}

%checkclass = %obj.getClassName();
if(%checkclass $= "Player")
{
   echo("Activating FinalTrig2");
   finalTrig2.triggerOff = 1;

   if(!isObject(fbot4))
       aiplayer::spawnBot(fbot4, fpath1start, fullauto);

   if(!isObject(fbot5))
       aiplayer::spawnBot(fbot5, fpath2start, fullauto);

   if(!isObject(fbot6))
       aiplayer::spawnBot(fbot6, fpath3start, fullauto);

   LoopForRemainingAi(); // <--- our check for clearing the area

   finalTrig2.triggerOff = 1;
}


This trigger will spawn our final 3 static bots inside the station01 model, and when the Player has killed them, the Tutorial game will be finished. We need a way to check for this, so we are going to use a looping function called "LoopForRemainingAi" to check for any objects of the class "AiPlayer". As this is a specific script for this level only, we'll place it into the "gameSFPST.cs" file. Quit T3D and load up the script file in your text editor or IDE. At the very bottom add:

function LoopForRemainingAi()
{
      %ai = 0;
      %here = fpath1start.getPosition();
	  
      InitContainerRadiusSearch(%here, 100, $Typemasks::AiObjectType);
	  
      while((%target = containerSearchNext()) !=0)
      {
         if(%target.getState() !$="Dead")
            %ai++;
      }

      if(%ai == 0)
      {
         echo("No Ai left at Station - Game Over, Player Wins!");
         centerPrintAll( "Station Clear of Bots - You Win",  5, 1);
          //time in seconds not m/s!
         schedule(5000, 0, "quitSimpleFPSTutorial");
      }
      else
      {
         echo("continue checking for remaining Ai");
         schedule(5000, 0 , "LoopForRemainingAi");
      }
} 


We are going to use "raycasts" again, but instead of a straight-line like we used with our Ai/bot scanning, this time we will create a box and check for objects within it's radius - "InitContainerRadiusSearch". This will now start a search for all Ai bots (typemasks::AiObjectType) within a radius of 100 units. If it finds a bot it will first check whether it is dead or not, and if the bot is alive it will add incrementally increase it's count of the bots (%ai). We check for whether the bot is alive or not, due to the stock bot taking sometime to delete (fade out) after being killed. When the function has checked all objects, if there are still bots left it will repeat the search 5 seconds later. If there are no bots left, the player will have won the Tutorial game and the loop will end.

It might be a good idea to inform the Player that the game has ended. One way to do this is to display the information on the screen. We can use the various "centerPrint" functions for this, and "centerPrintAll" will automatically find clients (though remember there's only one Player here). After 5 seconds the message will disappear, and we will end the game. Note: centerPrint counts in "seconds" and not "m/s".

Lastly create the function to quit the Tutorial game and add it at the bottom of "gameSFPST.cs".

function quitSimpleFPSTutorial()
{
   MessageBoxYesNo( "You Won!", "Exit?", "disconnect();", "");
}


The Player can exit back to the Main Menu or stay inside the level.

Now that we've told the Player that the game is over, it might help if we gave them some instructions to start with - but this time, let's print it at the bottom of the screen so the player can still see where he is going. Restart Torque3D and load our level. Select the first trigger ("trig1") and in the "enterCommand" make the script now appear as:

%checkclass = %obj.getclassname();
if(%checkclass $= "Player")
{
   if(!isObject(bot1))
      aiplayer::spawnBot(bot1, botSpawn1, semiauto);

   bottomPrintAll( "Get to the Station and Clear it of Bots",  5, 1);
}


Now when the player enters the first trigger he's got some basic information on what he should do.

And that' s all folks! You have completed the Simple FPS Tutorial.


And here is a final video of what the Player should experience.

Video of Playing the Simple FPS Tutorial Level (YouTube)