This tutorial will walk you through using the basic AIPlayer class that copmes with T3D. The code for AI tends to be very specific to your game and its genre which makes it hard for Torque to implement anything general purpose.
The built-in script commands cover moving the bots from one place to another in a straight line, aiming at a moving object, aiming at a static position, and firing on that object. There is a great deal that you can do with these basic commands and they can give you a jumping off point for writing far more complicated behavior.
How to get Started
Load up Torque and load a mission. At this point you should be standing in the mission as a soldier holding a rifle. Let's detach the camera from the Player so that we can fly around. You can do this either by hitting F8 (will drop the camera at the player) or Alt-C (will drop the camera at the original spawn point). Fly the camera around a bit until you find an open patch of ground.
Now hit the ~ key (the key beside the 1 key on most keyboards) to bring down the console window. We are going to be copy/pasting script commands into this and observing the results.
Let the Fun Begin!
First, let's actually spawn a bot. Copy the following line of code into the text input field at the bottom of the console window and hit enter:
You should have seen another soldier spawn where your camera is and drop to the ground. Let's break down what exactly this script command did a little more:
This piece of the script command is creating a new object of the type AIPlayer and giving it the name bob so that we can access the new bot by name.
This is telling the engine that the new AIPlayer will be using the PlayerData datablock named DefaultPlayerData. Basically, datablocks set up a bunch of static data that is going to be shared among all the Players/AIPlayers that use this datablock. This includes things like which model to use, how fast they move, which weapons they can pick up, how high they can jump, and what sounds to use for collision. For the purpose of this tutorial we are simply using the same datablock as the default Player that you spawned in as.
Okay, this is going to dip a little bit into the underlying networking of Torque and touch on a few higher level scripting concepts. If you don't completely follow along don't sweat it. It'll make sense in time.
LocalClientConnection is a global script variable that gets created whenever you host a server. Since "single-player" in Torque is done by creating a server and then connecting a local-only client to that server, we still have access to that variable. The variable is actually a GameConnection object which handles your connection to the server and can be a handy place to store data that is specific to a client. A client/connection is distinct from a Player or a Camera or WheeledVehicle since you can actually delete/recreate those without reconnecting to the
In the Full or Empty templates, when a client joins a Torque game it creates a free flying camera that can be used until a Player or some other control object is created for the client to run around as. The ID number for this camera is then stored in a dynamic script member on the client's connection object (i.e. LocalClientConnection.camera). When we detached from the Player before and started flying around as a camera what we were really doing was switching back to that camera. One thing to keep in mind here is that LocalClientConnection is only available if you are the host (which you are in single-player). For multiplayer you will have to access the client connections a different way (covered elsewhere).
We use getPosition() to get the 3D coordinates of the camera that we are flying around as (try echo(LocalClientConnection.camera.getPosition()); to see what these coordinates are) and create the AIPlayer at that position (position isn't part of the datablock, because it obviously varies from Player to Player).
Come chase me little Bob
Now, how do we get our new AIPlayer to run around? Torque has the most basic form of pathfinding possible built-in: move in a straight line from point A to point B and don't worry about running into things. You can see an example of this by flying the camera away from the bot (use ~ to hide and unhide the console window), pasting the following script command into the console, and hitting enter:
Bob should come running over to stand below wherever your camera was when you ran the command. Fly the camera around to a new position and run the command again (you can hit the up arrow in the console input field and it will cycle through your previous commands).
getTransform() is similar to getPosition() except that it also includes rotation information. We could use either in this case but it is often useful to have this extra information (for example: orienting a dropped object to match your camera's rotation). Try running echo(LocalClientConnection.camera.getTransform()); to see what it is returning.
Fun with guns
Okay, time to give Bob a gun. Simply run this command:
This uses the ShapeBaseImage system to mount a Lurker (rifle) image on your AIPlayer (in slot/mount 0). The image/weapon system can get a little complicated so I won't go into it here beyond showing you how to give one to the player. You can check the name of your weapons in the datablock library "ShapeBaseImageData".
This is great, except that poor Bob doesn't have any ammo. So now run this command:
Again, the inventory system can get pretty complicated but this is how you go about giving a Player/AIPlayer 50 rifle bullets. Note that this inventory is all made in scripts in the stock templates. It's not built in to the engine.
We need something for Bob to shoot at so let's fly over a little ways from Bob create a second bot named Dirk:
How do we get Bob to aim his gun at Dirk? Simple enough:
This is a pretty interesting command. It means that wherever Dirk moves to, Bob will automatically track him. The tracking code is run in C++ so it is quite quick and accurate. To see this in action fly the camera over to a new position where you can see Bob clearly and are pretty far away from Dirk and run:
You should see Bob automatically turn to follow Dirk's movements (even up and down hills).
Time to shoot at Dirk!
This command tells the weapon/image in slot 0 that it is being "triggered" (same as when you push the mouse button). This causes the Lurker to start rapid firing. Once all 50 bullets have been shot then Bob will be out of ammo and we will have to give him some more:
Since the gun is still in a "triggered" state, Bob will start firing right away. If Dirk dies just spawn a new one with the new AIPlayer(Dirk) command from above. You will have to reset Bob's AimObject also.
Bob has pretty poor aim
Unless you have just the right angle on Dirk, Bob is most likely shooting at Dirk's feet and isn't doing much damage. The problem is that the Dirk's position that Bob is tracking is centered at the pivot point of his bounding box which is located between the bottom of his boots. There are a couple of ways to fix this:
The first one is to use the setAimLocation() command to tell Bob to aim at a specific location rather than tracking Dirk directly. This can be done with this command:
Be sure to note that we couldn't do Dirk.getPosition() + "0 0 1". We had to use the VectorAdd() script function to add two vectors together (+ will just add the first numbers of the two vectors).
The only problem with this command is that if Dirk (or a human controlled Player) moves then Bob will still be shooting at the old position and not the new one. You could counter this by setting up a schedule to reset the aim location but this is going to kill your performance. Fortunately, there is a second parameter to the setAimObject method that allows you to specify an offset while amiing:
Bob will now track any of Dirk's movements and shoot at 1 unit above his current position (essentially in the middle of Dirk's torso). You can use this offset variable to do some pretty cool things with leading a moving target or affecting the bot's accuracy dynamically.
Time to say goodbye to Bob:
You can do quite a lot with just these simple script commands. For example, I wrote an entirely script driven AI for a soccer game using the schedule() command and leveraging just the stock AI commands. You could also get pretty far into an FPS shooter with these commands or do a simplistic RTS.