| Home Page | Recent Changes

Bot Vision

Bots can snipe through pea-soup fog...

EntropicLqd: I have a problem - my bots can snipe me from a mile away in dense fog. This is somewhat unfair since I can't actually see them (nor they me). I've tried reducing the bot's sight radius but that has had no effect. Anyone have any other thoughts on making the bots only able to see very close targets? I've included the code (from my mutator subclass) I'm using to adjust the sight radius below.

function ModifyPlayer( Pawn Other ) {
    if ( Other.Controller != None ) {
        if ( Other.Controller.bIsPlayer && !Other.IsHumanControlled() ) {
            Log("Sight radius adjusted");
// repEndDist is the end distance of the fog
            Other.SightRadius = repEndDist / 8;
        }
    }
}

I'm still stuck on this one.

ZxAnPhOrIaN: I need that code for my level, wich uses dense fog.

EntropicLqd: The SightRadius is reset within the function Bot.ResetSkill(). This in turn is called from Bot.Possess(), Bot.InitialiseSkill(), and Bot.Restart(). My guess is that the SightRadius value is being reset to it's original value after the ModifyPlayer() function is called. I'll have a go at proving that tonight if I get time.

What are the implicatons of changing the Bot.Default.SightRadius value within the mutator? Will it screw up subsequent normal games or will the value be reset with each new game?

Mychaeel: In Unreal Tournament changes to a class's default properties persisted over level switches. I'm not so sure about that in UT2003 – it would be worth finding out for sure.

EntropicLqd: In UT2003 changes to a class's default properties persist not only over level changes, but also for the life of that instance of UT2003 - the value is not reset until you quit and re-load UT2003. Not only that but the SightRadius has bugger all effect on how far the bots can see to attack things - irrespective of what the code claims. I suspect the real problem lies within the AI attack code being able to pick out targets that the bot's can't see as it were.

Foxpaw: You could make a mutator that would subclass the bots - not the most compatible solution but maybe someone could find a better way. What you really want to do is to modify the Controller's PickTarget function... but you can't do that because it's native and final. So modify whatever calls it. Picktarget can take an argument that gives a maximum range for selecting a target. I'm not sure where it's called from. You could maybe call PickTarget every X amount of time (much more frequently than the bot's normally would) though that would not be very elegant. If you could find when the function is called you could maybe modify that function to pass an arbitrary maximum range to target people through the fog.

Bot Vision - Better than X-Ray

If you've ever spent any time attempting to make a mod that restricts the distance players can see, or their peripheral vision then you'll know that getting the Bots to behave in a believable manner is hard. This page will describe my on-going thoughts and musings on how to get things working in the manner you wish.

Finding Objects and General Navigation

Now, in general a Bot's view of the world is a network of nodes. Some of the paths between the nodes are "more expensive" than others, and a Bot will generally try and take the easiest path to wherever it is trying to get to. Generally, this may be armour, health, a weapon, or another pick up. Left to it's own devices a Bot will quite happily run around a level collecting stuff. The Bot knows where everything on the level is - because it can navigate through the network of nodes to find any object in the level. Remember, the nodes are placed on the level as PathNode objects, Inventory items, and other such stuff.

When a Bot has decided to go somewhere it stores information about it's route through the level in its Controller. This makes sense - the controller is responsible to directing the Bot, and the pawn (representing the Bot in game) is responsible for the animation. This distinction is more or less true. I'm not convinced that it is as cut and dried as that. It's best to think of the Controller as the Bot's brain, and the Pawn as the Bot's representation in the world.

The Controller class has some useful attributes to support this navigation, reproduced below for your pleasure and enjoyment.

var Actor     RouteCache[16]; // A cache of pathnodes the Bot needs to travel along to reach its goal?
var [[ReachSpec]] CurrentPath;
var vector    CurrentPathDir; // Direction the bot needs to travel to remain on it's "path"
var Actor     RouteGoal;      // final destination for current route
var float     RouteDist;      // total distance for current route
var float     LastRouteFind;  // time at which last route finding occured

// Some useful functions for Bot navigation are:
//Navigation functions - return the next path toward the goal
native(518) final function Actor FindPathTo(vector aPoint);
native(517) final function Actor FindPathToward(actor anActor, optional bool bWeightDetours);
native final function Actor FindPathToIntercept(Pawn P, Actor RouteGoal, optional bool bWeightDetours);
native final function Actor FindPathTowardNearest(class<NavigationPoint> GoalClass, optional bool bWeightDetours);
native(525) final function NavigationPoint FindRandomDest();

Seeing Enemies and Shooting at Them

So, how do Bots see other players? Well, there are a few functions and attributes that would appear to affect this part of the Bot's behaviour. In my experience none of them seem to work well in low visibility conditions. I've listed all the ones I can find below.

// From the Controller class
float FovAngle; // The Field of View from the player's (human or bot) point of view

// Enemy information
var Pawn   Enemy;         // A reference to the current Enemy of the bot
var Actor  Target;
var vector LastSeenPos;   // enemy position when I last saw enemy (auto updated if EnemyNotVisible() enabled)
var vector LastSeeingPos; // position where I last saw enemy (auto updated if EnemyNotVisible enabled)
var float  LastSeenTime;

// LineOfSightTo() returns true if any of several points of Other is visible (origin, top, bottom)
native(514) final function bool LineOfSightTo(actor Other);

// CanSee() similar to line of sight, but also takes into account Pawn's peripheral (how big is that?) vision
native(533) final function bool CanSee(Pawn Other); 

function bool WouldReactToSeeing(Pawn Seen)
event HearNoise( float Loudness, Actor NoiseMaker);
event SeePlayer( Pawn Seen );   // called when a player (bIsPlayer==true) pawn is seen
event SeeMonster( Pawn Seen );  // called when a non-player (bIsPlayer==false) pawn is seen
event EnemyNotVisible();


// From the ScriptedController class
function bool CheckIfNearPlayer(float Distance);


// From the Bot class
// Calls the CanAttack() function of the current weapon to see if Other is in range
function bool CanAttack(Actor Other);

event SeePlayer(Pawn SeenPlayer); // Called when a player has been seen
function SetPeripheralVision(); // Set's the peripheral vision of a Bot based on skill

// SetAlertness()
// Change creature's alertness, and appropriately modify attributes used by engine for
// determining seeing and hearing.  SeePlayer() is affected by PeripheralVision, and
// also by SightRadius and the target's visibility HearNoise() is affected by HearingThreshold
function SetAlertness(float NewAlertness);

// Attempts to determine if Bot has line of sight to Target deltatime from now.
function bool CheckFutureSight(float deltatime);

Irritatingly the control for the Bot's "sight" does not solely rest with the Controller. There are some attributes of the Pawn class that have an impact. The most obvious one of these is SightRadius, which is allegdly the Bot's maximum seeing distance. However, as I have proved conclusively the Bot's maximum seeing distance != the Bot's attacking distance.

The SightRadius variable and its effect within the game:

I've experimented a lot with AI and this SightRadius variable while developing AI for CasterMod. I found out, that the suggestions on this page are not completely right about this variable and the AI function. THE SIGHTRADIUS VARIABLE DOES DETERMINE HOW FAR A BOT CAN SEE OPPONENTS.

The Problem:

The real problem here is that, once it has an enemy assigned, it tries to attack if possible. There are as mentioned above a load of functions to tell the bot what it can see. Three of them are of major importance. These are LineOfSightTo(), CanSee() and SeePlayer().

  • LineOfSightTo()
    • LineOfSightTo() sais whether anything (solid as world geometry) blocks the direct view to the enemy. It does not consider FOV or SightRadius of the bot.
  • CanSee()
    • CanSee() goes a step further. It checks the LineOfSightTo() and then checks whether the enemy is in the bots FOV. It still does not use the SightRadius for calculation.
  • SeePlayer()
    • Now in contrast to the two functions above SeePlayer() takes everything , means LineOfSight, FOV and SightRadius into account, but as you can see it is not a boolean function, but an event called natively. What it does is to assign an enemy it sees just now. This can also be done by HearNoise() without being able to see the enemy or by getting hit by an enemy. After that assignment of an enemy the bot only uses LineOfSight() and CanSee() to check whether it can still see the enemy. That is the main problem.

Solutions:

I worked out some solutions which I currently implement into CasterMod. For one thing enemies should only be assigned if the bot does see them, so if it hears a noise it should not take on the enemy that produced it, but instead turn to the source of the noise. If it can now properly see the enemy SeePlayer() gets called by the engine. Problem 1 solved.

Now to keep the bot from shooting at enemies it can't see we have to create a new function for check if it can see them properly. My experiments with this variable and the SeePlayer() function had the following result. SightRadius is the maximum range a bot can see some player with the Visibility==128 (default). So it can see someone with Visibility==255 almost twice as wide.

This results in a simple UScript function for the check:

function bool IsVisibleToMe(Pawn ThisPawn)
{
      local float Distance;         //Distance between my pawn and pawn in question
      local float VisFactor;        //converted visibility of enemy in float ranging from 0-2

      If (!CanSee(ThisPawn))        //view blocked or looking in other direction
             return false;

      Distance = VSize(ThisPawn.Location - Pawn.Location);
      VisFactor = ThisPawn.Visibility / 128;

      If ( (Distance/VisFactor) <= Pawn.SightRadius) //can see enemy
             return true;
      return false;
}

Now you'd have to replace the CanSee() and LineOfSightTo() calls everywhere you feel it's necessary with this function, e.g. before shooting someone. Notice that you may have to go very deeply into the bot code in order to do this.

Then you'd have to change SightRadius accordingly to the conditions in the Zone the bot is in. If there's distance fog in closer range then SightRadius, set SightRadius to fog distance. Anyway, you should change the default SightRadius value from 12000 to sth., well, about 3000-4000. This is about as far away as you could usually notice an opponent in game as a human being without sniperrifle. Hope this contribution helped you understanding how bot vision basically works in Ut200x.:)

(by Hazard.ep)

ZxAnPhOrIaN: Is sight radius how far the bot can see weapons, powerups, triggers, etc?

Wormbo: It would be possible to reset the bot's target to None from an external actor if the distance to the enemy gets too big. The Tick function might be handy (and hacky ;)) here.

UArch would be intresting to expand this and add light/shadow detection into it, could be useful for stealth based games (like thievery for ut), i created a small mutator which adjusts pawns visibility so you can be stealthy in unreal/ut against the monsters (fun in coop)

SeePlayer function

This function is called by the engine on the AI when it can see a pawn where bIsPlayer == true. It is not called every tick, but instead at a rate based on the visibility of the pawn being seen and the distance between the AI and the pawn compared to the sightradius value of the AI's pawn. This is not a steady rate, but a rather sporadic one, especially near the lower threshold (I'm not sure how this rate is calculated).

There is a simple threshold to this function. It is only called if the distance/sighradius <= visibility/128. This means that by changing the visibility of a pawn the range at which the AI can see it can be decreased or increased.

i.e. if a pawn's visibility is 64, it can only be seen when closer than half the AI's sightradius.

Discussion

SuperApe: This page looks to be exclusively related to modding with bots in mind. I do not think this should be refactored into the bot navigation family of pages. This should be linked by and grouped with Mod-related pages only.


Category Custom Class

Refactor Me – Find a place for this in terms of Modding for bots.

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In