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 * 32 + 16, target.location * 32) animation = QPropertyAnimation(damage_counter.adapter, 'y_location') animation.setDuration(1500) animation.setStartValue(target.location * 32) animation.setEndValue(target.location * 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.