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.
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
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
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.
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
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
hits is true.
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)
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.
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.