UDN
Search public documentation:
ArtificialIntelligenceReference
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
Artificial Intelligence Reference
Last updated by Chris Linder (DemiurgeStudios?) for creation. Original author was Chris Linder (DemiurgeStudios?) Please feel free to contact me with questions or comments at chris@demiurgestudios.com.Introduction
This document will go over AI basics in the Unreal engine. It will cover the classes involved with AI as well as useful UnrealScript functions and how to use these function to create both scripted and dynamic AI. This document will also go over the control flow in ScriptedController as an example of how to use movement functions in latent code. This document will also go over how to use states and events to control behavior using Bot as an example.Hints
These are a few hints that might help you create your own AI or understand an existing AI. I used these techniques extensively to create this document.Use "showdebug"
This is an extremely useful console command that displays a lot of information about the current Viewtarget. It will tell you the state, the MoveTarget, the amount of health of the pawn, its animation data, current frame, and many other things. At first this many not seem useful because your player is the current ViewTarget but it is useful when you press "F5".Use "F5"
This will cycle the ViewTarget between all pawns in the game. This will put a camera behind an AI you want to watch and see what it is doing. This command is very useful when used with "showdebug".Use the UnrealScript Debugger
The UnrealScript Debugger is a great help when trying to figure out the flow of code in an AI. This will allow you to set break points anywhere in script including latent code. You can then step though the code and see what is happening. The debugger also lets you watch variables, view the call stack, and perform many other debugging actions.How to Control a Pawn
Pawns can be controlled by two types of Controllers, PlayerControllers and AIControllers. Or they can just sit there but that is sort of dull and we won't talk about that. We will also not talk about the PlayerController case in this document. Instead we will focus on AI controlling a pawn and how AIControllers can be used. AI can be divided basically into two types; AI that does a predefined script of actions, and AI that reacts dynamically to its environment. There is clearly a fuzzy boarder between these two but the distinction is useful at least for this document. It is important to note that there is not a class hierarchy branch for these two types of AI. The same class (Bot for example) will often be capable of doing both type; what matters is how you set up your controller and pawn. If you want an AI to do a script of actions you should use an AIScript of some sort which you place in Unrealed and link to a specific pawn or set of pawns. If you want an AI that will respond to its environment and make choices and not need to be configured in Unrealed, you should set the ControllerClass in defaultproperties of the pawn you want to control. The ControllerClass should be some subclass of AIController but not necessarily a direct subclass. Bot is an example of such a controller and it is the default ControllerClass for all UnrealPawns.Controller and the Great Functions You Need
Controller is the base class for actors that control pawns. This class is implemented in Controler.uc and UnController.cpp. I will be talking mainly about the script interface and the native functions that are accessible from script. These functions are very useful for writing both scripted AI and dynamic AI.Non-Latent Functions
FindPathTo
Actor FindPathTo(vector aPoint) This function returns the path node that is next in the sequence of path nodes that will lead to aPoint. Given that this function only returns actors, if can not be used on the final leg of your journey to the given arbitrary point. This function will return the path node nearest to the given point on the last step of the journey. Consider using PointReachable to see if you can get to the point and then simply call MoveTo. Beware of calling FindPathTo too often because it is very computational expensive given that is re-computes the entire path every time you call it even if the path has not changed. In most cases though, you will need to call this function for every path node you encounter on the way to your destination. After you call this function is it traditional to call MoveToward to actually move towards to the given actor.FindPathToward
Actor FindPathToward(actor anActor, optional bool bWeightDetours) This function is much like FindPathTo but it gives you a path to a given actor. Consequently it does not have FindPathTo's problem of not being useful for the last part of the journey. If the target actor is not a path node this function will return that all the nodes up that actor and then return that actor. Sometimes this has undesired results; for example if the actor you want to reach is standing right next to a path node this function will take your pawn to the path node first, then the actor even if the actor is closer to your pawn than the path node. You can fix this by using the ActorReachable function to limit the calls to this function as well as make your pawns behave more logically. Beware of calling FindPathToward too often because it is very computational expensive given that is re-computes the entire path every time you call it even if the path has not changed. In most cases though, you will need to call this function for every path node you encounter on the way to your destination. After you call this function is it traditional to call MoveToward to actually move towards to the given actor.FindRandomDest
NavigationPoint FindRandomDest() This function returns a random NavigationPoint.PointReachable
bool PointReachable(vector aPoint) This returns true if a direct path from your pawn to aPoint is traversable using the current locomotion method.ActorReachable
bool ActorReachable(actor anActor) This returns true if a direct path from your pawn to anActor is traversable using the current locomotion method.LineOfSightTo
bool LineOfSightTo(actor Other) LineOfSightTo returns true if controller's pawn can see Other. Checks line to center of other actor, and possibly to head or box edges depending on distanceCanSee
bool CanSee(Pawn Other) CanSee returns true if LineOfSightTo object and it is within creature's peripheral vision.FindPathTowardNearest
Actor FindPathTowardNearest(classFindPathToIntercept
Actor FindPathToIntercept(Pawn P, Actor RouteGoal, optional bool bWeightDetours) This function appears to do that same thing as FindPathToward.Latent Functions
Latent functions stop the flow of latent state code until they "return". Every latent function in script has two latent functions in C++. These functions are execFunctionName() and execPollFunctionName(). execFunctionName() is called when the function is first called from script in latent state code. Every subsequent tick, execPollFunctionName() is called until this function sets GetStateFrame()->LatentAction = 0. This will cause the latent function to "return".MoveTo
latent function MoveTo( vector NewDestination, optional Actor ViewFocus, optional bool bShouldWalk) First, MoveTo sets up the controller and the pawn to move. Destination is set based on NewDestination, walking is set if bShouldWalk is true, and Focus of the controller is set to ViewFocus if it given and the Destination otherwise. The MoveTimer is also set by calling Pawn->setMoveTimer(...) based on the distance to the destintation. This timer is decremented in performPhysics(...) and when it runs out MoveTo will return. This is so that if the pawn gets caught on something or is knocked off the path by something and can not reach its destination, the latent code does not hang here forever. Once all the move info is set up, Pawn->moveToward(...) is called which does the actual moving of the pawn. Pawn->MoveToward(...) is also called every tick from execPollMoveTo(...) until MoveTo returns. MoveTo will return when the either the pawn reaches the destination or the destination is determined to be unreachable or when the MoveTimer runs out.MoveToward
latent function MoveToward(actor NewTarget, optional Actor ViewFocus, optional float DestinationOffset, optional bool bUseStrafing, optional bool bShouldWalk) MoveToward is very similar to MoveTo but it moves towards an Actor, not a point. MoveToward also special cases moving towards NavigationPoints so that the pawn cuts corners and travels more logically on a set of path nodes. MoveTimer is additionally useful in MoveToward because the destination can be a moving actor that can move out of view. When MoveTimer runs out MoveToward returns control to the latent state code which can find a new path if the target is no longer visible. MoveTimer is set to a short time when the target is a Pawn for this reason. Pawn->moveToward(...) is called when the actual destination is calculated and all the other movement information is set up. Pawn->MoveToward(...) is also called every tick from execPollMoveToward(...) until MoveToward returns. MoveToward will return when the either the pawn reaches the destination or the destination is determined to be unreachable or when the MoveTimer runs out. Pawn->MoveToward is called every tick from PollMoveToward.FinishRotation
latent function FinishRotation() This function will return when the yaw of the pawn is within 2000 unreal rotation units of the yaw of DesiredRotation. This function as well as the corresponding execPollFinishRotation(...) do nothing to effect the rotation of the pawn, this is handled in physicsRotation(...).Events and Notifies
SeePlayer
event SeePlayer( Pawn Seen ) SeePlayer is called whenever this controller's pawn can see another "player". This will be every tick when a player is in view. It worth noting that "player" is any controller with bIsPlayer set to true (Bot for example), not just human players. In most cases once you receive this event you will disable SeepPlayer by calling Disable(`SeePlayer') or switching to a state that ignores SeePlayer.SeeMonster
event SeeMonster( Pawn Seen ) This is just like SeePlayer but with monsters (those controllers with bIsPlayer set to false).EnemyNotVisible
event EnemyNotVisible() This event is called whenever the Enemy is not visible. Like SeePlayer this will be called very frequently unless you switch to a state that ignores EnemyNotVisible or call Disable(`EnemyNotVisible').HearNoise
event HearNoise( float Loudness, Actor NoiseMaker) HearNoise is called when the controller's pawn hears a noise.NotifyTakeHit
function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, classNotifyAddInventory
function NotifyAddInventory(inventory NewItem) This function is called when AddInventory(...) is called on the controller's pawn.PrepareForMove
event PrepareForMove(NavigationPoint Goal, ReachSpec Path) This event is called from C++ if the reachspec for the current movement does not support the pawn's current configuration. PrepareForMove(...) gives the pawn a change to prepare for the move by doing something special. What to do is up to you, however, because this function is not implemented. By default, the pawn will crouch when it hits an actual obstruction. However, Pawns with complex behaviors for setting up their smaller collision may want to call that behavior from here. bPreparingMove is set to true.AIController
AIController extends Controller. This class adds basic support for AIScripts as well as several useful AI functions. This class also includes game specific concepts, such as Skill, that I will not go over.Scripting
The scripting functionality of AIController is not extensive. There is a variable MyScript that contains a reference to the current AIScript. This variable is none if there is no script. Trigger events are passed to this script by calling TriggerScript(...) when the controller is triggered.Movement
bAdjustFromWalls
When bAdjustFromWalls is true, the controller is given a chance to adjust around an obstacle and keep moving. The C++ AAIController::AdjustFromWall(...) function does the actual adjusting from a wall.GetFacingDirection
int GetFacingDirection() This function returns the direction faced relative to movement direction. This means if the pawn is facing the direction it is running, this function will return 0 no matter which way the pawn is running. The function will return an int between 0 and 65535 in the standard unreal rotation scheme (0 = forward, 16384 = right, 32768 = back, 49152 = left).Mover Functionality
AIController has a few functions that deal with movers. These functions include WaitForMover (Mover M), MoverFinished(), and UnderLift(Mover M) which set up the movement of the pawn to deal with movers properly and to use lifts and doors. These functions are called by Movers or NavigationPoints associated with Movers.Misc
Startle
Startle(Actor A) This function is called from the AvoidMarker class which is a type of trigger. Startle is not implemented in AIController but the idea of a startle function is useful. Consider implementing this function and maybe calling it more.DisplayDebug
DisplayDebug(Canvas Canvas, out float YL, out float YPos) DisplayDebug(...) displays useful debug info on the HUD. This information includes such thing as the MoveTarget, Destination, Focus, PendingMover, and RouteGoal. DisplayDebug(...) will be called when bShowDebugInfo is true, which can be toggled by using the exec command "ShowDebug".ScriptedController
ScriptedController extends AIController and adds the ability to get actions from a ScriptedSequence. To examine how ScriptedControllers work, we will look at the control flow when the pawn is being controlled by a script.Control Flow
This is an example of flow of execution when a ScriptedController is controlling a pawn with instructions from a ScriptedSequence which is a type of AIScript. For simplicity this example will only go over actions in the ScriptedSequence that direct the movement.Entry Point
Initially the ScriptedController does nothing. There is no code in any *BeginPlay function that puts the controller on any sort of control path. The control over a pawn starts in the TakeControlOf(Pawn aPawn) function which is called by the ScriptedSequence associated with this controller. This function possesses the given pawn so that it is under the control of this ScriptedController and then goes to state 'Scripting'. State 'Scripting' is where all the control of the pawn happens starting at the state label Begin:.Begin
At the Begin: label the next action is chosen from the ScriptedSequence by calling InitforNextAction(). This function sets the CurrentAction which will be used to determine what to do next. A few things are also done to setup weapons and shooting. Next the Pawn is made ready to move by calling:Pawn.SetMovementPhysics(); WaitForLanding();