Creating animations

I have been working on the animations of the game, in order to support something a little bit more complex than what currently can be done. Originally I wasn’t supposed to have any kinds of animations in the game, but then I added tiny damage counters that float away after a character gets hit, poisoned or healed. They looked so nice that I wanted to add more, but that requires a little work under the hood.

There is Model object, that represents everything in the game world. User interface then queries the model when it wants to know what to display on screen. Animations don’t really belong to the model, but luckily I can use Events to trigger them. Every time something happens in the game world, an Event (or multiple events) are raised that describe what just happened. These are relayed to the user interface, who can decide how to show them.

Previously I was inspecting content of those events in the user interface and creating animations for counters when needed. That was one long and unwieldy piece of if – statements. Creating animations sounded like something a single class should handle, so I broke the functionality to a different part and replaced the old if-forest with following:

    def receive_event(self, event):
        """
        Receive event from model
        """
        anim = self.animation_factory.create_animation(event)
        anim.trigger(self)

So, when ever UI gets an event, it will delegate creation of the corresponding animation to a separate factory and then just trigger the resulting animation. Since the AnimationFactory is guaranteed to return an animation object, I don’t even have to check for None here.

Creating animations

Factory itself is pretty simple.

class AnimationFactory():

    def __init__(self):
        """
        Default constructor
        """
        self.animations = {
            'attack hit': AttackHitAnimation,
            'damage triggered': DamageTriggeredAnimation,
            ...
            }

    def create_animation(self, event):
        if event.event_type in self.animations:
            return self.animations[event.event_type](event)
        else:
            return Animation(event)

Initializer creates a dictionary that has a Type for each animation I want to be able to play. create_animation method then just searches the dictionary based on the event_type, creates it and returns it. If there is no match, a generic (empty) animation is created and returned. This simplifies the code in the client side, where there is no need to check for None.

When trigger method of an animation is called, it will create animation using Qt’s animation framework and start it. Qt will then take care of transitions and timings, without Python code having to worry about it at all. There are some house keeping still involved though, like keeping a reference to running animations (otherwise they wouldn’t be running for very long) and removing them after animation has finished playing. This all is complicated by the fact that the animated objects might be removed from the screen at any given moment (for example, by player moving to a different level).

System is now mostly working as intended. I might have to add some way of pausing the game while a long running animation is running. Otherwise the game model and on-screen animations would be out of sync. With short animations it does not matter or the player is not able to notice it, the long running ones are the problematic ones. It is also nice that since the animations are handled completely on the user interface side, I can have different kinds of implementations of them for Qt and Curses interfaces.

Herculeum 0.9 released

Yesterday I released Herculeum 0.9. Release log is short:

  • curses interface
  • bug: Effects with None as duration or frequency cause crash when triggered
  • switch to Python 3

Most of the changes are under the hood. The game is now available at PyPi, which makes installing as easy as:

pip install decorator
pip install hy
pip install herculeum

AI routines of rats and fire beetles have been rewritten. They now choose an area to patrol and walk around there, pausing to wait a bit now and then. Rats prefer to stay close to walls of the room, while fire beetles stay in the middle of the room. A large room with couple of rats and a fire beetle or two is now much more interesting to sneak through.

AI routines have been written with Hy, hence the dependency to it. The plan is to use Hy for all of the AI routines in the future, which means a rewrite for skeleton warriors and the old Crimson Jaw.

Curses interface is up and working (it’s actually the interface I’m mostly using when developing the game nowdays). PyQt interface will continue to be supported though, it is not going anywhere.

Magic didn’t make into this release, although part of it is already working. I’m trying to finish it for the next release, which hopefully won’t take as long as the previous one to make.

Here’s a screenshot of curses interface showing rats and beetles patrolling the room at top right.

herculeum 0.9 - rats and beetles

Some acceptance tests for magic system

I read my description of what kind of spell system I would like to have in Herculeum and wrote some high level tests based on it. Not all requirements are captured by these, for example gaining mana is missing. But these should be plenty to get me started with the programming tasks. After these tests are passing, I have probably learned a bit more about the magic system and can continue with more tests.

Feature: Magic
  as an spell caster
  in order to survive my adventure
  I want to use spells

  Background:
     Given Simon is Wizard
       And Uglak is Goblin
       And Uglak is almost dead
       And Uglak is standing in room
       And Simon is standing away from Uglak

  Scenario: Magic missile
       When Simon casts magic missile to Uglak
       Then Uglak should be dead

  Scenario: Fireball
      Given Zhagh is Goblin
        And Zhag is almost dead
        And Zhag is next to Uglak
        And Simon is standing away from Zhag
       When Simon casts fireball to Zhag
       Then Uglak should be dead
        And Zhag should be dead

  Scenario: Healing
      Given Simon is almost dead
       When Simon casts healing wind
       Then Simon should be in full health

  Scenario: Domain specialization
      Given Simon has Rune
       When Simon uses rune for fire domain
       Then Simon should have more fire spells
        And Rune should not be in inventory of Simon

  Scenario: Out of mana
      Given Simon has no mana left
       When Simon casts magic missile to Uglak
       Then Uglak should be alive

One aspect that is not captured by these tests is user interface. The reason for this is that I have not found a good way to write high level tests for user interface. I can write simple unit test style tests for Qt interface, but those are too verbose and complicated to be written at this point I think.

Results from the tests at this point:

7 features passed, 0 failed, 1 skipped
15 scenarios passed, 0 failed, 5 skipped
109 steps passed, 0 failed, 39 skipped, 5 undefined
Took 0m0.5s

Satin and Qt event loop

Qt comes with a really good testing library called QTest. It can be used to simulate keyboard and mouse input when testing the system. PyQt naturally includes the same library, but using it can be a bit tricky sometimes. Mouse input works quite fine, but keyboard input does not work well without event loop running in the background. It is possible to get around this, by instantiating QApplication and calling exec_ – method on it. At this point QApplication takes charge of things and execution leaves the test method.

There are several ways around this. One is to construct two threads, using one to send keyboard and mouse events to the system under test. One should note though, that the QApplication should be running in the main thread (it will helpfully notify you if this is not the case). Another thing is shared resources. Constructing QPixmap objects outside of the thread running QApplication is not advisable (you’ll be notified about this too).

Second option is to construct a QTimer, that will start execution of your test code. In this model you do not need to worry about multiple threads or shared resources.

Doing either one  can get tedious, especially if the amount of tests is more than two. Satin now has a class decorator satin_suite, that will take care of the basic steps, leaving the developer free to write more expressive tests. Essentially, the decorator will perform following steps:

  1. Replace setup-function with a version that creates instance of QApplication and calls the original setup-function.
  2. Replace teardown-function with a version that deallocates QApplication and calls the original teardown-function.
  3. Replace each test_* function with a version that will install a QTimer, start QApplication, execute test code and exit QApplication

Details are still rough and the system has some faults (like any exception or failure halting the execution of tests).

Test driven development and user interfaces

I have been using test driven development for my game and have been extremely happy with the results. One section that is lacking with tests is the user interface though. Since I’m currently working on some new controls, I decided to give it a better try.

Qt has good support for testing and PyQt exposes some of the needed classes. Relying only on those however, would probably create rather brittle tests. I rather not hard code names of controls and their hierarchy, if I can avoid it.

This is where Satin comes into play. Currently it is just a readme and license file, but the plan is to write little helpers that can be used to test UI without hardcoding everything:

    dialog = CharacterDialog(character)
    assert_that(dialog, has_label(character.name))

As long as there is a QLabel with text set to character’s name, this assert will pass. It does not matter what the QLabel is named or where it is located.

more eye candy

I have been working on the user interface side of the project for a chance and made some nice progress. Damage counters aren’t moving in constant speed anymore, but bouncing up similar to how they work in Final Fantasy. Another addition is the new inventory window, which looks nice, but doesn’t do anything yet.

Last addition this week is Qt resource system. Previously I was managing locating and loading icons manually. Most of the graphics still use that system (and probably will always do so), but user interface is now using resource system of Qt. UI icons are packaged into a single file, where they can be easily loaded from anywhere in the application:

self.necklace_slot = ItemGlyph(None,
                               surface_manager,
                               self,
                               QPixmap(':necklace.png'))

ItemGlyph is a class I wrote by myself, but the interesting part is loading necklace.png from inside resource file. QFile (which QPixmap uses to load the file) automatically detects the colon in the beginning and will load the file from resource file, instead from filesystem. Advantage of this is that I don’t have to package dozens and dozens of icon files anymore when I release the game.

There’s couple youtube videos behind the cut. Quality is somewhat choppy, I haven’t yet figured out how to record smooth video. But you’ll get the basic idea.

Continue reading

PyQt and animating QGraphicsItem objects

Qt has really nice system for creating animations by using QPropertyAnimation objects. Essentially, you define start and end values for a property, duration and fire of the animation. Qt takes care of the rest. After animation completes, a signal is emitted. You can have multiple animations running at any given time and are free to your own stuff in the meantime.

Slight problem with this system is that QPropertyAnimation only works with objects that inherit QObject. QGraphicsItem does not do that and therefore is incompatible with it. Common solution to this problem in Qt side is to create own class and inherit both QObject and QGraphicsItem. PyQt does not seem to support this though, so I had to come up with something else.

class DamageCounter(QGraphicsSimpleTextItem):
    """
    Counter for showing damage

    .. versionadded:: 0.6
    """
    def __init__(self, damage, parent):
        """
        Default constructor
        """
        super(DamageCounter, self).__init__()

        self.setText(str(damage))
        self.setBrush(QColor('white'))

        self.adapter = DamageCounterAdapter(self, self)

class DamageCounterAdapter(QObject):
    """
    Adapter for damage counter

    .. versionadded:: 0.6
    """
    def __init__(self, parent, object_to_animate):
        """
        Default constructor
        """
        super(DamageCounterAdapter, self).__init__()
        self.object_to_animate = object_to_animate

    def __get_y_location(self):
        return self.object_to_animate.y()

    def __set_y_location(self, y):
        self.object_to_animate.setY(y)

    y_location = pyqtProperty(int, __get_y_location, __set_y_location)

DamageCounter inherits QGraphicsSimpleTextItem and can be placed on QGraphicsScene. It has DamageCounterAdapter attached to it, that is inherited from QObject. I defined only one property y_location in the adapter, mainly because I only need that currently. This property reflects state of the DamageCounter’s y-coordinate, it can be read and written.

Because Adapter inherits QObject, I can use QPropertyAnimation to modify it. Modifications are reflected in DamageCounter, thus animating it.

Creating a little damage counter that moves towards top of the screen when a creature gets hit is now pretty simple:

damage_counter = DamageCounter(damage = damage,
                               parent = self)
self.view.scene().addItem(damage_counter)
damage_counter.setPos(target.location[0] * 32 + 16,
                      target.location[1] * 32)

animation = QPropertyAnimation(damage_counter.adapter,
                               'y_location')
animation.setDuration(1500)
animation.setStartValue(target.location[1] * 32)
animation.setEndValue(target.location[1] * 32 - 32)
animation.finished.connect(self.remove_finished_animation)

self.animations.append(animation)

animation.start()

Here’s a screen shot of rat hitting the player.

Maybe not the most elegant solution, but it works. I’ll clean and wrap that into a function somewhere next.

More PyQt, themes and graphics

I’m starting to like Qt more and more as I learn more about things I can do with it. I have been working with the user interface of pyherc and recently added possibility to use qt style sheets. They allow changing how the program looks without touching the source code. Eventually I might make it possible for user to select from several different themes and write their own too even.

Related to the themes is new look of the icons. I’m trying to stand out a bit and go for very minimalistic colour scheme, with mostly different shades of gray. Only very special items or monsters would have colour on them:

Goal is to have very clear visual style, quite similar to movie Sin City. Probably it wouldn’t be too much work to allow icons to be loaded from different directories too. This would allow creation of full themes, that could completely change how the user interface looks. Below is link to screen shot of full UI. Quickbar at the top will probably stay, but the inventory has to change to something less obstructive, maybe a dialog that can be displayed upon request.

There’s lot of work to do in the user interface side still. Currently the game is only playable with combination of mouse and keyboard. Eventually I would like to offer ability to control the whole game with only single input device, be it keyboard, mouse or joypad. This places some constraints to how the interface needs to be constructed that I still have to figure out.

pyherc 0.5 released

I released version 0.5 of pyherc this morning. Nothing too dramatic, but I’m pretty happy how quickly  I managed to convert user interface from PGU to PyQt4. Main reason for doing this was that I really didn’t understand how to create custom user interface elements in PGU. Not that I have created anything complex with PyQt yet either, but at least the very few components I did were easy enough.

New user interface is the main new feature in the release. Everything else is mainly fine tunings, modifications and small additions. Event system is slowly growing to accommodate more events and the new user interface makes extensive use of them.

I also like how the testing side is progressing. Cutesy is slowly growing (need to split it to separate files soon). Unit tests are automatically executed every time I make a check in. There’s problem with statement coverage though: reports seems to be generated correctly, but in the end nosetests crashes, which is worrying me. Should really try to dig out what is the reason behind that.

I don’t have any concrete plans (I pretty much never have those anyway) for what next release holds, but it might be nice to continue with the user interface and add ability to wield weapons again. Could also continue with the crypt area and add more features there (rooms, monsters, items).

First steps with PyQt

I have meant to do some user interface programming for a while already, but the problem has been that I didn’t know enough of PGU to be able to do anything remotely useful in short period of time. That coupled with fact that I don’t know much about user interface design to begin with made it really hard to start though.

Couple days ago I gave a try to PyQt for a fun and results were more than encouraging. In a short period of time I had new user interface, with almost same functionality as before. I can walk around, fight monsters and get feedback from the world in form of textual events. Walking stairs doesn’t work correctly yet (well, walking works, but the displayed graphics isn’t updated correctly) and I’m missing inventory and line of sight routines. I’m pretty confident though that I’m going to continue with PyQt instead of PGU.

Sadly, PyQy doesn’t seem to have support for joysticks out of the box and the user interface I’m currently working with doesn’t really yield itself for joystick that well. I’m probably going to drop plan for joypad playable roguelike and return to it later, if I still feel it’s a good idea.