Game Development Community

Improved AI Guard Unit

by Twisted Jenius · 08/25/2008 (7:20 am) · 167 comments

Code File Downloads: TGE 1.5.2 Version and T3D 1.1 Version. If you are using TGEA, download the TGE files and then see comments #8 and #9 below for the necessary changes you need to make.

Update: The new T3D port of this resource already includes the "final update" posted in the comments as well as updated instructions. However it does not include any additional features, such as those found in The Universal AI Starter Kit.



Improved AI Guard Unit


First off I want to give credit to the 3 resources that I based most of this resource's code on.

AI Guard Unit
A-Star Guard
Artificial Intelligence Beginning

I'd recommend checking those resources (especially AI Guard Unit) if you have any questions or problems before posting here, because they already have a lot of good feedback and answers in their comments.



Now down to business. What's different about this resource from those? In addition to having all the great features of the AI Guard Unit resource, it also has the following new features and changed behaviors when compared to that resource (some of which are taken from the other 2 resources listed):

-Unpathed bots pace while in an idle guarding state.
-After killing or losing track of the player, the bots can either return to their spawn point or stay near the location where they last saw the player alive.
-Individual bots can be either pathed or unpathed; multiples of both kinds can be used at the same time from this one resource.
-Pathed bots are still able to move away from their paths while attacking and then return to their path once they are done (returning to the closest node).
-The bots are able to detect players better after the bot has been sniped.
-The bots won't keep bumping into you while they're attacking (with 2 different range settings for that).
-Vastly improved ability to follow the player around corners and other obstacles.
-You are able to use different datablocks and weapons for different bots.
-Trigger based spawning in addition to loading them all at the start.
-No engine modifications are needed.
-Line of sight tests and correct facing while sidestepping obstacles and dodging attacks.
-Various bug fixes and other improvements.
-Lots of easy to change variables (conveniently located at the top of aiPlayer.cs) with well commented code. Most variables can be set to 0 to turn off features that you may not be using.
-Compared to any of the resources whose code this was based on; this AI is a lot more life like and challenging to battle with, while being even more customizable.



INSTALLATION

These instructions are for TGE 1.5.2. For TGEA instructions please check the comments.

1. In the file game.cs function startGame() replace this-

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

With this-

AIPlayer::LoadEntities();

2. In the file player.cs function Armor::damage() replace this-

// 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);

With this-

// 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.marker);
            %this.player = 0;
         }
      }
      else
      {
         %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
      }
   }

3. Replace your old aiPlayer.cs with the new one and just place aiPlayerDatablocks.cs in the same folder.
4. Load your map - Stronghold as an example.
5. Go into the map editor (F11). Then go into the Editor Creator (F4).
6. Under Shapes there should be a new drop down called AIMarker, under that a new item called AIPlayerMarker.
7. Create a new AIPlayerMarker.
8. Select your marker, position it where you like and hit (F3) to modify the marker.
9. If you want to override any of the marker's default values, create a dynamic variable with the appropriate name and value (see below for details on what those are).
10. Update your item by clicking 'APPLY' - very important and easy to miss step.
11. Save your mission and reload it.



ADD-ONS

If the dynamic variables listed below sound too complicated or like too much work, check out the new AI Marker Editor resource instead of messing with all of that. The AI Marker Editor resource is a GUI based editor to help simplify creating, editing and managing your spawn markers. If you install this resource, you can forgo steps 6-9 in the above instructions by pressing "alt a" before step 5 and using the editor.

Also check here for a bug fix update (these fixes are already in the T3D version download).



DYNAMIC VARIABLES

NOTE: If you don't want to override a variable's default value, just don't add that variable to the marker. You should not assign a value which is already the default value to a variable.

All of the variables that can be set on the spawn marker itself and their values (more details below):
pathname	path name
respawn		"true" or "false"
range		"ranged"
doesReturn	"guard"
block		datablock name
Weapon		weapon name
spawnGroup	number


pathname
Set this to the name of the path that this bot is to follow. You have to place a path with nodes in your mission and give it a corresponding name to have the mob go on that path. If you wish for the bot to be unpathed, don't add this variable and the bot will default to unpathed. As an example, to make the bot pathed when using the Stronghold mission, create a new dynamic variable called 'pathname' and set its value to 'path1'.


respawn
If you want to override the default respawn value, create a dynamic variable called 'respawn' and set it's value to either 'true' or 'false'.


range = "ranged";
This variable can be used to differentiate between bots with different weapon ranges. For example melee only bots and bots with ranged weapons; or ones with sniper rifles and rocket launcher versus ones with assault rifles and other small arms. If you don't wish for a mob to be considered as ranged, don't add this variable to the marker.


doesReturn = "guard";
This sets whether the bot returns to its spawn point or stays near the location where it last saw the player, after killing or losing sight of the player. This can be set differently for each bot. For unpathed bots the default is for the bot to go to the player's last location. When "doesRetun" is set to "guard" for unpathed bots, that particular bot will return to its spawn point (much like in the original AI Guard Unit resource). For pathed bots the default is for the bot to go back to its path. When set to "guard", after the bot has lost sight of or killed the player, the bot will then operate like an unpathed bot that is not set to "guard".


block
A few things need to be set up before you can use different datablocks (characters for your AI). First, the model has to exist under data/shapes with all the necessary .dsq and .dts files. Second the new character needs to have a definition script in /server/scripts/ (eg. player.cs, adam.cs, kork.cs or skeleton.cs). Next that definition file must be called in onServerCreated() in game.cs.

Then you have to copy, paste and change the names of the first 2 functions in aiPlayer.cs which are DefaultPlayer::onReachDestination() and DefaultPlayer::OnDamage(). Changing "DefaultPlayer" in the function's name to whatever the name of your datablock is. After that, add the datablock to aiPlayerDatablocks.cs making sure that the name of the datablock is unique, and the body type is valid (eg. datablock PlayerData(UniqueName : ValidBody)). If all that is set up correctly, just add a block = "UniqueName" parameter to your AIPlayerMarker and you should be ready to go.


Weapon
Setting up different weapons is very similar to using different datablocks:
* The model has to exist in data/shapes
* A Script has to be made to handle the weapon (eg. crossbow.cs)
* That script must be called in onServerCreated() in game.cs

Where it differs is in adding the datablock. For weapons, you don't have to add the datablock into aiPlayer.cs or aiPlayerDatablocks.cs. You should just be able to add a Weapon = "myNewWeapon" parameter to your aiPlayerMarker.



SPAWNING BY TRIGGER

spawnGroup
A bit of setup is needed to spawn by a trigger. First, you have to place a triggerObject down. You can name it whatever you want, but you have to specify a few things:
* dataBlock = "guardTrigger";
* spawnGroup = "number";

You have to specify a spawnGroup for each trigger, and then add a matching spawnGroup parameter on each AIPlayerMarker you want to spawn in that group. Once that's done, comment out AIPlayer::LoadEntities(); in game.cs and you should be good to go.



CHANGE LOG
* August 27, 2008: Added the doesReturn dynamic variable and another line of sight improvement.
* October 9, 2008: Further improved the bots ability to turn corners.
* October 14, 2008: Fixed the premature firing bug and changed a few of the default values.
* October 15, 2008: Modified the item retrieval functions a little.
* November 3, 2008: Commented out the premature firing bug fix I made on October 14. While it did fix that bug, it could cause another (worse) firing bug.
* November 4, 2008: Re-fixed the premature firing bug in a slightly different manner so that it should no longer cause other problems.
* November 12, 2008: Fixed a different kind of premature firing bug.
* November 20, 2008: Added the changes posted in the comments by Rex (originally written by Dale Harper).
* November 21, 2008: Fixed a rare pacing bug.
* November 28, 2008: Minor change to one of the movement functions.
* January 16, 2009: Fixed an error in the usage instructions. The dynamic variable that I previously had listed as 'weapRange' should have just been 'range' (this is now corrected).
* September 23, 2009: Posted the final update I will be making for this resource.
* November 29, 2009: I am no longer giving any support for this resource at all.
* August 28, 2010: Added the T3D port of this resource.



If you enjoy using this resource, you should consider purchasing The Universal AI Starter Kit. It has been updated with many new features, bugs fixes and performance enhancements, along with better documentation, an easier installation process, and the code has been more modular and customizable. Check The UAISK's product page or my blogs for more information.

About the author

Developer of The Universal AI Starter Kit, which is available in the Add-ons store on this site. Also working on the game Twisty's Asylum Escapades.

Page «Previous 1 2 3 4 5 6 7 Last »
#1
08/25/2008 (7:37 am)
Looks Sweet
#2
08/25/2008 (10:18 am)
This is awesome! I'm giving this a try right away. Thanks TJ! :)
#3
08/30/2008 (7:01 pm)
Great.. thanks budy..
#4
09/02/2008 (8:41 am)
this is great, worked just fine. thanks
#5
09/20/2008 (7:00 pm)
thanks a lot for sharing
#6
09/21/2008 (1:13 pm)
This is a great start for AI. A while back I had modified the AIGuard to allow for Multiple bot types, but this works so much nicer than what I had. It's also nice because it combines functions of th AIGuard with AIPatrol.
What I was really looking for was an idea on how to make Factions in the game and allow some of the placed AI to be friendly to the Player while others were enimies. At the same time, I wanted "Good" bots to be able to fight "Evil" bots rather than the bots only targeting the Player Character. It took a little work, but I was able to modify this code to do just that. Overall, this is a great place to start for anyone looking for a functional AI system for their game.
#7
10/10/2008 (12:11 pm)
Slick as Butter!!! Very cool resource extremely useful.
#8
10/24/2008 (1:00 pm)
If any one is implimenting this I just want them to know its really straight forward.
I was using a stock TGEA 1.7x version and the T3d Starter example.

There are a few things you need to know to get this to work..


1. Follow all the instructions.

2. In game.cs I had to find
// Load our default player script
   exec("./players/player.cs");

//- - -and after it add.....
exec("./aiPlayer.cs");

3. in AIPlayer .cs find
shapeFile = "~/data/shapes/player/player.dts";

I had to change mine to

shapeFile = "~/data/shapes/players/Spacesuit/Spacesuit.dts";


4.in aiPlayerDatablocks.cs I changed
datablock PlayerData(DefaultPlayer : PlayerBody)

to
datablock PlayerData(DefaultPlayer : DefaultPlayerData)
(Im sure i will have to modify this slightly but it atleast got it up and running.)

if your using the T3D sample you will be missing some other things.
so load up game.cs


5. in games.cs in funtction startGame() right after
// Start the game timer
   if ($Game::Duration)
      $Game::Schedule = schedule($Game::Duration * 1000, 0, "onGameDurationEnd" );]
   $Game::Running = true;

//- - - -//--add in
[i] AIPlayer::LoadEntities();[/i]

6. find ...
function endGame()
{
   if (!$Game::Running)  {
      error("endGame: No game running!");
      return;
   }
//Add in this.......
// Stop the AIManager
   AIManager.delete();
   //--End--
....leave the rest


7. I had to do something slightly different from the instructions
since I was using a TGEA 1.7x T3D version in regards to the Player.cs

In the S E C O N D instruction you are asked to

2. In the file player.cs function Armor::damage() replace this-

I found
function PLAYERDATA::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
   if (%obj.getState() $= "Dead")
      return;
   %obj.applyDamage(%damage);
   %location = "Body";
//--_REMOVED This ---
   // 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);
//--Remove That

Removed it and replaced it with
function PLAYERDATA::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
   if (%obj.getState() $= "Dead")
      return;
   %obj.applyDamage(%damage);
   %location = "Body";
//--REplaced it with this--
   // 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.marker);
            %this.player=0;
         }
      }
      else
      {
         %client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
      }
   }
//ReplaCEd WITH tHIS
}

I followed the rest of the instructions.


Works really well, and its cool running away from the AI Spawn

Hope this helps anyone USing TGEA 1.7x with T3D.


-Surge.
#9
10/24/2008 (7:08 pm)
Thanks for the TGEA install instructions. I'm sure that will help someone out because I've already gotten a question about it (but I couldn't really answer the question because I don't currently have access to TGEA).

EDIT: You shouldn't need to add in AIManager.delete(); when doing a TGEA install (meaning step 6 in the instructions above is unnecessary). In fact even when integrating this resource with TGE, you can delete those lines because AIManager is not used by this resource.
#10
10/25/2008 (6:40 am)
Great Resource Twisted Jenius, Im working through the trigger spawning today so
I will post anything different for TGEA if I come across it.
#11
11/19/2008 (4:03 pm)
This is the best 'out of box', scripted AI the Community has to offer! I used the original Mark H. "AI Guard" resource as scripted AI, but could never get multiple weapons, across multiple bots. With this improved resource it is outlined and I tried a very quick implementation but alas, there were 'issues'.

The major difficulty with the standing resource guide on adding a weapon datablock to the marker was in the AIPlayer::Spawn function. Here, the following code sets all bots weapons if one bot has a dif. weapon:

if (%obj.Weapon !$= "")

              {

                $AI_GUARD_WEAPON = %obj.Weapon; 

              }

This was producing the effect of confusing my separate bots as to which weapon ammo they should be looking for...I had 3 different datablocks, the original Default, SlowerPlayer and my own KorkPlayer as the 3rd. The Default bots were given a dynamic 'weapon' of Chaingun, and KorkPlayer was not specified and thus should have been given the default, 'crossbow' datablock. This was not happening. KorkPlayer was also receiving the chaingun weapon!!

The fix requires replacing the $AI_GUARD_WEAPON global variable with a bot specific variable in the %player = new AIPlayer() section of the AIPlayer::spawn function. Then changing all instances of $AI_GUARD_WEAPON to our new bot specific variable.

This involved going thru several functions, any containing the string literal of "$AI_GUARD_WEAPON" and changing it a localized variable, in the ::Spawn function.

I'll try to outline this and hope my formatting skilz are there; the original author, Dale Harper, graciously allowed me to reprint it here.

In Function:

function AIPlayer::spawn(%this, %name, %obj, %block)

In Block:

%player = new AIPlayer()

Change This:

oldpos = %obj.getposition();

To This:

oldpos = %obj.getposition();

            botWeapon = $AI_GUARD_WEAPON;         //DH Added for bots use dif. weapons




Note: botWeapon is now a bot specific variable indicating bot's weapon.



Change This: // just below previous change but out of new AIPlayer block

if (%obj.Weapon !$= "")

            {

                $AI_GUARD_WEAPON = %obj.Weapon; 

            }

To This:

if (%obj.Weapon !$= "")

            {

            %player.botWeapon = %obj.Weapon;         //DH Added for bots use dif. weapons  

            }



Note: If there is a Weapon dynamic variable set in the spawn marker datablock this

code assigns that weapon to our bot specific variable.


In Function:

function AIPlayer::openfire(%this, %obj, %tgt)

Change This:

if($AI_GUARD_ENDLESS_AMMO == true)

            {

                %obj.incinventory($AI_GUARD_WEAPON @"Ammo",100);

            }

To This:

if($AI_GUARD_ENDLESS_AMMO == true)

            {

                %obj.incinventory(%obj.botWeapon @"Ammo",100);

            }

....more to follow.
#12
11/19/2008 (4:15 pm)
....and continuing into further functions and reassigning the Global Variable to prevent the lockout for a different weaponType......


In Function:

function AIPlayer::EquipBot(%this, %obj)

Change This:

//This adds a weapon to the bots inventory.

            %obj.incinventory($AI_GUARD_WEAPON,1);

To This:

//This adds a weapon to the bots inventory.

            %obj.incinventory(%obj.botWeapon,1);

Change This:

//This mounts the weapon on the bot.

            %obj.mountImage($AI_GUARD_WEAPON @ "Image",0);

To This:

//This mounts the weapon on the bot.

            %obj.mountImage(%obj.botWeapon @ "Image",0);

Change This:

if($AI_GUARD_WEAPON_USES_AMMO == true)

            {

            //This sets the bots beginning inventory of ammo.

                %obj.setInventory($AI_GUARD_WEAPON @ "Ammo",4);

                }

To This:

if($AI_GUARD_WEAPON_USES_AMMO == true)

            {

            //This sets the bots beginning inventory of ammo.

                %obj.setInventory(%obj.botWeapon @ "Ammo",4);

                }



Note: if you are allowing a bot to use a pray and spray weapon, you may want to

use the following code to allow the bot more ammo. Just change chaingun to the

name of your automatic weapon.




if($AI_GUARD_WEAPON_USES_AMMO == true)

            {

            //This sets the bots beginning inventory of ammo.

                if(%obj.botWeapon $= "chaingun")

                  %obj.setInventory(%obj.botWeapon @ "Ammo",20);

                else

                  %obj.setInventory(%obj.botWeapon @ "Ammo",4);

                }



In Function: // near top of function before switch/case statments start

function AIPlayer::Think(%this, %obj)

Change This:

if(%obj.getInventory($AI_GUARD_WEAPON @ "Ammo") == 0)

            {

                %this.enhancefov(%obj);

                %ammostr = $AI_GUARD_WEAPON @"Ammo";

                %i_ammo= %this.getclosestiteminsightandrange(%obj, %ammostr );

                        if(%i_ammo > 0)

                        {

                            %obj.action="GetAmmo";             

                        }

            }

To This:

if(%obj.getInventory(%obj.botWeapon @ "Ammo") == 0)

            {

                %this.enhancefov(%obj);

                %ammostr = %obj.botWeapon @"Ammo";

                %i_ammo= %this.getclosestiteminsightandrange(%obj, %ammostr );

                        if(%i_ammo > 0)

                        {

                            %obj.action="GetAmmo";             

                        }

            }



In case statment:

case "GetAmmo":

Change This:

%ammostr = $AI_GUARD_WEAPON @"Ammo";

To This:

%ammostr = %obj.botWeapon @"Ammo";





Change This: Near end of Think function

%objname= %obj.action @ ":"@ %this.attentionlevel @ ":" @ %obj.getdamagelevel() @ ":" @ %obj.getInventory($AI_GUARD_WEAPON @ "Ammo") ;

To This:

%objname= %obj.action @ ":"@ %this.attentionlevel @ ":" @ %obj.getdamagelevel() @ ":" @ %obj.getInventory(%obj.botWeapon @ "Ammo") ;


I made the above changes and it sorted my multiple bots now having a correct choice of weapon and it's proper ammo! Thanks Dale!

I hope I set this up proper here, to not set anyone astray...Enjoy!

Rex
#13
11/19/2008 (8:42 pm)
Very nice.
Works Great
#14
11/20/2008 (12:29 am)
@Rex

While I did originally forget to test giving bots different weapons, I have now briefly tested that feature. And from my testing, it seems that the original code works fine but the code you supplied is bugged. For me, your code is only allowing the bots to spawn with the default weapon and any attempt to change their weapon is being ignored. I did my testing on TGE 1.5.2 that been has fairly heavy modified, but nothing that should interfere with this.

I do highly encourage bug reports from all community members (especially when you also have a fix for that bug), and if we can find something that works for both of us I'll gladly update this resource to reflect that.
#15
11/20/2008 (6:35 am)
I use a totally stock TGE SDK....and this was a scripted solution, and it works!?!, not so sure about 'bugged'?? I have also injected a 'deathSelection' routine, but again, it's all scripted.....but this moved code from the onDisabled and moved it into Armor::Damage, as the original script suggests?!?

What MOD's have you made, Jermaine? Any Source changes?

I've also got the markers to 'name' the generated bot as well.....all scripted, all working??????
#16
11/20/2008 (5:13 pm)
Ok, my mistake. I retried the changes you suggested again and it turns out the reason it was bugged for me the first time was because of a typo I made. Now both your version and the old version work fine for me. But since the old version didn't work for you, I've now updated this resource with your changes.
#17
11/29/2008 (2:19 am)
wow! quite hectic having the orc suddenly decide that you might be a tasty morsel to kill instead of running around in a big silly loop. brought a huge smile to my face hiding and running from him. EXCELENT RESOURCE!!!
#18
11/29/2008 (5:14 am)
just one question: how do I change the aiPlayer model? I dont want the aiPlayer and the player to use the same models.
#19
11/29/2008 (9:09 am)
ok, this is too wierd!! I had a vehicle in the scene, an aiPlayer started harrasing me, so I shot him. now he was standing in front of the vehicle, and when I shot him, he reeled backwards, came into contact with the vehicle, mounted it, and began chasing me while "driving" the vehicle!!!!!

I'm trying to replicate this again. its freaky, someone try this and let me know!!

BTW I'm using TGEA 1.7.1

YES!! replicated it, he's not a very good driver though, gonna catch this on video, will upload shortly.
its brilliant.
#20
11/29/2008 (10:25 am)
heres the video

http://www.youtube.com/watch?v=Mfc3UzU867g

.
Page «Previous 1 2 3 4 5 6 7 Last »