This tutorial can be applied to PogamutUT2004 and will mostly hold for PogamutUDK examples.
Purpose of this first tutorial is to get you familiar with basic concepts which will be needed in further tutorials. We will show how to create a simple bot that will just stand in the environment, talk and do nothing. It is a Pogamut's analogy of the "Hello world !" program - a simple program used to demonstrate basics of given technology.
This example is installed by Pogamut UT2004 installer. In NetBeans click New Project -> Maven -> Project From Archetype -> Local Archetypes Catalog and select 00-empty-bot-archetype project. Moreover, as Pogamut 3 has been fully mavenized, you can try and run this example even without installing the Pogamut NetBeans plugin. However in this case you won't be able to use visualization as this is a part of Pogamut NetBeans plugin. To open up this example in NetBeans follow up the steps in Opening Pogamut Examples chapter (if the archetype is not present, follow "adding new Pogamut example project" section in the same chapter). This archetype information is below.
For UT2004 example:
cz.cuni.amis.pogamut.ut2004.examples
00-empty-bot-archetype
3.3.1
http://diana.ms.mff.cuni.cz:8081/artifactory/repo
For UDK example only change Pogamut 3 with Maven Quickstart Tutorial.
to cz.cuni.amis.pogamut.udk.examples and 3.2.5-SNAPSHOT . The rest remains the same. Note that there is also a somehow outdated version of tutorial how to install Pogamut manually. Use it only if regular installation fails. Link: You will find up-to-date list of available archetypes inAfter setting up the example according to the section above, a new project named "00-empty-bot" will be opened in the Projects tab, open project's Source Packages sub folder and then the package present in this folder
Note that sometimes instead of "00-empty-bot" you get a project with the name "badly formed maven project". In this case simply right click the project and select
option. The project should reappear normally. If the project is reporting some bugs right click it and select the option again. The bugs are reported probably because Maven has not yet downloaded all dependencies (Clean and Build option assures that). If the Javadoc of the project is missing (help does not show up), right click the folder and select option.Finally double click EmptyBot.java
file
containing the source code of your first bot
Notice that the project's node in Projects tab is in bold, this means that the (F6) and (Ctrl + F5) buttons are linked to this project. Before we start your first bot, we have to launch the Unreal Tournament dedicated server - an environment simulator that Pogamut and the bot will connect to. You have two options how to start the server:
First way - start standalone server
execute the server by running
startGamebotsDMServer.bat
, wait until
START MATCH
string appears
in the console, now the server is ready to receive bot
connections. Shortcut to bat file startGamebotsDMServer.bat
can be found
in the start menu under Pogamut folder. File startGamebotsDMServer.bat
can
be found in /UT2004/System/ directory.
let the server console window opened and return back to Netbeans, switch to Services tab (Ctrl + 5 or → ), right click UT2004 Servers node and select action
in the dialog that will appear you can optionally
assign Server name, e.g..
Local UT
and you have to specify
server's complete URI (first delete the
[not set]
string and then
type the URI), in this case
localhost:3001
then press
button (if localhost does not work, try 127.0.0.1:3001 instead).
just created Local UT server will appear under the UT2004 Servers node, you will have to create this server only once, it will remain registered in the IDE
Second way - start server through UT2004 GUI.
Start UT2004 game through shortcut in the start menu or by running UT2004.exe in UT2004/System/ directory.
In the UT2004 game menu click
In the next menu select
tab and select some of the GameBots2004 game types.After you finished setting up the server, click listen button located in lower right. The server will be started and you will be connected to it as a player.
Connect to server from NetBeans the same way as shown above.
Now the server is running and the IDE knows how to connect to it. You can start the bot by pressing F6 ( ). Alternatively, you can simply right click the main bot class and select or select the project and click . If everything works fine, the bot will connect to the server and begin execution. After starting, the bot will appear under the Local UT node in Pogamut bots folder. It will be represented by a node named EmptyBot. If you select the bot's node, you will see some additional information about the bot in Properties window ( → ). Agent3D section shows properties defining bot's position in the space, Agent configuration section enables you to change bot's behavior/abilities (e.g. if the bot will be able to use raycasting, if it will automatically pick up items etc.). You will see that the bot is standing still, no movement, no rotation, after all, that's what it was designed for.
To see how the bot is doing in the game you will have to start the Unreal Tournament (UT) client, you can:
Start the UT directly from Netbeans
Right click Local UT server node and select action, the UT2004 will start in the spectate mode and it will be automatically connected to the selected server.
Start UT in a standard way
You can either use
When starting the UT for the first time it will open in a full screen mode, when developing bots you will often want to see both the Netbeans window and the UT. You have two options - use two monitors or configure UT to start in windowed mode, you can do this from main menu of UT Resolution panel → uncheck the checkbox.
→ tab →Inspecting the bot directly in UT is a very handy debugging tool. You can see, where the bot exactly is, whether it has stuck etc. Gamebots, Pogamut's extension of the Unreal Tournament, also adds some in game visualization features that can be useful when observing the bot.
Once the UT has connected to the game, press the left mouse button to view the bot. Also notice the green text in the upper half of the screen labeled GAMEBOTS 2004 HUD HELP (press CTRL + H to trigger HUD help on and off). You can try some of the listed functions, but we will address them later on.
In pre-3.3.1 Pogamut version the HUD control key used to be ALT. However ALT button had issue with UT2004 not reacting to keys when ALT + TABing in and out - sometimes the ALT button was "left hanging" in UT2004 when ALT + TABing from the UT window. The solution was to press ALT button again when back in UT2004.
Now return back to the Netbeans (if UT was in full screen mode use ALT + TAB). We will go through bot's source code in order to describe its basics.
The EmptyBot extends UT2004BotModuleController
class,
this is declared by line:
public class EmptyBot extends UT2004BotModuleController
UT2004BotModuleController
is a base bot class that will
suit most situations. UT2004BotModuleController
class has
several methods that have to be overridden by the EmptyBot and
by any other bot extending this class.
These methods are:
logic()
- method called
periodically by an internal thread associated with the bot. It
enables the bot to be proactive, that is to act "on his own"
without any external stimuli.
botKilled(BotKilled event)
- called
each time the bot has died
botInitialized(GameInfo info, ConfigChange config,
InitedMessage init)
- called when the bot received INITED message from the server. This
means that the INIT command succeeded, the handshake between bot and server is over and the bot is ready to receive other commands. Note that this does not mean the bot is already spawned in the environment! If it is set bAutoSpawn=False in GameBots ini file, the bot won't be spawned automatically and user needs to issue command Respawn
to spawn the bot. Then botInitialized()
method would be the right place to send first Respawn
command. Now briefly about method parameters. info
object holds information about the game
type, map etc. config
object holding information about bot current configuration - his vision time delay, autotracing setting, spawn setting etc. init
object holds information about bot variables such as his speed in the environment, maximum possible health etc.
botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self)
- called when the bot is spawned in the game for the first time. This means that the bot graphical represantation is visible in the game and Self
object was received from the environment holding information about bot location and current state. This is the last place to do some preparations before logic()
method will be called periodically.
beforeFirstLogic()
- called after botFirstSpawn method, right before the first call of logic()
method. This method is an ideal place to make last preparations of your custom modules before the logic()
method gets executed. The advantage here is that when this method gets called your bot is fully initialized and present in the environment.
getInitializeCommand()
- used
for setting initial properties of the agent such as it's
name, starting location etc. This method is used by Pogamut to get the initialize command for the bot.
prepareBot(UT2004Bot bot)
- called before the
bot connects to the environment, but after UT2004Bot is constructed. This is the right place to initialize Pogamut bot modules (but about them later)!
Sequence of calling these methods is:
prepareBot(UT2004Bot bot)
getInitializeCommand()
botInitialized(GameInfo info, ConfigChange config,
InitedMessage init)
botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self)
beforeFirstLogic()
logic()
- called N times. Note that between
two logic iterations, botKilled(BotKilled event)
method
may be called notifying about bot death.
Let's explore methods implemented in this example. First
is getInitializeCommand()
:
public Initialize getInitializeCommand() { return new Initialize().setName("EmptyBot"); }
In this method an Initialize
object is
created, a bot's name is set and the initialized object is
returned to the rest of Pogamut platform. Note that all setters in all Pogamut commands
always return the same object, so you may chain the setters calls.
new Initialize().setName("EmptyBot").setTeam(0);
Second method is botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self)
:
public void botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) { // Display a welcome message in the game engine // right in the time when the bot appears in the environment, i.e., his body has been just spawned // into the UT2004 for the first time. body.getCommunication().sendGlobalTextMessage("Hello world! I am alive!"); // alternatively, you may use getAct() method for issuing arbitrary {@link CommandMessage} for the bot getAct().act(new SendMessage().setGlobal(true).setText("And I can speak! Hurray!")); }
The "Hello world!" message is printed there. Note that the body
is the
gateway for the most commands you may issue to the bot to Unreal Tournament.
These commands are grouped together in several categories, like body.getCommunication()
.
All commands are well documented, so you can receive contextual help in NetBeans while
coding.
Alternatively you can use the basic command interface accessed through
getAct()
method to issue commands. The getAct()
method returns object
implementing IAct
interface that defines
act(CommandMessage command)
method. The
act()
method is the gateway for issuing actions
represented by CommandMessage
objects.
SendMessage
is a command sending message to a
global chat channel - the constructor. Notice that the object is created
and it's name property is set in only one line. The
setText()
method returns the instance on which
it has been called, so more properties can be set in a chain e.g.
new SendMessage().setGlobal(true).setText("And I can speak! Hurray!")
.
To see all command objects you can use in your bot, open
CommandMessage
class definition. To do it as
quickly as possible press Ctrl + O and type name of
the class for search. In this case, type
CommandMessage
. Once the source opens in the
central window press Alt + F12 to open
Hierarchy browser. Now you can see list of all
classes extending the CommandMessage
which
are commands provided by the PogamutUT2004.
Last method we will describe in this section is
main()
. This is the first method called when
you execute the bot program (like in any other Java program). This
method is responsible for instantiating the bot and connecting it to the
environment. Bot runner class facilitates bot execution:
public static void main(String args[]) throws PogamutException { // wrapped logic for bots executions, suitable to run single bot in single JVM new UT2004BotRunner(EmptyBot.class, "EmptyBot").setMain(true).startAgent(); }
Note that setMain(true)
is setter that should be used
only in global main methods (i.e., public static void main(String args[])
)
so the Pogamut platform correctly terminates itself after the end of the bot run. Otherwise
the JVM should not exit itself ... blame the JMX guys! Not us :-) So the rule of thumb,
you're in main method, use setMain(true)
, you're starting the agent from different method,
do not use it.
The logic()
method will be described in
the next section.
Two features that you will find handy during parameterization and evaluation of the bot are logging and introspection.
Logging is a mechanism for tracing program's execution by printing messages - it can be used both for debugging (multi threaded applications are often hard or impossible to debug using breakpoints) and evaluation (you can process logs of bot's activity and utilize some statistical software).
Introspection is designed to ease the bot's parameterization. It is often needed to adjust multiple behavior parameters at runtime and you will probably end up creating your own GUI (graphical user interface) for this purpose. In introspection, you just annotate desired variables with @JProp annotation and they will be accessible via the Netbeans GUI.
Let's look how logging and introspection works in EmptyBot
example. First start the bot (F6), then have a look on
it's source code. In the initial section several variables annotated
with the @JProp
are defined.
@JProp public String stringProp = "Hello bot example"; @JProp public boolean boolProp = true; @JProp public int intProp = 2; @JProp public double doubleProp = 1.0;
Now expand bot's node
under the UT server node (in Services tab), you
will see two new nodes - Logs and
Introspection. After selecting the
Introspection node the annotated variables will be
shown in the Properties (Ctrl + Shift +
7) window. Note that the intProp
variable is
being continuously updated. New values of variables can be also set in
this window.
Controlling the Logs. In Pogamut each major module usually has his own log category. When you set your log level to be high for Pogamut and Pogamut modules (to log even less important things) your console will be flooded with thousands of messages and this can cause performance problems in NetBeans (in Eclipse it is ok) - your bot will lag etc. To turn off logging in Pogamut add .setLogLevel(Level.OFF)
to UT2004Runner:
public static void main(String args[]) throws PogamutException { // wrapped logic for bots executions, suitable to run single bot in single JVM // you can forcingly set logging to aggressive level FINER so you would see (almost) all logs // that describes decision making behind movement of the bot as well as incoming environment events new UT2004BotRunner(EmptyBot.class, "EmptyBot").setMain(true).setLogLevel(Level.OFF).startAgent(); }
This will turn off logging for Pogamut modules, however your user log should not be affected. In NetBeans we do not recommend to set log level higher then INFO. Alternatively, you can also turn off logging for Pogamut modules one by one. E.g.
/** * This method is called only once right before actual logic() method is * called for the first time. */ @Override public void beforeFirstLogic() { //Turns off logging for path executor - logs hundreds of messages, useful when debugging path finding. pathExecutor.getLog().setLevel(Level.OFF); //Turns off logging for navigation module wrapping pathExecutor module. navigation.getLog().setLevel(Level.OFF); //Turns off your user specified logs you use in e.g. the logic() method - ( log.info("I'm logging."); ) log.setLevel(Level.OFF); }
Last non-empty method is logic()
. There is a commented code implementing a simple
follow player behavior. Try to uncomment the code blocks and observe the bot following the first player he sees in
the environment. More information about modules used here is in following tutorials.
public void logic() throws PogamutException { /* // Mark that new logic iteration has begun log.info("---LOGIC: " + (++logicIterationNumber) + "---"); // Log logic periods long currTime = System.currentTimeMillis(); if (lastLogicTime > 0) log.info("Logic invoked after: " + (currTime - lastLogicTime) + " ms"); lastLogicTime = currTime; // Can my but see any player? if (players.canSeePlayers()) { // YES! log.info("Can see any player/s?: YES"); // Set my target to nearest visible player ... lastPlayer = players.getNearestVisiblePlayer(); // ... and try to move with straight movement (without any navigation) log.info("Running directly to: " + lastPlayer.getId()); move.moveTo(players.getNearestVisiblePlayer()); // We've just switched to manual movement ... stop path navigation if running if (navigation.isNavigating()) { navigation.stopNavigation(); } } else { // NO, I cannot see any player log.info("Can see any player/s?: NO"); if (lastPlayer == null) { log.info("lastPlayer == null ... no target to pursue, turning around"); move.turnHorizontal(30); } else { log.info("lastPlayer == " + lastPlayer.getId() + " ... going to pursue him/her/it"); // Yes, I should try to get to its last location if (info.getDistance(lastPlayer) < 200) { // are we at the last log.info("Arrived to lastPlayer's last known location."); move.turnTo(lastPlayer.getLocation()); if (info.isFacing(lastPlayer.getLocation())) { lastPlayer = null; } } else { // We are still far from the last known player position // => just tell the navigation to guide us there log.info("Navigating to lastPlayer's last known location."); navigation.navigate(lastPlayer); } } } */ }
Note that you can observer Pogamut logs accesible by getLog()
method or directly by log.
under bot's
Logs node.