This is tutorial number 7 in our Pygame RPG series.
The one major component our Player is missing currently is an attacking system. Luckily it’s pretty similar to how our Movement animation system was implemented so this tutorial should be pretty easy to follow.
Once we’ve created this basic attack system, we can easily expand it to include many other types of attacks and abilities as well. This is something we will explore in later tutorials in this series.
The Attack animations for the Player must be handled in the same way we loaded the Movement animations. No explanation is required here; for any confusion refer to the Movement Tutorial.
# Attack animation for the RIGHT attack_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Attack_R.png"), pygame.image.load("Player_Attack2_R.png"),pygame.image.load("Player_Attack2_R.png"), pygame.image.load("Player_Attack3_R.png"),pygame.image.load("Player_Attack3_R.png"), pygame.image.load("Player_Attack4_R.png"),pygame.image.load("Player_Attack4_R.png"), pygame.image.load("Player_Attack5_R.png"),pygame.image.load("Player_Attack5_R.png"), pygame.image.load("Player_Sprite_R.png")] # Attack animation for the LEFT attack_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Attack_L.png"), pygame.image.load("Player_Attack2_L.png"),pygame.image.load("Player_Attack2_L.png"), pygame.image.load("Player_Attack3_L.png"),pygame.image.load("Player_Attack3_L.png"), pygame.image.load("Player_Attack4_L.png"),pygame.image.load("Player_Attack4_L.png"), pygame.image.load("Player_Attack5_L.png"),pygame.image.load("Player_Attack5_L.png"), pygame.image.load("Player_Sprite_L.png")]
One small note: We have actually repeated several of the animations in the above lists. The reason is that we lacked enough attacking frames, so the attack ends too quickly to be seen properly. Hence we duplicated some frames to produce a more smoother and visible effect.
Remember if there are 10 frames and the game is running at 60 frames per second. A single attack will end in 1/6 of a second. Ideally one should have about 15 – 20 for a smooth and fluid attack.
Next we’re going to be adding some new variables into the Player class for combat related code. If you’ve gone through the previous tutorials properly, you’ll know what these two are for (state check and frame check).
# Combat self.attacking = False self.attack_frame = 0
Finally, our actual attack method to be added into the Player class. The concept is very similar to that of the
update() method, as both are incharge of cycling through frames.
def attack(self): # If attack frame has reached end of sequence, return to base frame if self.attack_frame > 10: self.attack_frame = 0 self.attacking = False # Check direction for correct animation to display if self.direction == "RIGHT": self.image = attack_ani_R[self.attack_frame] elif self.direction == "LEFT": self.correction() self.image = attack_ani_L[self.attack_frame] # Update the current attack frame self.attack_frame += 1
The gist of it is as follows: The first code block checks to see if all attack frames have been executed. If so, it resets the frame back to original position. The second code block
This is a little extra method we had to create due to a small “bug” in our images. Pygame unfortunately doesn’t have a lot of support for individual entities and non-rectangle collision detection. Basically, when we turn our character from right to left and attack, the center point of the image changes, pushing the player back.
You can try this out yourself by removing the method and running the code. This issue could likely be rectified with someone who has more skill in image editing. This tutorial will be updated when an appropriate solution is found.
def correction(self): # Function is used to correct an error # with character position on left attack frames if self.attack_frame == 1: self.pos.x -= 20 if self.attack_frame == 10: self.pos.x += 20
The summarize, what the above function does is to adjust the position of the Player by 20 pixels during the left attack in order to cancel out the (approximately) 20 pixels error that occurs.
Updating the Game Loop
Why is there an if statement that checks if
player.attacking == False? It’s because we don’t want to player to be able to execute another attack till the first is over. You may tweak this setting based on your type of game though.
if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: player.jump() if event.key == pygame.K_RETURN: if player.attacking == False: player.attack() player.attacking = True
The key we have assigned the attack button is the “Enter” key, represented in Pygame by
Why is it nessacery to keep calling the
attack() method as shown below? Well, when we click the attack button, it only triggers it once. In other words, it only advances the player by one attack frame.
# Player related functions player.update() if player.attacking == True: player.attack() player.move()
In order to ensure it keeps calling itself every frame until all attack frames have been executed, is to set
player.attacking to True and add the above if statement.
A small 10 second clip showing off the attack animations on our Player. If the animation seems a bit clunky, it’s more because of the screen recorder rather than the animation itself (which is actually pretty smooth).
As promised, we’ll now move on to other elements within the game. The second most important “feature” in this game, is the Enemy class. The next 4 – 5 Tutorials will all deal with creating the Enemy class and creating a level system to generate enemies accordingly.
This marks the end of the Pygame RPG Tutorial – Attack Animations. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the article content can be asked in the comments section below.