[TUTORIAL] AI Fundamentals

Prerequisites:

Skills:
You will need a solid understanding of Unrealscript basics.
If you do not understand the fundamentals, you will not understand the logic behind the code and will experience difficulty adapting it to your own needs.
Beyond the basics, this tutorial assumes no prior experience with AI inside or outside of UDK.

Existing Code:

  • A custom Pawn
  • A custom weapon
  • A custom GameInfo with your custom weapon defined in the default inventory

The goal:
Create basic AI that will:

  • React to seeing player
  • Fire at player with distance-dependent accuracy
  • Only react when player is within range

The concept:
AI Controllers and Pawns:
In UDK, a Pawn is a physical representation of anything that can be controlled (Vehicles, people, turrets etcetera).
A pawn, generally speaking, will never have to be specifically made for a player or an AI controller, since a pawn can be possessed and controlled by either.

A controller, will translate physical input or programmed logic into actions called on the pawn, depending on whether or not it’s a player controller or AI controlled.

A player controller is generally easier to create since it takes most of it’s logic from the player, while an AI controller will do nothing until it is pre-programmed.
AI, or at least the way we have to deal with it right now, is not “intelligent”. It can not “create” any logic of it’s own, it is merely a pre-programmed object that will call functions based on different circumstances to create the illusion that it is intelligent enough to think.

Initial Setup

Class definition:

[MyCustomAIController.uc]


class MyCustomAIController extends UDKBot;
//Extending from UDKBot will make our lives a lot easier

//Now, we must override GetPlayerViewPoint as the default implementation puts the AI's "eyes" in the ground!
simulated event GetPlayerViewPoint(out vector out_Location, out Rotator out_Rotation)
{    
    local vector AI_Location; //Vector variable to apply offset from our location
    AI_Location=Pawn.Location;
    AI_Location.z=Pawn.Location.z+Pawn.EyeHeight; //To re-iterate: Make sure your eyeheight is set up in your custom pawn!
    
    out_Location = AI_Location;
    out_Rotation = Rotation;
}

[MyCustomBot.uc]


class MyCustomBot extends MyCustomPawn 
placeable;
//We can place this bot in the level without using kismet or console commands to spawn

simulated event PostBeginPlay()
{
    Super.PostBeginPlay();
    AddDefaultInventory(); //We don't be able to shoot at player without a weapon!
}

defaultproperties
{
    ControllerClass=class'MyCustomGameInfo.MyCustomAIController' //This will automatically create an AI controller for the bot
}

The Framework: 1.1

http://entrod.sp-website.net/images/UDK/Tutorials/UDKBOT_Accuracy/1.1.png

Fig 1.1
When Player is within range of bot and within field of view, bot will set player as enemy.
Variables:

[MyCustomAIController.uc]


var Actor Target; //Using Actor instead of Pawn adds flexibility to have AI respond to non players EG. Investigating noise from an object thrown by player. Casting is used when checks are done on actual enemies
var float MaxVisibleDistance; //This will determine whether or not we should react to the player

Functions:

[Controller.uc]


event SeePlayer( Pawn Seen );
//We will use this event to run logic based on seeing the player

[Object.uc]


native(225) static final function float VSize( vector A ); //We will use this to determine if the player is close enough to be detected

The Framework: 2.1

http://entrod.sp-website.net/images/UDK/Tutorials/UDKBOT_Accuracy/2.1.png

Fig 2.1
Bot will track player as he moves around.

Variables:

[Controller.uc]


var Actor Focus; //Setting this variable will automatically make our AI (pawn) aim at the player

The Framework: 3.1

http://entrod.sp-website.net/images/UDK/Tutorials/UDKBOT_Accuracy/3.1.png

Fig 3.1
When firing, a random offset to the aim direction is applied based on distance (exaggerated).

Variables:

[MyCustomAIController.uc]


var () float accuracy; //We will use this to determine the distance at which our bot becomes inaccurate.

[Controller.uc]


var editinline repnotify Pawn Pawn; //A reference to the pawn currently controlled by us, used for firing logic
var BasedPosition     FocalPosition; //The point we will be modifying for our random offset

Functions:

[Controller.uc]


native final function Vector GetFocalPoint(); //In order to add an offset from where we are aiming, we must get the point we are aiming at
function Rotator GetAdjustedAimFor( Weapon InWeapon, vector ProjStart ) //This event is called when the weapon is fired, we will override this to add our offset aim

Implementation

1.1:



auto state Idle
{    
     event SeePlayer (Pawn Seen)
     {
          if (vsize(Pawn.Location-Seen.Location)<MaxVisibleDistance)
          {
               SetTarget(Seen); //If player is close enough, set him as our current target
          }
          Super.SeePlayer(Seen);
     }
}

2.1:



simulated function SetTarget(Actor E)
{
     //If we already have a target, don't reset it
     if (Target==none)
     {
          Target=E;  //Handle for the target
          Focus=E;  //Look at the target
          GotoState('Combat'); //Handle shooting at the target now
     }
}

3.1:



state Combat
{
     begin:
     
     if (Pawn.Weapon.HasAmmo(0) && !Pawn.IsFiring())
     {
          Pawn.StartFire(0); //Start firing! If we aren't firing already. If you understand weapons well enough you should replace 0 with a custom variable in your class
     }
     sleep(0);
     Goto('Begin'); //Pseudo tick event
}


function Rotator GetAdjustedAimFor( Weapon InWeapon, vector ProjStart )
{
    local vector AimAdj;
    local float TargetDist;

    AimAdj = GetFocalPoint(); //We must first get the location of our aim before we can offset it

    if ( target != None )
    {
        TargetDist=vsize(Pawn.Location-GetFocalPoint()); //This will determine the distance to the target
        AimAdj.x=RandRange(AimAdj.x-TargetDist/accuracy,AimAdj.x+TargetDist/accuracy); //We will add a random offset based on the distance and our accuracy
        AimAdj.y=RandRange(AimAdj.y-TargetDist/accuracy,AimAdj.y+TargetDist/accuracy);
        AimAdj.z=RandRange(AimAdj.z-TargetDist/accuracy,AimAdj.z+TargetDist/accuracy);
    }
    SetRotation(Rotator(AimAdj - ProjStart)); //Now it will point the gun (or projectile to be accurate) at our new aiming spot
    return Rotation;
}

Closing thoughts
Learning:
I hope you learned something from this (even if rudimentary) tutorial and have a better concept of how ai works in UDK and how to apply behaviour based on circumstantial variables.
While basic, it should give you a solid foundation to expand on with your own logic.

For the future:
To the more adept UnrScript programmers, this tutorial doesn’t really offer much right now, but I do intend to expand on it greatly when I get free time.
So if there’s anybody who already has their core AI in place but wishes to expand, please check in once in a while for updated modules.

Interesting stuff, are there any other taught AI modules out there for UDK?

Mougli Has some really good AI tutorials, as well as non AI tutorials too.

Great stuff, I’m going to start going through these ASAP

You say ‘rudimentary’ but the basics are extremely important in an overview, and yours is another unique perspective of wtf is going on and why! :slight_smile:

I have npc bots wandering around doing the stuff I want so far. I’m working up to the stalking, hunting, even shoot versions which are another difficulty and your overview is terrific. Eyeheight for example, I never knew that was relevant to bots. As you say: your tut “is a solid foundation to expand on with your own logic” Simple but profound. Thanks for making the effort!