Movatterモバイル変換


[0]ホーム

URL:


How to Create a Space Invaders Game in Python

Master game development with Python in this engaging tutorial, where you'll learn how to create your own Space Invaders game using Pygame. From setting up your environment to crafting game mechanics with ships, aliens, and bullets, this guide is packed with practical steps and essential coding insights. Ideal for begin
  · 16 min read · Updated nov 2023 ·Game Development

Kickstart your coding journey with ourPython Code Assistant. An AI-powered assistant that's always ready to help. Don't miss out!

Space Invaders is a classic arcade game that has entertained gamers for decades, and now you can build your version of this iconic title.

In this tutorial, we will cover the essentials ofgame development with Pygame, from setting up your development environment to creating the game's core mechanics. By the end of this tutorial, you'll have a fully functional Space Invaders game that you can customize and expand upon to make it your own. Let's dive into the first step: setting up your development environment.

Installation and Setup

Let's start by making sure Pygame is installed on your computer. Head to your terminal and installpygame module usingpip:

$ pip install pygame

After that, create a directory for the game and create the following.py file inside it;settings.py,main.py,world.py,display.py,alien.py,ship.py, andbullet.py. Create a folder inside our game directory and call itassets, where we will store our game assets, such as sprites. Here is the file structure of our code:

Now, we can start coding.Let's define our game variables insettings.py:

# /* settings.pyWIDTH, HEIGHT = 720, 450SPACE = 30FONT_SIZE = 20EVENT_FONT_SIZE = 60NAV_THICKNESS = 50CHARACTER_SIZE  = 30PLAYER_SPEED = 10ENEMY_SPEED = 1 BULLET_SPEED = 15 # for both sidesBULLET_SIZE = 10

You can always tweak these parameters based on your needs.Next, let's create the main class of our game. This class will be responsible for calling and running the game loop:

# /* main.pyimport pygame, sysfrom settings import WIDTH, HEIGHT, NAV_THICKNESSfrom world import Worldpygame.init()screen = pygame.display.set_mode((WIDTH, HEIGHT + NAV_THICKNESS))pygame.display.set_caption("Space Invader")class Main:    def __init__(self, screen):        self.screen = screen        self.FPS = pygame.time.Clock()    def main(self):        world = World(self.screen)        while True:            self.screen.fill("black")            for event in pygame.event.get():                if event.type == pygame.QUIT:                    pygame.quit()                    sys.exit()                if event.type == pygame.KEYDOWN:                    if event.key == pygame.K_SPACE:                        world.player_move(attack = True)            world.player_move()            world.update()            pygame.display.update()            self.FPS.tick(30)if __name__ == "__main__":    play = Main(screen)    play.main()

From the name itself, theMain class will be the main class of our game. It takes an argument ofscreen which will serve as the game window for animating the game. Themain() function will run and update our game. It will initialize theWorld first (our game world). To keep the game running without intentionally exiting, we put awhile loop inside it. Inside our loop, we place another loop (thefor loop), which will catch all the events going on inside our game window, especially when the player hits the exit button.

Adding Game Characters

The Ship

Let's create a class for game characters, theShip class for the player's character, and theAlien class for the opponents of the player's character. Let's create theShip class first:

# /* ship.pyimport pygamefrom settings import PLAYER_SPEED, BULLET_SIZEfrom bullet import Bulletclass Ship(pygame.sprite.Sprite):    def __init__(self, pos, size):        super().__init__()        self.x = pos[0]        self.y = pos[1]        # ship info         img_path = 'assets/ship/ship.png'        self.image = pygame.image.load(img_path)        self.image = pygame.transform.scale(self.image, (size, size))        self.rect = self.image.get_rect(topleft = pos)        self.mask = pygame.mask.from_surface(self.image)        self.ship_speed = PLAYER_SPEED        # ship status        self.life = 3        self.player_bullets = pygame.sprite.Group()    def move_left(self):        self.rect.x -= self.ship_speed    def move_up(self):        self.rect.y -= self.ship_speed    def move_right(self):        self.rect.x += self.ship_speed    def move_bottom(self):        self.rect.y += self.ship_speed    def _shoot(self):        specific_pos = (self.rect.centerx - (BULLET_SIZE // 2), self.rect.y)        self.player_bullets.add(Bullet(specific_pos, BULLET_SIZE, "player"))    def update(self):        self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))

We start by importing thepygame module and variablesPLAYER_SPEED, andBULLET_SIZE insettings.py. We also import theBullet class, which we'll create later.

TheShip class represents the player's spaceship in the game. It inherits frompygame.sprite.Sprite, indicating that it's a game object that can be easily managed within Pygame. The constructor__init__() sets up the ship's initial properties, such as its position, image, mask, speed, and other attributes.

The ship's position and collision mask are also initialized. The speed of the ship is set to the value defined inPLAYER_SPEED. The ship's life is initialized to 3, and a group for player bullets is created. The functionsmove_left(),move_up(),move_right(), andmove_bottom() are responsible for the ship's movement and the_shoot(), from the name itself, is responsible for shooting. To apply all the changes in movement, we have theupdate() function to do so.

The Aliens

Now let's move on to creating the class for our opponents, theAlien class:

# /* alien.pyimport pygamefrom settings import BULLET_SIZEfrom bullet import Bulletclass Alien(pygame.sprite.Sprite):    def __init__(self, pos, size, row_num):        super().__init__()        self.x = pos[0]        self.y = pos[1]        # alien info        img_path = f'assets/aliens/{row_num}.png'        self.image = pygame.image.load(img_path)        self.image = pygame.transform.scale(self.image, (size, size))        self.rect = self.image.get_rect(topleft = pos)        self.mask = pygame.mask.from_surface(self.image)        self.move_speed = 5        self.to_direction = "right"        # alien status        self.bullets = pygame.sprite.GroupSingle()    def move_left(self):        self.rect.x -= self.move_speed    def move_right(self):        self.rect.x += self.move_speed    def move_bottom(self):        self.rect.y += self.move_speed    def _shoot(self):        specific_pos = (self.rect.centerx - (BULLET_SIZE // 2), self.rect.centery)        self.bullets.add(Bullet(specific_pos, BULLET_SIZE, "enemy"))    def update(self):        self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))

TheAlien class works the same as theShip class. The only difference between them is they have different movement speeds, no ability to move backward, and can only shoot one after the other explodes or out of the frame.

The Bullets

Now, let's move on to making theBullet class:

# /* bullet.pyimport pygamefrom settings import BULLET_SPEED, HEIGHTclass Bullet(pygame.sprite.Sprite):    def __init__(self, pos, size, side):        super().__init__()        self.x = pos[0]        self.y = pos[1]        # bullet info        img_path = f'assets/bullet/{side}-bullet.png'        self.image = pygame.image.load(img_path)        self.image = pygame.transform.scale(self.image, (size, size))        self.rect = self.image.get_rect(topleft = pos)        self.mask = pygame.mask.from_surface(self.image)        # different bullet movement direction for both player and enemy (alien)        if side == "enemy":            self.move_speed = BULLET_SPEED        elif side == "player":            self.move_speed = (- BULLET_SPEED)    def _move_bullet(self):        self.rect.y += self.move_speed    def update(self):        self._move_bullet()        self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))        # delete the bullet if it get through out of the screen        if self.rect.bottom <= 0 or self.rect.top >= HEIGHT:            self.kill()

TheBullet class is responsible for managing the bullets fired by the player and enemy ships.

Inside theBullet class constructor (__init__()), several properties are set up. These properties include the bullet's position, image, collision mask, and movement speed.

The image of the bullet is loaded from a file path based on the "side" parameter. For example, if it's a player bullet, it loads'assets/bullet/player-bullet.png'. The image is then scaled to the specified size, and the initial position and collision mask are configured. The direction of the bullet's movement is determined based on its"side".

The_move_bullet() method is responsible for updating the bullet's position during the game. It adjusts the bullet's vertical (y) coordinate based on its movement speed, which controls its direction. If it's a player bullet, it moves upward; if it's an enemy bullet, it moves downward. Theupdate() method is called regularly in the game loop to update the bullet's position and check if it has moved out of the visible screen area. If the bullet goes beyond the screen boundaries (either above or below), it is removed from the game using thekill() method. This ensures that bullets that have fulfilled their purpose or missed their target are not visible or active in the game, maintaining a clean and efficient game environment.

Creating the Game World

Let's move on to making theWorld class. TheWorld class coordinates various game elements, controls gameplay mechanics, and ensures that the game responds to player input, handles collisions, and progresses through levels:

# /* world.pyimport pygamefrom ship import Shipfrom alien import Alienfrom settings import HEIGHT, WIDTH, ENEMY_SPEED, CHARACTER_SIZE, BULLET_SIZE, NAV_THICKNESSfrom bullet import Bulletfrom display import Displayclass World:    def __init__(self, screen):        self.screen = screen        self.player = pygame.sprite.GroupSingle()        self.aliens = pygame.sprite.Group()        self.display = Display(self.screen)        self.game_over = False        self.player_score = 0        self.game_level = 1        self._generate_world()

In the class constructor, several attributes and groups are initialized. These include the game screen (self.screen), the player's ship (self.player), the aliens (self.aliens), and a display object (self.display) used to show information like the player's score and game level. The constructor also initializes game over status, player score, and game-level variables. It also calls a method named_generate_world(), to set up the initial game state:

# /* world.py    def _generate_aliens(self):        # generate opponents        alien_cols = (WIDTH // CHARACTER_SIZE) // 2        alien_rows = 3        for y in range(alien_rows):            for x in range(alien_cols):                my_x = CHARACTER_SIZE * x                my_y = CHARACTER_SIZE * y                specific_pos = (my_x, my_y)                self.aliens.add(Alien(specific_pos, CHARACTER_SIZE, y))            # create and add player to the screen    def _generate_world(self):        # create the player's ship        player_x, player_y = WIDTH // 2, HEIGHT - CHARACTER_SIZE        center_size = CHARACTER_SIZE // 2        player_pos = (player_x - center_size, player_y)        self.player.add(Ship(player_pos, CHARACTER_SIZE))        self._generate_aliens()

The_generate_aliens() method is responsible for creating and positioning the enemy aliens in the game world. It calculates the number of alien columns and rows based on the game's width and character size and iterates through the grid to create aliens at specific positions.

The_generate_world() method is used to set up the initial game world. It creates the player's ship at the center of the screen and generates the aliens using the_generate_aliens() method.

# /* world.py    def add_additionals(self):        # add nav bar        nav = pygame.Rect(0, HEIGHT, WIDTH, NAV_THICKNESS)        pygame.draw.rect(self.screen, pygame.Color("gray"), nav)        # render player's life, score and game level        self.display.show_life(self.player.sprite.life)        self.display.show_score(self.player_score)        self.display.show_level(self.game_level)

Theadd_additionals() method adds additional elements to the game, including a navigation bar at the bottom of the screen, and renders information about the player's life, score, and game level using thedisplay object:

# /* world.py    def player_move(self, attack = False):        keys = pygame.key.get_pressed()        if keys[pygame.K_a] and not self.game_over or keys[pygame.K_LEFT] and not self.game_over:            if self.player.sprite.rect.left > 0:                self.player.sprite.move_left()        if keys[pygame.K_d] and not self.game_over or keys[pygame.K_RIGHT] and not self.game_over:            if self.player.sprite.rect.right < WIDTH:                self.player.sprite.move_right()        if keys[pygame.K_w] and not self.game_over or keys[pygame.K_UP] and not self.game_over:            if self.player.sprite.rect.top > 0:                self.player.sprite.move_up()        if keys[pygame.K_s] and not self.game_over or keys[pygame.K_DOWN] and not self.game_over:            if self.player.sprite.rect.bottom < HEIGHT:                self.player.sprite.move_bottom()        # game restart button        if keys[pygame.K_r]:            self.game_over = False            self.player_score = 0            self.game_level = 1            for alien in self.aliens.sprites():                alien.kill()            self._generate_world()        if attack and not self.game_over:            self.player.sprite._shoot()

Theplayer_move() method handles player ship movement and shooting. It checks for key presses to move the player's ship in different directions (left, right, up, down) and also allows the player to shoot bullets when an attack flag is passed. The player can move by pressing"W","A","S","D" or the arrow keys. To shoot, the player can press theSPACE bar. Additionally, it checks for a game restart command (if the player presses the"R" key):

# /* world.py    def _detect_collisions(self):        # checks if player bullet hits the enemies (aliens)        player_attack_collision = pygame.sprite.groupcollide(self.aliens, self.player.sprite.player_bullets, True, True)        if player_attack_collision:            self.player_score += 10        # checks if the aliens' bullet hit the player        for alien in self.aliens.sprites():            alien_attack_collision = pygame.sprite.groupcollide(alien.bullets, self.player, True, False)            if alien_attack_collision:                self.player.sprite.life -= 1                break        # checks if the aliens hit the player        alien_to_player_collision = pygame.sprite.groupcollide(self.aliens, self.player, True, False)        if alien_to_player_collision:            self.player.sprite.life -= 1

The_detect_collisions() method checks for collisions between player bullets and enemy aliens and between alien bullets and the player's ship. It updates the player's score and decreases the player's life accordingly:

# /* world.py    def _alien_movement(self):        move_sideward = False        move_forward = False        for alien in self.aliens.sprites():            if alien.to_direction == "right" and alien.rect.right < WIDTH or alien.to_direction == "left" and alien.rect.left > 0:                move_sideward = True                move_forward = False            else:                move_sideward = False                move_forward = True                alien.to_direction = "left" if alien.to_direction == "right" else "right"                break        for alien in self.aliens.sprites():            if move_sideward and not move_forward:                if alien.to_direction == "right":                    alien.move_right()                if alien.to_direction == "left":                    alien.move_left()            if not move_sideward and move_forward:                    alien.move_bottom()    def _alien_shoot(self):        for alien in self.aliens.sprites():            if (WIDTH - alien.rect.x) // CHARACTER_SIZE == (WIDTH - self.player.sprite.rect.x) // CHARACTER_SIZE:                alien._shoot()                break

The_alien_movement() method controls the movement of enemy aliens. It determines whether the aliens should move sideward or forward based on whether they've reached the screen's edges. This method also manages the change in direction for the aliens.

The_alien_shoot() method allows enemy aliens to shoot bullets at the player. It checks the alignment of the player and aliens to determine when the aliens should fire:

# /* world.py    def _check_game_state(self):        # check if game over        if self.player.sprite.life <= 0:            self.game_over = True            self.display.game_over_message()        for alien in self.aliens.sprites():            if alien.rect.top >= HEIGHT:                self.game_over = True                self.display.game_over_message()                break        # check if next level        if len(self.aliens) == 0 and self.player.sprite.life > 0:            self.game_level += 1            self._generate_aliens()            for alien in self.aliens.sprites():                alien.move_speed += self.game_level - 1

The_check_game_state() method monitors the game's state. It checks if the game should end (game over) when the player's life reaches zero or if any alien reaches the bottom of the screen. If all aliens are defeated, the game progresses to the next level:

# /* world.py    def update(self):        # detecting if bullet, alien, and player group is colliding        self._detect_collisions()        # allows the aliens to move        self._alien_movement()        # allows alien to shoot the player        self._alien_shoot()        # bullets rendering        self.player.sprite.player_bullets.update()        self.player.sprite.player_bullets.draw(self.screen)        [alien.bullets.update() for alien in self.aliens.sprites()]        [alien.bullets.draw(self.screen) for alien in self.aliens.sprites()]        # player ship rendering        self.player.update()        self.player.draw(self.screen)        # alien rendering        self.aliens.draw(self.screen)        # add nav        self.add_additionals()        # checks game state        self._check_game_state()

Theupdate() method is the core game loop. It updates various game elements, such as bullet positions, alien movements, player ship rendering, alien rendering, and additional elements like the navigation bar. It also checks the game state to determine if the game should continue, end, or progress to the next level.

Adding Game Display

Lastly, create another class indisplay.py and call it theDisplay class. TheDisplay class manages the rendering of in-game elements, including player lives, scores, game levels, and event messages like"GAME OVER". These elements are essential for conveying important information to the player, and the class provides a structured way to handle their presentation on the game screen:

# /* display.pyimport pygamefrom settings import WIDTH, HEIGHT, SPACE, FONT_SIZE, EVENT_FONT_SIZEpygame.font.init()class Display:    def __init__(self, screen):        self.screen = screen        self.score_font = pygame.font.SysFont("monospace", FONT_SIZE)        self.level_font = pygame.font.SysFont("impact", FONT_SIZE)        self.event_font = pygame.font.SysFont("impact", EVENT_FONT_SIZE)        self.text_color = pygame.Color("blue")        self.event_color = pygame.Color("red")    def show_life(self, life):        life_size = 30        img_path = "assets/life/life.png"        life_image = pygame.image.load(img_path)        life_image = pygame.transform.scale(life_image, (life_size, life_size))        life_x = SPACE // 2        if life != 0:            for life in range(life):                self.screen.blit(life_image, (life_x, HEIGHT + (SPACE // 2)))                life_x += life_size    def show_score(self, score):        score_x = WIDTH // 3        score = self.score_font.render(f'score: {score}', True, self.text_color)        self.screen.blit(score, (score_x, (HEIGHT + (SPACE // 2))))    def show_level(self, level):        level_x = WIDTH // 3        level = self.level_font.render(f'Level {level}', True, self.text_color)        self.screen.blit(level, (level_x * 2, (HEIGHT + (SPACE // 2))))    def game_over_message(self):        message = self.event_font.render('GAME OVER!!', True, self.event_color)        self.screen.blit(message, ((WIDTH // 3) - (EVENT_FONT_SIZE // 2), (HEIGHT // 2) - (EVENT_FONT_SIZE // 2)))

We start by importing essential modules and constants, such aspygame for game development and various settings likeWIDTH,HEIGHT,SPACE,FONT_SIZE, andEVENT_FONT_SIZE. Thepygame.font.init() function is called to initialize the pygame font module, enabling the code to work with fonts for rendering text.

TheDisplay class is introduced to manage the rendering of text and visual elements on the game screen. It takes the game screen (screen) as an argument, allowing it to handle display-related tasks. In the constructor, several attributes are initialized. These includeself.screen, which stores the game screen, and fonts (self.score_font,self.level_font, andself.event_font) for displaying text. The font styles and sizes are specified, and colors for regular text (self.text_color) and event text (self.event_color) are defined.

Theshow_life() method is responsible for presenting the player's remaining lives visually. It loads a life image from a file, scales it to a specific size, and then arranges multiple life images in a row at the bottom of the screen, corresponding to the player's remaining lives.

Theshow_score() method is designed to render and display the player's score on the screen. It creates a text surface using the player's score, applying thescore_font and positioning it at a specific location on the screen.

Theshow_level() method is used to visually represent the current game level. It renders the game level as text usinglevel_font and places it in a designated position on the screen.

Thegame_over_message() method is responsible for displaying a"GAME OVER" message on the screen when the game ends. It creates a text surface for the message usingevent_font and positions it at the center of the screen.

And we are now done coding! To try our game, open your terminal, head to the game directory, then runpython main.py to call the game.

Here are some game snapshots:

Running the Game

Game while Playing

Different Game Level

Game over

Here's a video of me playing the game:

Conclusion

In conclusion, this tutorial has provided a detailed examination of the Pygame implementation of the classic Space Invaders game. We've delved into the code, dissecting its key components, from player mechanics to enemy behavior and game presentation. With this knowledge, you are well-prepared to modify, expand, and create your versions of Space Invaders or other game projects using Pygame.

You can getthe complete code here.

Here are some other game development tutorials:

Happy coding ♥

Finished reading? Keep the learning going with ourAI-powered Code Explainer. Try it now!

View Full Code Switch My Framework
Sharing is caring!



Read Also


How to Create a Pong Game in Python
How to Make a Flappy Bird Game in Python
How to Create a Platformer Game in Python

Comment panel

    Got a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!





    Ethical Hacking with Python EBook - Topic - Top


    Join 50,000+ Python Programmers & Enthusiasts like you!



    Tags


    New Tutorials

    Popular Tutorials


    Ethical Hacking with Python EBook - Topic - Bottom

    CodingFleet - Topic - Bottom






    Claim your Free Chapter!

    Download a Completely Free Ethical hacking with Python from Scratch Chapter.

    See how the book can help you build awesome hacking tools with Python!



    [8]ページ先頭

    ©2009-2025 Movatter.jp