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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s