The ability for Bots to move around a map is
taken for granted when you are fighting for survival in a Death Match
game. After all, you as a player learn your way around a map relatively
quickly. However, this ability does not come naturally to Bots. For
a Bot to learn its way around a map and to remember the best way to get from
one point to the next is a complicated problem for artificial
intelligence. Even more complicated is for a Bot to recognize its own
location in the map so that it can choose a best path. Instead of
building sophisticated intelligence into the Bot, the "knowledge"
about how to navigate around a map is encoded into the map itself.
One of the duties of a level designer is to
insert what are called "path nodes" into the map.
These path nodes are invisible when the game is played but bots can
"see" the path nodes use them to find their way from one place to the
next. Each path node can tell the Bot the next path node they should
visit in order to get to a given final destination. In the following
diagram, the bot wants to reach path node 8 (maybe this is where the rocket
launcher is located).
There are two possible paths. In order
to find it's way to path node 8, it asks the nearest path node, node
4, where to go. Path node knows that the best way to get to node 8
is to go through node 1 and tells the Bot so. The Bot then proceed to
path node 1 but does not know what to do after that. The Bot again asks
the nearest path node -- this time the nearest path node is node 1 -- where to
go. Path node 1 knows that to get to node 8, the Bot should go through node
2. The cycle continues until the Bot finally reaches it's final
destination.
Most of the intelligence built into the path
nodes is pre-computed by the level editor itself. Each path node knows
which node the Bot should visit next for each path node in the entire
level. However, in order to learn more about how Bot navigation works, we
are going to ignore the fact that this functionality is already supported, and
build our own system of path nodes and our own algorithms for computing best
paths.
For this assignment, you will create a
special Bot, a CamDroid, that wanders around the level. The CamDroid is special in that it does
not partake in combat. Instead, players
will be able to “see” through the eyes of the Camdroid when they type in a
special command.
Bot navigation relies on invisible PathNode objects distributed throughout the map by the level
designer. When UnrealEd builds the map, one of the many tasks it performs
is a all-pairs-shortest-path calculation on the PathNode object.
The all-pairs-shortest-path algorithm pairs
every possible starting node with every possible destination node and finds the
shortest path through the network of PathNodes .
For each possible pairing of starting node
and destination node, the starting node has a special data structure called a ReachSpec. The ReachSpec records the final destination and the next closest
node in the network. Thus when a Bot needs to know how to get to a
particular destination, it can quickly look up the ReachSpec in the nearest PathNode and find out the next place to go. The Bot never
knows the path further than one node in the future.
Since the all-pairs-shortest-path algorithm
is done when the level is built, the algorithm never needs to be run during game-time.
This is important to game play because the all-pairs-shortest-path algorithm is
computationally expensive and Bots cannot afford to be running slow algorithms
while participating in a game.
In order for a Bot to get to a location in
the map that does not coincide with a PathNode (for example, it is trying to get to another enemy
player -- enemy players are not recorded by ReachSpecs because they can move around freely), it merely has to
find the PathNode that is
nearest to (and visible from) its desired location and use the network of nodes
to reach that PathNode.
Once at the final PathNode
, it can make a bee-line to that non-node position. This system only
works if the level designer has placed the nodes in the map such that
there is one or more other node visible from every node.
Bot navigation is handled by a function in
the Controller
base class, FindPathToward(Actor
anActor). anActor is the final destination that the Bot wishes to
arrivate at. Notice that the type is Actor and not NavigationPoint.
This means the Bot can find its way to any location, regardless of whether it
is part of the path node network or not. FindPathToward() returns an Actor object that is the next closes point
to move to. The return value may or may not be the same as anActor, depending on whether the route to anActor is complicated.
Once the next point to move to is known, the
Bot moves by calling MoveToward(Actor newTarget). MoveToward() is usually called by state code and the newTarget
usually depends on the goal of the Bot (e.g. attack or retreat or collect
power-ups, etc.). you may find it beneficial to look at some of the state
code in the Bot class to see how moving is handled there.
You have two tasks:
1. Create a functioning Camdroid bot. Camdroid is made up of CamdroidPlayer – a subclass of AIController – and CamdroidPawn – a type of xPawn. Modify
the stub CamdroidPlayer so
that the bot randomly picks a PathNode to travel to. Note, I
have put special path nodes called MyPathNodes into the test levels. The only difference is that they are visible. Once it travels to that location, it
picks a new location. The stub CamdroidPlayer class has a single stubbed out function, Start. Start is called by the DroidGame after Camdroid is created (by the game) and after the match has
started. Here is what I
recommend. Create a function
called PickNewDestination that randomly chooses a PathNode for the final destination. Create a special state, called navigating that loops forever.
2. Modify the player’s controller and pawn, CameraPlayer and CameraPawn, respectively, so that when the user types “spy” into
the console, the user’s camera is attached to the camdroid. When the user types “unspy” into the
console, the camera returns to the player’s body. I recommend using the CameraPawn to override the SpecialCalcView() function.
SpecialCalcView takes three parameters: out Actor ViewActor, out
Vector CameraLocation, out
VectorCameraRotation. Changing the value of the
pass-by-reference variables will tell UT where to place the user’s camera (we
don’t ask how this is done, only that it works). Note that SpecialCalcView is only called when bSpecialCalcView variable is true.
The base class for all Bots and human users.
function Actor FindPathToward
(Actor anActor, optional bool bWeightDetours)
Use the
built-in ReachSpec information for the nearest NavigationPoint to find the next closest node in the network to
anActor. Returns an Actor that the Bot should visit next.
function Actor FindPathTo
(vector aPoint)
Works
the same as FindPathToward() except it takes a vector instead of an Actor.
function MoveToward (Actor newTarget,
optional Actor viewFocus, optional float destination offset, optional bool
bUseStrafing, optional bool bShouldWalk)
If
called in state code, causes the Controller to move toward newTarget. This function assumes that newTarget is visible from the Controller's current
position. viewFocus
specifies what the actor should
look at as it is moving to the next position.
function MoveTo (vector
newDestination, optional Actor viewFocus, optional bool bShouldWalk)
Works
the same as MoveToward()
except it takes a vector instead
of an Actor.
MoveTarget
A
reference to an Actor. Frequently used as a holder for the next
intermittent node in the path.
The base class of all human users.
event PlayerTick (float
deltaTime)
This is
the tick function for PlayerControllers. The normal Tick() does not work here, but instead PlayerTick() is called every tick.
function PlayerMove (float
deltaTime), function ProcessMove (...)
This
function is called when the player sends the command to walk forwards or
backwards.
state PlayerWalking
This
state code is running when the player is standing or walking on solid
ground. You will notice that functions such as ProcessMove() are handled specially in this state.
state PlayerFlying
This
state code is running when the player is falling or flying through the
air. You will notice that functions such as ProcessMove() are handled specially in this state.
Base class for everything that can manifest
itself in a level.
function bool FastTrace (vector
TraceEnd, optional vector TraceStart)
Quickly
traces a ray between the object making the call and the position, TraceEnd. If there are no obstructions between the
object making the call and TraceEnd, this
function returns true. If TraceStart is specified, it uses that position as the place to
originate the ray.
Location
A vector
specifying this object's absolute position in the level.
The base class for all objects that can be
used by Bots for navigation. This includes PathNode, PlayerStart, and InventorySpot .
PathList
An array
of ReachSpecs. You may notice that there are no functions
that actually reference this array. All path navigation routines are
native to the game engine and not UnrealScript.
The most basic type of NavigationPoint that is placed into maps by level designers. It
doesn't really add any functionality to the NavigationPoint (at least visibly, there may be native code hidden
from the UnrealScript) except this class is placeable.
The base class for all pawns.
Bool SpecialCalcView (out Actor ViewActor, out vector CameraLocation, out vector CameraRotation)
This
function places the camera when bSpecialCalcView = true. The three parameters are
pass-by-reference and changing them determines where UT will use as the user’s
viewpoint.
Certain functions in the Controller and its
subclasses are specified as "exec" functions. This means that
these functions can be executed directly from the console window.
The console window is a message window that
slides down from the top of the screen when you press the '~' key. In the console window you can see the
previous broadcast messages. There is also a prompt where you can enter
exec funtions. For example, typing "ghost " allows you to fly through the air and walk
through walls.
Your CameraPlayer has two special exec function called spy and
unspy. When spy is run from the console, it will cause the player’s
camera to become attached to the camdroid so that the player sees what the
droid sees. When unspy is run from
the console, the user’s viewpoint returns to the player’s pawn’s body.
Here is an example of an exec function that
lists the location of a given actor in the game.
exec function foo (coerce string name)
{
local Actor a;
foreach AllActors (class'Actor', a) {
if (name ~= string(a.name)) {
// Found the actual object
with this name
Level.Game.Broadcast(self,
a.Location);
break;
}
}
}
The parameter that you pass in is forced to be
of type string. This function figures out what object in the level
corresponds to this name. Note that every Actor has a name variable and
that that name can be typecast into a string. the ~= is a special operator that checks string equality without
case sensitivity.
For example, when you type
foo mypathnode4
into the console, you should see a
broadcast message with mypathnode4 object's location.
Obviously, spy and unspy will be very
different. But I wanted you to be
familiar with how to parameterize exec functions.
In order to complete your project you will
need the following files:
You must create a new package in the UT2004
class library. All the new classes you write should be part of this
package. The name of your package should be "Hw3".
To test your project, run UT2004
and select the "CamDroid Game" from the Instant Action
menu. You should be able to choose the NAV-test.ut2 map. I
recommend setting the number of bots to zero.
You may also wish to use other maps. I do not expect your droid to be able
to navigate across jumps or movers.
Make sure to rename the map file to a "NAV-" prefix.
Other things to consider when testing your
code is whether the droid s the edge of walls (line of sight calculations do
not consider the fact that your body has width). I will not take off
points if this happens, but you should be wary of situations where you get
caught on a wall and stop moving altogether. You should either avoid
clipping the wall or programmatically handle the situation where you get stuck
on the wall.
For your final grade, I will test your code
on a map that does not have MyPathNodes. So
don’t hard code any dependencies to MyPathNodes.
Get the droid working first. The camera part is pretty easy after
that. You’re welcome.
To get credit for your work, you must submit
a single UMod file.
The UMod file should contain MyPathNode.uc, DroidGame .uc,
CameraPlayer.uc, CameraPawn.uc, CamdroidPlayer.uc, CamdroidPawn.uc, Hw3.u, and Hw3.int.
Your UnrealScript files should unpack automatically into UT2004\Hw3
directory. Your .u and .int files should unpack into UT2004\System
directory.
You should also write a readme.txt
which describes any assumptions or
optimizations you have made to the basic functionality enumerated in this
document. Also describe any additional functionality that you program
into your game or your Controller (for example, are there ways to abort the
auto-movement once it has begun?). The readme.txt should be in your UT2004/Hw3 directory (not UT2004/Hw3/Classes).
Make sure that this file is included in your UMod.