Pygame RPG Tutorial – Magic Attacks

This is the Magic Attacks Tutorial (19) in the Pygame RPG series.

Remember that mana system we created a few tutorials back? Well we’re going to be using it now, by implementing Magic Attacks into our Pygame RPG. The idea is that a fixed number of mana is required to use a Magic attack. And as we explained earlier in the series, we gain mana by defeating enemies.


Magic FireBall Class

Most of our work here is going to be based around the FireBall Class. Almost all the fireball code is going to be written here.

The first thing we do is acquire the position of the player, and save it in self.direction. We obviously want the direction of the fireball to be in the direction that the Player is facing. Building upon this, we will load one of two images into the fireball.

Pygame RPG - Magic Fireball Right
Pygame RPG - Magic Fireball Right
class FireBall(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.direction  = player.direction
            if self.direction == "RIGHT":
                  self.image = pygame.image.load("fireball1_R.png")
            else:
                  self.image = pygame.image.load("fireball1_L.png")           
            self.rect = self.image.get_rect(center = player.pos)
            self.rect.x = player.pos.x
            self.rect.y = player.pos.y - 40

Finally, we center the image around the position of the player, and then create a rect out of it. We then update the x and y positions of the rect to that of the player, while adding a slight offset to make sure that it appears from exactly the center of the player. (See the Video at the bottom for a demonstration)

Firing the Fireball

Next up is the fire() function. This is both a “move” and “render” function at the same time. Basically, we didn’t create a separate render() function, we just put the blit() function in here. We draw either the left fireball image or right fireball image, depending on the direction of the Player.

It’s similar to the StageDisplay and StageClear functions that we created earlier. As long as the fireball is within the range of -10 and 710, we’re going to keep drawing and moving the fireball forward. Otherwise, if it falls out of this range, we’re going to delete delete it using the self.kill() function.

      def fire(self):
            player.magic_cooldown = 0
            # Runs while the fireball is still within the screen w/ extra margin
            if -10 < self.rect.x < 710:
                  if self.direction == "RIGHT":
                        self.image = pygame.image.load("fireball1_R.png")
                        displaysurface.blit(self.image, self.rect)
                  else:
                        self.image = pygame.image.load("fireball1_L.png")
                        displaysurface.blit(self.image, self.rect)
                        
                  if self.direction == "RIGHT":
                        self.rect.move_ip(12, 0)
                  else:
                        self.rect.move_ip(-12, 0)   
            else:
                  self.kill()
                  player.magic_cooldown = 1
                  player.attacking = False

We’ve used the move_ip() function on the rect of the fireball, which “moves in place”. Since we pass 12 or -12 (depending on direction), it moves the fireball forward (in either the left or right direction).

Every time the fire function is called, the fireball will move with a velocity of magnitude 12. As we have 60 Frames per second, this means that the fireball will cover 720 pixels in a second. In short, it will take the fireball around a second to reach from one end of the screen to the other.


Enemy Class

Finally, we have to update the enemy code. Previously, we just had to check for collisions with the Player. This time however, we need to account for fireballs as well. Hence, we add a new spritecollide function, storing the collision values in f_hits.

Most of the code is the same, we just included f_hits on line 6 and line 9.

      def update(self):
            # Checks for collision with the Player
            hits = pygame.sprite.spritecollide(self, Playergroup, False)

            # Checks for collision with Fireballs
            f_hits = pygame.sprite.spritecollide(self, Fireballs, False)

            # Activates if either of the two expressions is true
            if hits and player.attacking == True or f_hits:
                  self.kill()
                  if player.mana < 100: player.mana += self.mana # Release mana
                  player.experiance += 1   # Release expeiriance

            # If collision occurred and player not attacking, call "hit" func           
            elif hits and player.attacking == False:
                  player.player_hit()

The gist of it is that now the enemy will be checking for collisions with both fireballs and the Player. Which is why, on line 9, the code block for the kill code activates if either f_hits or hits is true.


Game Loop

Here we create a group to store all the fireballs, called Fireballs. We’ll use this later on, like we used the Enemies group.

Fireballs = pygame.sprite.Group()

Finally, we’re down to the part of the code, where the fireball is actually called, created and added into the Fireballs group.

As you can probably see, the below block of code is located within the game loop, where we check for keyboard presses. We’ve kept the “m” key as the trigger for the fireball. Of course, you may keep it as something different.

        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_m and player.magic_cooldown == 1:
                  if player.mana >= 6:
                        player.mana -= 6
                        player.attacking = True
                        fireball = FireBall()
                        Fireballs.add(fireball)

We’ve kept the mana cost for a single fireball at 6. Of course, you can adjust the value as you see fit. The magic cooldown is another limiting feature we’ve placed upon the fireballs. Basically, it prevents you from casting two fireballs at once. This is more of an optional feature, that you may want to remove.

Now that we have a group where all our fireballs exist, we can simply iterate over it and call the fire() command on them all.

    for ball in Fireballs:
          ball.fire()
    
    for entity in Enemies:
          entity.update()
          entity.move()
          entity.render()

(The enemies code is just there to indicate where you should be putting it)


Demo Clip

Now for the part I’m sure you all want to see. The below video is a 30 second clip showing off the Fireball mechanic.


New Section

In the next section we’ll work on adding a new level to our Pygame RPG. As with any new level, it will also have new enemies, mechanics and environment. (Coming soon)


This marks the end of the Pygame RPG – Magic Attacks Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments