Tutorial body

Purpose of this tutorial is to get you familiar with basic concepts AnimationCore extension. We will create simple bot that will just stand in the environment and execute two animation planners in succession. One will be a SimpleAnimationPlanner instance, the other will be a BMLAnimationPlanner instance.

Put Anim-01-Static into your projects (or workspace) directory.

Opening the example, executing the bot

Follow these steps:

  1. Open Netbeans (StartProgramsNetbeansNetbeans 6.7)

  2. Once Netbeans were loaded open example project: FileNew Project ...SamplesPogamutAnim-01-Static then click Next >

  3. In New Project dialog enter name of the folder where the example will be unpacked, for example Anim-01-Static, after you are finished click Finished button.

  4. Now a project named "Anim-01-Static" will be opened in the Projects tab, open project's Source Packages sub folder and then the cz.cuni.amis.pogamut.ut2004.examples package contained in this folder

  5. Finally double click Static.java file containing source code of our first animating bot.

Notice that the project's node in Projects tab is in bold, this means that the Run Main Project (F6) and Debug Main Project (Ctrl + F5) buttons are linked to this project. Before we will start this first bot we have to launch the Unreal Tournament listen server.

Follow these steps:

  1. Start Unreal Tournament 2004

  2. Press Host Game

  3. Choose Animation DeathMatch

  4. Choose DM-Flux2 in the list of maps (left side of menu)

  5. Click on Mutators and add GBHudMutator and GBNoWeaponMutator (by either double-clicking on them in the left list or clicking on them in the left list, then clicking on Add). You should have both mutators listed on the right side of the menu now.

  6. Click Listen, to start the server.

  7. Wait until the map loads, then press Escape and click on Spectate.

Let's get our agent running now. Use Alt+Tab to get back to NetBeans and hit F6, use Alt+Tab again to get back to UT2004. Soon, you should be able to see a new bot named "AnimationBot" in the world. In a few seconds, bot will pose itself into basic animation position and start executing a sequence of simple animations.

Understanding the use of AnimationCore

Now, we are going to examine the source code in greater detail. We will skip any code that you should already know from previous tutorials and code that is not relevant to the use of AnimationCore.

Instantiating required modules

Let's have a look on Static.java. AnimationCore propagates its functionality to its user through a class called Animation. Its constructor looks like this:

    public Animation(
        AbstractEmbodiedAgent<UT2004SyncLockableWorldView> agent,
        AgentInfo agentInfo, Items itemsModule, Players playersModule,
        UT2004SyncLockableWorldView worldView,
        ICommandSerializer commandSerializer,
        Logger log, String ipAddress, int port)
        throws IOException, PogamutException

We don't have these classes instantiated yet:

  • AgentInfo

  • Items

  • Players

  • UT2004SyncLockableWorldView

In order to instantiate them, we have this code:

    // Instantiate modules.
        game = new Game(worldView, this.getLogger().platform());
        info = new AgentInfo(worldView, game, this.getLogger().platform());
        players = new Players(worldView, info, this.getLogger().platform());
        items = new Items(worldView, info, this.getLogger().platform());

Instantiating animation module

Now we are ready to instantiate the animation module. Since its constructor can throw two types of exceptions, it is enclosed in a try statement:

    // Try to instantiate new animation module.
        animation = new Animation(this, info,
          items, players, worldView, commandSerializer,
          this.getLogger().getCategory("Animation"), "127.0.0.1", 3002);

Pay attention to the last two parameters, since they have to correspond with an IP address and port of GameBotsAnim. Port 3002 is the default port and "127.0.0.1" represents your present computer. We have experienced some issues with the way Java looks up and/or connects to server. In case the connection doesn't work, change "127.0.0.1" to "localhost".

Supplying a plan

After this, we have to supply a plan to our newly created animation module. That can be done through a method called setPlanner of the animation module. But first we have to create it.

The first planner we will make our bot to execute will be an instance of SimpleAnimationPlanner called WristFlapAnimationSimplePlanner. This planner makes the bot do a simple wrist flapping animation (as the name suggests). So let's instantiate it and assign it to the animation module:

    // Try to create and set a new planner,
    WristFlapAnimationSimplePlanner planner = new WristFlapAnimationSimplePlanner();
    // then assign it.
    animation.setPlanner(planner);

Finishing touches to the bot's constructor

Ok we are almost done in the bot's constructor. There are only two more things you should know about and you can do in the constructor.

The first one is that you can attach event listeners to animation module, that listens for report that animation module finished the planner. A listener has to implement an AnimationListener interface. We will get more into detail about it a bit later in the tutorial. The second one is that you can also listen for the message ANIMSTOP coming from GameBots2004 (through standard means), that reports that bot has returned from animating state, to normal state and you can do all the standard things that you can do with an unarmed scenario bot. Listener for this event is a standard Pogamut event listener and as such implements IWorldEventListener with a type parameter AnimationStop. More on that later as well.

In order to take advantage of these feature, examine this code:

    // Set a listener to PlannerFinished event.
    animation.addAnimationListener(moduleListener);
    // Set a listener to ANIMSTOP message coming from GameBots2004
    // (GameBotsAnim communicates using a different binary connection)
    worldView.addEventListener(AnimationStop.class, serverListener);

doLogic

As you already know, doLogic is where most of the bots actions originate from. This is also the place the animation module gets ticked from, since animation module required this kind of tick as well. In order to give you some time to Alt+Tab back into the game, first few (25) iterations are skipped using a simple counter. The pause between each iteration is handled by Pogamut itself, since it calls doLogic in set intervals.

After the first iterations pass, the "beef" of doLogic comes to hand. The beef consists of two parts:

  • Starting animation (once).

  • Ticking animation module (called till the end of bots execution).

Starting animation gets done by calling startAnimation method on animation module. This is to be done after each assignment of a new plan and positioning a bot to the target location of animation execution. Ticking the animation module should be happen every time since bot's starting animation up until it stops. In the next section we will cover how that is reported to you.

Reporting events

Two kinds of events should be of particular interested for you. We have mentioned them in the section called “Finishing touches to the bot's constructor”.

Plan finished

When the animation module finishes the execution of the plan, then every registered object that implements AnimationListener gets notified. In our example the class that handles the event is AnimListener. Let's have a look upon its code:

    protected class AnimListener implements AnimationListener {

        boolean once = false;

        @Override
        public void plannerFinished(IAnimationPlanner planner) {
            // We want to restart the planner only once.
            if (!once) {
                once = true;
                try {
                    // Change input file as you wish.
                    IAnimationPlanner new_planner = new BMLAnimationPlanner("BMLExample2.xml");
                    animation.setPlanner(new_planner);
                    animation.startAnimation();
                } catch (PogamutException e) {
                    getLogger().user().severe(e.getMessage());
                }
            }
        }
    }

What we see here is that a method called plannerFinished is resposible for the handling of the event. For the users' convenience also the recently finished planner is passed. For the purpose of this tutorial we use this method to append another planner. This time it is a BMLAnimationPlanner using a BMLExample2.xml plan. For the explanation of BML look into the reference directory of the AnimationCore distribution.

As you can see setting a different planner works similary to what we seen already. It is practically same.

Bot has reverted back to the standard start from animation state

When bot reverts back to the standards state (not controlled by the animation module), you will get notified through the world view that recognizes the ANIMSTOP message using the AnimationStop class. This is handled by AnimationStopListener class. Since you should know by now how to handle such messages we leave this up to you.