Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Creating .NET AI Bots for Unreal Tournament

Have you ever played a first person shooter? Then you've probably played against computer controlled players known as "bots" which are computer programs designed with artificial intelligence. Up until now the only way to do create your own bot for a game like Unreal Tournament was to learn a bunch of C++ and complicated AI routines. Of course, what you really want to do is code up your bot using C# or VB and Visual Studio Express.
Difficulty: Intermediate
Time Required: Less than 1 hour
Cost: Less Than $50
Software: Visual Basic or Visual C# Express Editions required, optionally install client and server piece: optional install of Unreal Tournament Game of the Year Edition client, optional Unreal Tournament Server Software
Hardware: None
Download: Download

Video: Programming Unreal Tournament AI bots

Unreal Tournament "Bots"

 

 

Have you ever played a first person shooter? Then you've probably played against computer controlled players known as "bots" which are computer programs designed with artificial intelligence. Up until now the only way to do create your own bot for a game like Unreal Tournament was to learn a bunch of C++ and complicated AI routines. Of course, what you really want to do is code up your bot using C# or VB and Visual Studio Express.

For this project, we created:

  • VB and C# project templates to simplify setup/installation of the necessary components for Visual Studio Express
  • "Bot API mutator" (custom level) which runs on the server that can accept .NET bots
  • A "visualizer" (custom Windows Form app) which provides a visual representation of a map so you can watch players play against each other

This project uses the code based on the Mutator for Unreal Tournament at http://www.planetunreal.com/gamebots/, but instead creates a .NET wrapper you to code your own "bots" in any .NET language. If you have ever wanted to write software which will take control of a player in Unreal Tournament and duke it out with other killer classes, then read on to find out how to do it.

 

Getting Started

Installing the Game (optional)
Installing the Game is *not* required as your bots can run on any of the supported remote servers. That being said, if you want to play against your own AI, you can configure your PC to be both a client (with you playing) and a server. You'll find full instructions on server configuration below, but to get Unreal Tournament Game of the Year Edition, you can order it from Amazon.com or download the shareware version ($19.99 for purchase) from Tucows. After installing, make sure to apply the patch up to version v436.

Configuring the Server (optional)
Your bot can only connect to servers running the “Bot API” mutator meaning you can either run a server locally or join a public server. If you want to run a server, Epic Games distributes the server version of Unreal Tournament for free at:  http://www.fileshack.com/file.x?fid=300. You can find step-by-step server setup instructions here. If you have a server of your own that is open to all feel free to add it to that list so that other bots from around the world can come round and visit you. Alternatively you can join one of the public servers listed at http://www.net.dcs.hull.ac.uk/utbot/ServerList.aspx without the hassle of configuring your own server and the benefit of seeing your AI bot play against other AI bots anywhere in the world.

Configuring Visual Studio Express
To simplify configuring Visual Studio Express, we've created project templates for VB and C#. You can download the appropriate:

  • Visual Basic Project Template
  • Visual C# Project Template

http://www.net.dcs.hull.ac.uk/utbot/Files/CS+UT+Remote+Bots.vsi (C#) http://www.net.dcs.hull.ac.uk/utbot/Files/VB+UT+Remote+Bots.vsi (VB)

Double click on your download to install it and when you next load up Express you should see a “UT Bots” Starter Kit in the new project window.

Configuring the Visualiser (optional)
To configure the visualiser, simply run UTBotsVisualiser.exe and enter the IP address for the running game.

 

Creating Unreal Tournament Bots

How your Bot works
You create your bot as a program running on your PC. The PC is connected by the network to the server which needs to be running the “Bot API” mutator. Twice per second the server makes a call to your bot's PerformActions() method, which is a member of the RemoteUserBot class. The server will also call methods in your bot when events happen in game, such as your bot hitting a wall or hearing a noise.

Performing Actions
You put code into the PerformActions method to give your bot its distinctive personality.

Your aim is to produce a bot that is aware of its own current state and that of the surroundings, reacting in a sensible manner. This may sound complicated, however, thanks to the methods and properties provided in the Getting Started pack, it is incredibly simple.

Game Messages
Although the majority of your code will be executed during the call to PerformActions() your bot also receives messages from the server whenever a certain ‘event' occurs.

For the purpose of clarity when we refer to an event we are not talking about an event of the .NET event-delegate type; in the context of your bot an event could be walking into a wall or hearing a noise.

It is also possible to override the behaviour of the methods which receive the messages passed in by the server so that your bot can respond sensibly to them. For example, you can override the AnotherBotDied method to keep a scoreboard of who's winning and losing in the game. For full details on these methods please check the API reference.

The creation of a working bot
The following examples have been provided as a basic starting point for developing your own working bot. Please note these examples are by no means the best way to do things; it is up to you to optimize your bot to make the best fragger.

Creating your Bot
After installing the Project Template from the Getting Started section, open up Visual Studio Express and select File...New Project... and select the "UT Remote Bot" template.

 

Solution Explorer View

Here you'll see the solution explorer view of the UT Remote Bots which contains a number of files for the project. In essence, the UT RemoteBot project is nothing more than a console application that sends and receives commands at a specific interval

  • Starter file- The basic template that describes the Remote UserBot and the server
  • RemoteUserBot file - Where you will code all of the work
  • UTRemoteBot.dll - The class library that contains the wrapper code for Unreal Tournament Functions
  • UTBotsVisualiser.exe - The visualiser tool mentioned previously
  • UT Bots - Guide.htm - An HTML getting started guide

Starter File
The Starter file is a console application that creates an instance of the RemoteUserBot class. You can customize it by providing a name for your bot, currently "UserBot", a "skin" for your bot which will display how your bot appears (ex: mercenary vs alien), the bot color (for teams), and the server to connect to. If you are self-hosting, you should set the server value to: "127.0.0.1"

Visual Basic

Imports UTRemoteBot

Module Starter
    '''<summary>
    ''' The main entry point for this bot. 
    '''Creates a new instance of your bot which will connect to the server and begin to play
    '''</summary>
    Sub Main()
        Dim b As RemoteUserBot = New RemoteUserBot("UserBot", BotSkin.Blake, BotColour.None, "classroom.dcs.hull.ac.uk")
        Console.WriteLine(" (Press Enter To Disconnect)" & Microsoft.VisualBasic.Chr(10) & "")
        Console.Read()
        b.DisconnectFromServer()
        Console.Read()
    End Sub

End Module

Visual C#

using System;

namespace UTRemoteBot
{

    public class Starter
    {

        /// <summary>
        /// The main entry point for this bot. 
        /// Creates a new instance of your bot which will connect to the server and begin to play
        /// </summary>
        [STAThread]
        public static void Main()
        {
            RemoteUserBot b = new RemoteUserBot("UserBot", BotSkin.Blake, BotColour.None, "classroom.dcs.hull.ac.uk");

            Console.WriteLine(" (Press Enter To Disconnect)\n");
            Console.Read();

            b.DisconnectFromServer();
            Console.Read();
        }

    }
}

If you run the program now you will find that your bot will appear in the game and stand waiting for instructions. The game framework is calling the PerformActions method in your bot at regular intervals, but because the method presently does nothing, your bot does not move.

The next thing we must do is change the content of this method to get control of our bot and make it move around in the game by adding code to the RemoteUserBot file. Opening up the RemoteUserBot file shows the code below where we will add whatever code we want to perform directly into the PerformActions() method.

RemoteUserBot Visual Basic

Imports System
Imports System.Collections.Generic
Imports UTRemoteBot


Public Class RemoteUserBot
    Inherits RemoteBot
    ''' <summary>
    ''' This methods creats an instance of your bot sets the UT options such as name, colour and 
    ''' skin and then connects to the specified server. Once connected PerformActions will be 
    ''' called every ~2 seconds and any in game events such as hitting a wall will call the appropiate
    ''' overriden method.
    ''' </summary>
    ''' <param name="aName">Name of your bot as it should appear in game</param>
    ''' <param name="skin">Skin that the bot should use in game</param>
    ''' <param name="colour">Colour of the bot in game</param>
    ''' <param name="server">The server which to connect either an IP address or a host name, it can only connect on the default port</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal aName As String, ByVal skin As BotSkin, ByVal colour As BotColour, ByVal server As String)
        MyBase.New(aName, skin, colour, server)

    End Sub

    ''' <summary>
    ''' Method to control the bot once it has connected to the server
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overloads Overrides Sub PerformActions()
        'Add code here to perform an action at a specific interval
    End Sub
End Class

RemoteUserBot Visual C#

using System;
using System.Collections.Generic;

namespace UTRemoteBot
{
    public class RemoteUserBot : RemoteBot
    {
        /// <summary>
        /// This methods creats an instance of your bot sets the UT options such as name, colour and 
        /// skin and then connects to the specified server. Once connected PerformActions will be 
        /// called every ~2 seconds and any in game events such as hitting a wall will call the appropiate
        /// overriden method.
        /// </summary>
        /// <param name="aName">Name of your bot as it should appear in game</param>
        /// <param name="skin">Skin that the bot should use in game</param>
        /// <param name="colour">Colour of the bot in game</param>
        /// <param name="server">The server which to connect either an IP address or a host name, it can only connect on the default port</param>
        /// <remarks></remarks>
        public RemoteUserBot(string aName, BotSkin skin, BotColour colour, string server)
            : base(aName, skin, colour, server)
        {
        }


        //Method to control the bot once it has connected to the server
        protected override void PerformActions()
        {
            //Add code here to perform an action at a specific interval

        }
    }
}

Bot's first sight
Now that we have a bot in the game we need to make it actually do something. The first thing we are going to make it do is move to a NavPoint. This is a location in the game which might hold something interesting. A series of NavPoints provides a path through the level to interesting items. A bot can travel around a game level by moving from one NavPoint to another.

Within the game framework (UTRemoteBot.dll), an instance of the UTVector class can be used to express the actual location of something. The location of a Navpoint is given by its Location property, which is given as a UTVector value.

Our bot needs to have a way of keeping track of where it is going so in the RemoteUserBot class create a new private UTVector called destinationLocation:

VB

Dim destinationLocation As UTVector
 

C#

private UTVector destinationLocation; 

We want our bot to head towards the first navpoint that it “sees”. To do this we place the following code into the PerformActions() method.

VB

Me.destinationLocation = Me.CurrentGameState.NavPointsVisible(0).Location

C#

this.destinationLocation = this.CurrentGameState.NavPointsVisible[0].Location; 

CurrentGameState property of our bot contains information on the current state of the game. NavPointsVisible is an array of references to UTVector instances which refer to all of the NavPoints a Bot can see. The above code simply takes the UTVector at position 0 (the first NavPoint your Bot can see) and sets destinationLocation to refer to it.

So at this point the bot has identified the location it wants to move towards, now we have to make it move in that direction.

Bot's first steps
Now we are going to make our bot move to the destination it has selected. Place the following code into the PerfomActions() method just below the code written in the previous example.

VB

Me.BotCommands.RunTo(Me.destinationLocation)

C#

this.BotCommands.RunTo(this.destinationLocation); 
 

BotCommands provides a set of methods which are the commands that your program can give to the bot. There are a wide range of commands, some of which are listed below. For full details you should investigate the API reference.

  • ChangeWeapon() - Changes the current weapon
  • Jump() - Jumps in the air
  • RotateBy() - Rotates by a specific degree
  • RunTo() - Runs to a specific location
  • Shoot() - Attacks a target or location
  • StopShooting() - Stops the bot from shooting
  • StrafeTo() - Strafes to a specific location

The RunTo() command is provided with a destination location. When this method is called your bot will start running towards the destination.

If you execute this program you will see your bot start running towards a navpoint.

Bot's first shot
For the purpose of this example we will next make our bot shoot at the location it is running towards, it is recommended that you change this when creating your own implementation as shooting at an empty NavPoint is unlikely to get you many frags.

Place the following code into the PerfomActions() method just below the code written in the previous example.

VB

Me.BotCommands.Shoot(Me.destinationLocation, false)

C#

this.BotCommands.Shoot(this. destinationLocation, false); 

If you execute the program you will see that your bot now shoots at the location as it runs towards it. It will continue doing this until you call a method to instruct it to stop.

Place the following code into the PerfomActions() method just below the code written in the previous step. Your Bot will now stop shooting.

VB

Me.BotCommands.StopShooting

C#

this.BotCommands.StopShooting(); 

Viewing your bot in action
You can find out what your bot is up to we have provided a visualiser. This is a modified version of TclViz. When you run the Visualiser you can connect it to any server running the mutator and get a birds eye view of the game in progress by simply providing the IP address of the server. You can see the waypoints which are marked by pink dots and the active players which have their name next to a yellow dot and a line indicating which way they are facing. From here you can view the game and see what your bot is doing as shown in the visualizer screenshot.

 

What to do next
Now that you have got your bot into the game and made it move around a bit you can start thinking about making it into a decent player of the game. Take a look at the other members of the NavPoint class so that your bot can look for weapons and health pack and move towards them. Then take a look at the members of the CurrentGameState class for the information that your bot is given about the game around it.

One thing you will need to add is some way that your bot can be in a particular state at any given time. Sensible states might be “Roaming”, “Hunting”, “Idle”, “Recovering” etc. Depending on the state your bot is in it will do different things each time PerformActions is called.

If it is in the roaming state it will be looking for a nav point. If it is in the hunting state it will be chasing other bots. If it is in the recovering state it will be avoiding other bots and looking for a MediPac, and so on. You can keep track of the state of your bot by creating an enumerated type with the different values. Then your PerformActions method can contain a switch statement which makes it behave differently, depending on what it is doing.

A state machine is just one way you could program your AI, you could make a sophisticated bot that had a way of determining if a result had a positive or negative consequence and then have the bot perform more actions that had a positive consequence. Essentially a learning bot, be careful though if your bot takes too long executing PerformActions it will get out of touch with the game as messages will sit on the queue unprocessed waiting for your code to finish.

There are also some very handy hints in the FAQ below about getting your bot on the road to stardom.

 

Conclusion
With this you should be able to go about creating your own deathmatch player. If you have any questions which are not answered in the FAQ below feel free to use the forum on the site (http://www.net.dcs.hull.ac.uk/utbot/ ).

If you're feeling particularly brave join our project on CodePlex at http://www.codeplex.com/Wiki/View.aspx?ProjectName=UTBots and help pitch in with improving the client.

 

Frequently Asked Questions

Do I need Unreal Tournament to use this stuff?
No, you can create bots and have them connect to any bot server and use the visualiser to get an idea of what's going on. Of course it isn't as pretty as UT! You can also run the server without owning a copy as Epic distribute a free server side runtime which you can get from http://www.fileshack.com/file.x?fid=300 .

My bot won't move! What did I do wrong?
This could be due to incorrect programming logic, please double check your code. You may also have left a Break Point within Visual Studio, if this is reached while watching your bot via Unreal Tournament your bot will appear to freeze until your program continues.

My bot keeps getting stuck when it runs into a wall. How can I stop this?

Try overriding the HitWall() method, an example of how to do this would be:
VB

 

Protected Overrides Sub HitWall(ByVal locationOfBot As UTVector, ByVal normalOfCollision As UTVector)
  Me.BotCommands.RotateBy(90)
End Sub

C#

protected override void HitWall(UTVector locationOfBot, UTVector normalOfCollision)
{
  this.BotCommands.RotateBy(90);
}


This method will be called when your bot hits the wall. The code above will simply rotate the bot by ninety degrees when it hits a wall.

What events can my bot react to?
You can try overriding any of the following methods:

  • AnotherBotDied()
  • BumpedActor()
  • HitLedge()
  • HeardNoise()
  • HeardPickup()
  • HitWall()
  • IncomingProjectile()
  • NewGlobalChatMessage()
  • PathReceieved()
  • PickedUpItem()
  • ShotAnotherBot()
  • TookDamage()
  • WasKilled()

My Bot seems a little slower than the other Bots! How do I make it faster?
Try improving your Bot's logic to optimize the speed.

The more quickly the PerformActions() method completes the more time your bot has to react in the game.

My Bot is nearly dead! How do I get it to collect some health?
A simple idea would be to just run your bot around the level, checking the CurrentGameState.ItemsVisible list to see if your bot can see a Health item and if it can, get it to run to that location to collect it.

Another idea would be to use the GetNearestItem() method inside the LevelMap class to find the location of the closest MediPack. Then you can call the GetPathTo() method from the BotCommands class to ask for an array of UTNavPoints to get to that health box. Once you have received this path, you can just get your bot to run to each point in turn until it reaches the health.

The following example shows you how you could do this:

VB

Dim pathToRun() As UTNavPoint
Dim index As Integer = 0

    
    Protected Overrides Sub PerformActions()
        'Check to see if we have a path to run already
        If (Not (Me.pathToRun) Is Nothing) Then
            'Run to this point on the path
            Me.BotCommands.RunTo(Me.pathToRun(index))
            'Check to see if our bot is near to the point we are running to
            If Me.Self.IsCloseTo(Me.pathToRun(index)) Then
                'Move our index to the next point in the path array
                index = (index + 1)
                If (index >= Me.pathToRun.Length) Then
                    'The index has reached the end of the path.
                    Me.pathToRun = Nothing
                End If
            Else
                'We don't have a path so find the nearest health box
                Dim box As UTObject = Me.LevelMap.GetNearestItem(HealthType.MediBox)
                'Ask for a path to that box
                Me.BotCommands.GetPathTo("Looking for health", box)
            End If
        End If
        'Override this method to collect the result back from GetPathTo()
        Dim PathReceieved As System.Void
        string
        requestID
        Dim path() As UTNavPoint
        If (requestID = "Looking for health") Then
            'The server has sent us a path so store it in our variable
            Me.pathToRun = path
        End If
    End Sub

C#

//Some variables to store our data
UTNavPoint[] pathToRun;
int index = 0; 

protected override void PerformActions()
{
  //Check to see if we have a path to run already
  if (this.pathToRun != null)
  {
     //Run to this point on the path
     this.BotCommands.RunTo(this.pathToRun[index]); 

  //Check to see if our bot is near to the point we are running to
  if (this.Self.IsCloseTo(this.pathToRun[index]))
  {
    //Move our index to the next point in the path array
    index++;
    if (index >= this.pathToRun.Length)
    {
      //The index has reached the end of the path.
      this.pathToRun = null;
    }
  }
  else
  {
    //We don't have a path so find the nearest health box
    UTObject box = this.LevelMap.GetNearestItem(HealthType.MediBox); //Ask for a path to that box
    this.BotCommands.GetPathTo("Looking for health", box);
  }
} 

//Override this method to collect the result back from GetPathTo()
protected override void PathReceieved(string requestID, UTNavPoint[] path)
{
  if (requestID == "Looking for health")
  {
    //The server has sent us a path so store it in our variable
    this.pathToRun = path;
  }
} 

Some of the methods want me to provide a HealthType, AmmoType, etc as a parameter. Where can I find these?
These types are Enumerators which have been used to make coding your bot easier. They are all located inside the UTRemoteBot namespace so to use them you need only type in the appropriate name and IntelliSense should fill in the rest.

For example typing WeaponType. Would fill the Intellisense with the different types of weapons in UT so you could then pass these to the LevelMap.GetNearestItem() method.

These types can all be sent as parameters to various methods in the BotCommands class:

  • AmmoType – A list of all the types of ammo for weapons in the game
  • ArmourType – A list of all the types of armour in the game
  • HealthType – A list of the different types of health items in the game
  • WeaponType – A list of the different types of weapon in the game

 

The following types can be sent as parameters to your bot's constructor in the Starter file:

  • BotColour – A list of the 5 different colours to use when customising your bot in the Starter file
  • BotSkin – A list of the skins that you can give your bot when customising it in the Starter file
  • OutputType – The level of output you want to be written to the console when developing your bot

How do I shoot at another player?

All you need do is check the CurrentGameState.PlayersVisible list for any players visible. You would then call the BotCommands.Shoot() passing in one of the Bots from the list and a bool which if set to true your bot will use the secondary fire mode on the current weapon if set to false your bot will use the primary fire mode.

GetPathTo doesn't seem to work
For reasons best known to code pixies GetPathTo doesn't always return a valid navigation path. In some cases it might even return nothing. The root cause of this problems lies deep within the Unreal Tournament path finding routines. It seems that in certain circumstances, for instance just after an item has been collected, the game just can't find a path. We strongly recommend that your bot doesn't rely too heavily on this method and only uses it occasionally with plenty of error checking!

Where do I connect to?
You can find a list of servers here: http://www.net.dcs.hull.ac.uk/utbot/ServerList.aspx  or you can run your own instructions for which can be found here: http://www.net.dcs.hull.ac.uk/utbot/ServerSetup.aspx

 

I want to run my own server
Great! Instructions and files can be found at: http://www.net.dcs.hull.ac.uk/utbot/ServerSetup.aspx

If your server is public and you don't mind strangers you can add it to the list here: http://www.net.dcs.hull.ac.uk/utbot/ServerList.aspx

Is Unreal Tournament Game of the Year Edition the only supported UT version?
Yes, although we are looking to add support for Unreal Tournament 2007 when it is released. Check the CodePlex Unreal Tournament Bots project for more information

Tags:

Follow the Discussion

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.