Game Development Community

dev|Pro Game Development Curriculum

AI Guard Unit

by Mark Holcomb · 11/27/2004 (5:18 pm) · 335 comments

Download Code File

This is my second attempt at making an AI controlled character. The first was an
AIPlayer that would follow paths. This character is a guard unit. The guard will
wait at it's post (spawn point) until it sees a target. It will then attack that
target while trying to close with it.

If the target is lost the bot will wait at the last place it saw the character and
look around for a little bit, and then it will try to return to it's post.

There is a chance the bot will get stuck trying to return, but there is a simple
routine that has the bot try to move in random directions to try and clear itself
of any obstacles between it and it's post. It's not perfect.

The thinking routine is a simple state machine - which can be expanded on to give the bot more responses and actions.

As with my first ai character, I am using a simple system that allows the designer to drop
markers in the game map that will mark where the bots will start out. When the mission is loaded, the markers are detected and bots are spawned at the marker locations. The markers are then hidden from view. (The markers can be left visible to help in map editing.)

The markers for the guards can be given a dynamic variable called respawn. (Assigned to the marker during map editing.) 'Respawn' will determine whether a bot respawns or not upon death.

The bots have an attention setting. How often they scan is determined by their attention level. The bots get more sluggish (freeing up processor time) when targets are too far away. Conversely, the bot becomes incrementally more attentive as targets come within range, and further aware as targets come into sight.

When a target is found in range and in sight the bot will shoot at the target. The firing sequence is a step of scheduled calls that call for a firing cycle, a trigger down cycle, and a firing delay to control bot rate of fire.

When a bot is attacked it will attempt to sideatep and it's field of vision is temporarily increased to a 360deg field of vision - to emulate looking around to see what happened. The bot's attention level is also set to make it think at it's fastest rate when attacked.

To use the AIGuard...the following changes need to be made.

1. Back up your original game.cs, player.cs, and your current build of Torque. To install AIGuard will require a recompile, since I have created a new class cloned by copying AIPlayer.cc and AIPlayer.h and renaming all references to AIPlayer to AIGuard.
(I did this because I wanted to be able to run both my AIPatroller and AIGuards in the same maps and did not want to have any confusion between them.)

2. Add the files AIGuard.cc and AIGuard.h to your Torque project. (In my instance I saved them to my c:\Torque\engine\game directory and then added them into my project.)

3. Recompile your project and copy your new executable to the appropriate directory for your app.

4. Copy the file AIGuard.cs into your server/scripts directory.

5. Modify game.cs to add the line

exec("./aiGuard.cs");

to the function onServerCreated().

6. Also in game.cs: The section for function StartGame needs to modified the following ways:

Below the lines that read:

// Start the AIManager
new ScriptObject(AIManager) {};
MissionCleanup.add(AIManager);
AIManager.think();

add this:

AIGuard::LoadEntities();

(If you followed my previous resource you may not have any reference to AImanager. Don't fret... just look for the place in game.cs where you have

AIPlayer::LoadEntities

and put in

AIGuard::LoadEntities();

right underneath it.

7. In player.cs in the code for Armor::Damage modify the lines

// Deal with client callbacks here because we don't have this
// information in the onDamage or onDisable methods
   %client = %obj.client;
   %sourceClient = %sourceObject ? %sourceObject.client : 0;
   if (%obj.getState() $= "Dead")
      %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);

to read:

// Deal with client callbacks here because we don't have this
   // information in the onDamage or onDisable methods
   %client = %obj.client;
   %sourceClient = %sourceObject ? %sourceObject.client : 0;   if (%obj.isbot == true)
   {
     %obj.attentionlevel=1;
     %obj.enhancefov(%obj);
   }
 
  if (%obj.getState() $= "Dead")
 {
     if (%obj.isbot == true)
    {
        if (%obj.respawn == true)
          {
           %obj.delaybeforerespawn(%obj.botname, %obj.markerpos, %obj.marker);
           %this.player=0;
           }
    }
   else
  {
     %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
  }
  }


*** If you followed my previous resource there should be no need to change this code.

8. Load your map - Stronghold as an example.
9. Go into the map editor. (F11) Then go into the Editor Creator (F4)
10. Under Shapes there should be a drop down called AIMarker, under that a new item called AIGuard.
11. Create a new AIGuard marker.
12. Select your marker, position it where you like and hit (F3) to modify the marker.
13. If you want to override the default respawn value - create a dynamic variable called respawn and set it's value to true or false.
14. Update your item by clicking 'APPLY'- very important and easy to miss step.
15. Save your mission and reload it.

A bot called Guard1 should appear at the spot of your marker.
If you come within range of the guard he should shoot at you and try to hunt you down.
If you get away, or when you die, he should return to his post.

I hope the resource helps other people get up and running with some AI code in their game.

And again, I'd like to thank the other members of this website whose code has been used in several places in the scripting to make this all work.

Mark H.

P.S. I've also included my AIPatrol class files with this - to install it follow the same instructions as for AIGuard, just substitute AIPatrol where AIGuard appears in the instructions. They can both be run at the same times with no problems.

P.S.S. 11/28/04 - I modified the AIPatrol.cs file to correct for a couple of typos.

P.S.S.S 12/3/04 - Added ammo and health seeking capabilities and fixed some errors. (Read posts below - or file in .zip for full details.)
#61
04/04/2005 (6:52 pm)
That's pretty much the idea behind it.

The attention level thing was actually an idea I borrowed from the guy who wrote the 3DGPAI1 book. I held on to it though because I originally had thoughts of making a game that was single player oriented with large maps, and the attention level aspect would keep the inactive players that were too far away from the main character from chewing up processor cycles.
#62
04/05/2005 (3:38 am)
I spoke with Stephen Zepp, who insisted that the schedule is a very lightweight functionality, and ensured me that 200 schedules is not a big deal at all and wouldn't really effect performance. If this is true, do you think it is better to stay away from them or use them? He thinks that my aiController harms performance more then it helps it do to the extra overhead. I am hoping that there is no real difference in performance, as I can choose the method that is easier to impliment.

http://www.garagegames.com/mg/forums/result.thread.php?qt=28423
#63
04/06/2005 (6:00 pm)
To all, I was getting the console spamming with
starter.fps/server/scripts/aiguard.cs (704): Unable to find object: '' attempting to call function 'getPosition'
starter.fps/server/scripts/aiguard.cs (711): Unable to find object: '' attempting to call function 'getPosition'
so I went a digging, and this simple fix seems to fix it, I'm not 100% why it was the way it was maybe Mark (thanx by the way) can shed some light?
ok when you add to the player.cs there is a line of code that reads
%obj.delaybeforerespawn(%obj.botname, %obj.markerpos, %obj.marker);
however digging in the aiGuard.cs the
function GuardPlayer::OnDamage(%this, %obj, %delta) function calls the same function but with a difference (can you spot it?),
%obj.delaybeforerespawn(%obj.botname, %obj.marker);
Ok so I changed the call in the player.cs to be
%obj.delaybeforerespawn(%obj.botname, %obj.marker);
and that has seemed to of fixed it, I think it will affect the aiPatrol as well but havn't installed that yet so I can't tell
Hope this helps
#64
04/07/2005 (3:35 am)
I had no problems. Did you follow the directions to the letter?
#65
04/07/2005 (1:51 pm)
Hi Guys, I haven't been following this in a while because I was working on another end of Torque (and was hoping someone would come up with a fix for the respawn/console problem).

@Chris Jones-Gill
Chris is was sure you'd nailed it because when I modified the player.cs the cosole errors went away. Then the AI Guards respawned, and respawned and respawned. At fourty or fifty AI Guards Torque ground to a halt. But I do think you are dead-on in regards to the location in the scripts of the problem, the reason I think this is because I believe I've fixed it, but I've only tested it for about twenty minutes. I'll try to explain what I did because (like my son always gets after me for not doing) I didn't keep track of the changes I made to the code. Here goes, what I believe I did was in the player.cs script where it was (I think):


if (%obj.getState() $= "Dead")
{
if (%obj.isbot == true)
{
if (%obj.respawn == true)
{
%obj.delaybeforerespawn(%obj.botname, %obj.markerpos, %obj.marker);
%this.player=0;
}

I changed to:


if (%obj.getState() $= "Dead")
{
if (%obj.client == true)
{
if (%obj.respawn == true)
{
%obj.delaybeforerespawn(%obj.clientname, %obj.markerpos, %obj.marker);
%this.player=0;
}

The reason I thought this might work (and it seems to) is that there is a respawn call already in the AIGuard.cs script, right? I think that is was duplicating it somehow. With this change, my bots respawn ok, ( and only one for each respawn point) and my player respawns ok and there is no "ghost" spawn below the terrain level or console errors. Like I said, this is probably a sloppy, stupid and totally wrong hack since I have no clue as to what I am doing, but it seems to be working.
Like to hear anyones input.
#66
04/07/2005 (3:43 pm)
Hi chaps,
@Chris Labombard Yep followed to the letter checked & rechecked, as a php mysql programmer i always check for my typo's as nobody's perfect LOL
@ Alan LOL I noticed the repawn of death issue as well, makes the game more harder I suppose, anyway i did a similer fix as yours before I poped in here today,
what I did is below if it helps anybody,
function Armor::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
   if (%obj.getState() $= "Dead")
      return;
   %obj.applyDamage(%damage);
   %location = "Body";

   // Deal with client callbacks here because we don't have this
   // information in the onDamage or onDisable methods
   %client = %obj.client;
   %sourceClient = %sourceObject ? %sourceObject.client : 0;

   if (%obj.getState() $= "Dead"){
        if ((%obj.isbot == true) && (%obj.respawn == true)){
          // %obj.delaybeforerespawn(%obj.botname, %obj.markerpos, %obj.marker);
	  // %obj.delaybeforerespawn(%obj.botname, %obj.marker);
           %this.player=0;
    } else {
      %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);     
      }
      }  
}
I tested this with 3 Guard Bots and 2 Patrol bots, I get no console errors when a Guard Bot kills me,
unfortunatley the only console error I get is when a Patrol bot kills me, that is
starter.fps/server/scripts/aiPatrol.cs (446): Unable to find object: '0' attempting to call function 'getPosition'
now there is a sort of fix on http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6742 but that applies to the aiPlayer.cs, which is for the none C++ version, and logic dictates do the same in the aiPatrol.cs, but this has been done already, I'm a bit stuck with that error, but i'm sure it's only due to the fact that there are no other "Real" players just me and I died and the patrol bot cannot find anyone else.
#67
04/08/2005 (3:43 am)
I don't understand why you two get these errors and I don't. Is it possible that for some reason my bots are ghosting as well, but not throwing errors?

What exactly is happening? The bots are double respawning? That might be why I dont get them, I always disable respawning.
#68
04/08/2005 (4:58 am)
Hello Chris L,
Yep I did a quick test today with respawning set to false, and all the code put back to how it was intended, and I didn't get the double respawn or any console errors,
When I first ran into the problem I put an echo statement into both the aiGuard.cs in the function delaybeforerespawn and with respawning on that function got hit twice, when only 1 bot was killed,
so that would basicly start another schedule to respawn another bot, this is due to both player.cs Amor::Damage calls it as well as in the aiGuard.cs GuardPlayer::OnDamage when a single bot dies,
hence the double spawn, the console errors when a Guard bot respwans where due to a mismatch in function calling as per my post on the 27th
so conclusively the error only happens if you have respawn set to true, and the above fixes should work,
#69
04/11/2005 (1:01 pm)
Along the lines of what Chris J and Chris L were doing to fix the respawning issue. All I did was, where you see
%client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
in Armor::damage in player.cs, I surrounded it with a simple if statement.
if (%obj.getState() $= "Dead" && %obj.isbot != true)
   {
         %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
   }

This seems to be working alright for me so far. The call chain looks like
Armor::damage calls applyDamage (before current bot respawn)
applyDamage
setDamageLevel
onDamage
return to Armor::damage which places you still before the current bot respawn

and since
%obj.delaybeforerespawn(%obj.botname, %obj.marker);
%this.player=0;

gets called in onDamage of aiguard.cs, there is no need to do either of those lines again.
#70
04/11/2005 (5:26 pm)
It is simple to fix it...
#71
04/16/2005 (12:20 am)
Thanx guyz,
Successfully, I removed all the errors and its working perfect now.
Once again, Thanx.

Ali Shikla
#72
04/16/2005 (2:28 am)
Hey,
I'm running the AIGuard script however i have changed it so that every AIGuard is replaced by AIPlayer and am trying to use it as the only AI so AIPlayer does not exist and no engine modification are required.
I'm running it on a modified version of Emaga 5 from 3DGPAi1 and in the editor I am totally unable to place AIPlayerMarker. It does not show up unless I change it to an Item. I can then place them but they do not become replaced by bots when the game is run.
Is there any reason as to why the bots do not place and why I'm unable to place a StaticShape datablock version of the AIPlayerMarker (previously AIGuardMarker).

Any help would be appreciated, thanks
#73
04/16/2005 (11:02 am)
Are you still executing AIPlayer.cs? or just AIGuard.cs?
#74
04/16/2005 (9:55 pm)
I'm just executing AiGuard.cs but it's fixed now. I wasn't executing staticShape.cs, which has the creation code for static shapes.
I still have a slight problem which I'm assuming has to do with the fact that I'm not executing AIPlayer.cs, my bots don't fade or play a death a animation. I tried a onDisabled function but it was never called.
#75
04/19/2005 (11:02 am)
I have done everything the resource said todo but no player spawns why?
I have also checked the post way above by someone having the same problem as me but the reply did not work.
#76
04/19/2005 (11:16 am)
Do you have console errors? Are you executing AIGuard.cs? Are you still executing AIPlayer.cs? Place echos in the load to see if they are spawning... Did you place AIMarkers in the map?
#77
04/19/2005 (11:36 am)
@James
Did you set the $AI_GUARD_ENABLED to true?
#78
04/19/2005 (11:46 am)
@ chris
I have so many console errors I cant list them all here but they are mostly 'cannot find file' however another aiplayer spawned from aiplayer.cs works so there shouldn't be any problems with this one. I am executing both files and I dont know how to echo although I know what it means

@ Todd
Yes I have set $AI_GUARD_ENABLED to true
#79
04/19/2005 (12:21 pm)
I have just added aipatrol and I can see the patrol guy but he wont shoot and I cant shoot him
#80
04/19/2005 (2:05 pm)
@James - Perhaps you should try messing with some simpler code. Changing values to see what they do and what not before trying this out...

echo("My name is Chris");

Sorry... but you need to at least have some understanding of what is going on in the scripts... Do a little research. Try tracing through the scripts...

What IDE are you using?