Career Paths

My Own Space Invaders

Enter the world of game development by creating a classic arcade game from scratch with the Pygame library.

Our Project: Simple Space Invaders Game

A 2D arcade game where the player controls a spaceship at the bottom of the screen, moves left-right, and shoots at descending enemies.

Core Technologies We'll Use:

Python
Pygame
OOP
Step 1 / 6

Step 1: Pygame Installation and Basic Window Structure

We install the Pygame library and create the basic code to open a game window.

What is Pygame?

Pygame is a set of Python modules designed for writing video games. It provides functionality for graphics, sound, and user input (keyboard, mouse), making 2D game development much easier.

1. Pygame Installation

Open your terminal, create a new folder and virtual environment, and install pygame:

mkdir space_invaders_game
cd space_invaders_game
python -m venv venv
# Activation...
pip install pygame

2. The Main Game Loop

Every game is based on a main loop that runs continuously. This loop does three basic things in each frame:

  1. Event Handling: Checks for player actions (key presses, clicks) or system events (closing the window).
  2. Update: Updates the state of all game objects (player movement, enemies, etc.).
  3. Draw/Render: Draws everything to the screen.

Create a file space_invaders.py and add the following code to create a simple black window.


import pygame

# --- Αρχικοποίηση Pygame ---
pygame.init()

# --- Σταθερές Παιχνιδιού ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BLACK = (0, 0, 0)

# --- Ρύθμιση Οθόνης ---
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Space Invaders")
clock = pygame.time.Clock() # Για τον έλεγχο του ρυθμού ανανέωσης

# --- Κύριος Βρόχος Παιχνιδιού ---
running = True
while running:
    # 1. Χειρισμός Εισόδου (Events)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: # Αν ο χρήστης κλείσει το παράθυρο
            running = False

    # 2. Ενημέρωση (Update) - (Θα προστεθεί αργότερα)

    # 3. Σχεδίαση (Draw / Render)
    screen.fill(BLACK) # Γέμισμα της οθόνης με μαύρο χρώμα

    pygame.display.flip() # Ανανέωση της οθόνης για να φανούν οι αλλαγές

    # Διατήρηση σταθερού ρυθμού ανανέωσης (60 καρέ ανά δευτερόλεπτο)
    clock.tick(60)

# --- Τερματισμός Pygame ---
pygame.quit()
Step 2 / 6

Step 2: Creating the Player Class

We use the principles of OOP to create a Player class, which will manage the player's spaceship.

Pygame heavily uses the concept of "Sprites". A sprite is simply a 2D image or object in your game. The pygame.sprite.Sprite class provides an excellent base for our game objects.

We will create a Player class that inherits from pygame.sprite.Sprite. In its __init__:

  • super().__init__(): It's important to call the parent class's constructor.
  • self.image: This is the "Surface" that will be drawn on the screen. Here, we create a simple green rectangle.
  • self.rect: This is the rectangle that surrounds the image. It's crucial, as Pygame uses it for positioning and collision detection.

Next, we'll add the Player class to our code and create an object from it. We'll also use a pygame.sprite.Group, which is a container for managing and drawing multiple sprites at once.


# (Κάτω από τις σταθερές, πριν τον game loop)

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([50, 40])
        self.image.fill((0, 255, 0)) # Πράσινο χρώμα
        self.rect = self.image.get_rect()
        self.rect.centerx = SCREEN_WIDTH // 2
        self.rect.bottom = SCREEN_HEIGHT - 10

# ... (Μέσα στον game loop) ...

# (Πριν τον game loop)
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

# (Μέσα στο τμήμα Draw του game loop)
# ...
all_sprites.draw(screen) # Σχεδιάζει όλα τα sprites στην οθόνη
pygame.display.flip()
# ...
Step 3 / 6

Step 3: Player Movement

We add logic to the Player class's update() method to handle keyboard input and move the spaceship left and right.

To make the game interactive, we need to update the player's position in each frame. This is done inside the update() method of the Player class, which is automatically called for every sprite in a Group when we call all_sprites.update().

Inside update():

  • pygame.key.get_pressed(): Returns a list with the state of all keys.
  • We check if the left (K_LEFT) or right (K_RIGHT) arrow key is pressed.
  • We update the player's self.rect.x position.
  • We add checks to ensure the player does not go off-screen.

Finally, we call all_sprites.update() inside the main game loop, in the "Update" section.


# (Μέσα στην κλάση Player)
    def update(self):
        self.speed_x = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT]:
            self.speed_x = -8
        if keystate[pygame.K_RIGHT]:
            self.speed_x = 8
        self.rect.x += self.speed_x
        
        # Περιορισμός κίνησης εντός οθόνης
        if self.rect.right > SCREEN_WIDTH:
            self.rect.right = SCREEN_WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

# (Μέσα στο τμήμα Update του game loop)
all_sprites.update()
Step 4 / 6

Step 4: Shooting

We implement the shooting functionality by creating a new Bullet class and adding a shoot() method to the player.

Now let's add the action! We'll create a new Bullet class, similar to Player, which will represent the bullets. Its update() method will simply move the bullet upwards and "kill" it (self.kill()) if it goes off-screen to conserve resources.

In the Player class, we will add a shoot() method. This will create a new Bullet object and add it to two groups: all_sprites (for drawing) and a new group, bullets (for collision checking).

Finally, in the game loop, we'll check if the player presses the SPACE key and, if so, call the player.shoot() method.


# (Προσθήκη νέας κλάσης)
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([5, 15])
        self.image.fill((255, 255, 255)) # Άσπρο
        self.rect = self.image.get_rect()
        self.rect.bottom = y
        self.rect.centerx = x
        self.speed_y = -10

    def update(self):
        self.rect.y += self.speed_y
        if self.rect.bottom < 0:
            self.kill()

# (Μέσα στην κλάση Player)
    def shoot(self):
        bullet = Bullet(self.rect.centerx, self.rect.top)
        all_sprites.add(bullet)
        bullets.add(bullet)

# (Πριν τον game loop)
bullets = pygame.sprite.Group()

# (Μέσα στο event handling του game loop)
    # ...
    elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            player.shoot()
Step 5 / 6

Step 5: Creating Enemies and Collision Detection

We create an Enemy class, add enemies to the screen, and implement the logic for detecting collisions between bullets and enemies.

We will create an Enemy class. The enemies will appear at random positions above the screen and move downwards. If they go off-screen, they will "reappear" at the top.

The most important step here is collision detection. Pygame makes this very easy with the pygame.sprite.groupcollide() function. It takes two sprite groups (here, enemies and bullets) and two Booleans. If the Booleans are True, the colliding sprites are automatically deleted. We will use this to remove both the enemy and the bullet upon collision.

We will also add a score variable, which will increase every time an enemy is destroyed, and we will draw it on the screen.


# (Προσθήκη νέας κλάσης)
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([40, 30])
        self.image.fill((255, 0, 0)) # Κόκκινο
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randrange(-100, -40)
        self.speed_y = random.randrange(1, 4)

    def update(self):
        self.rect.y += self.speed_y
        if self.rect.top > SCREEN_HEIGHT + 10:
            self.rect.x = random.randrange(SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)

# (Πριν τον game loop)
enemies = pygame.sprite.Group()
for i in range(8):
    enemy = Enemy()
    all_sprites.add(enemy)
    enemies.add(enemy)
score = 0
font = pygame.font.Font(None, 36)

# (Μέσα στο τμήμα Update του game loop)
# Έλεγχος σύγκρουσης σφαίρας με εχθρό
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
for hit in hits:
    score += 10
    # Δημιουργία νέου εχθρού για να συνεχιστεί το παιχνίδι
    new_enemy = Enemy()
    all_sprites.add(new_enemy)
    enemies.add(new_enemy)

# (Μέσα στο τμήμα Draw του game loop)
# ...
score_text = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_text, (10, 10))
pygame.display.flip()
Step 6 / 6

Step 6: Game Over and Final Touches

We finish the game by adding the end condition (player-enemy collision) and doing a final code review.

The final step is to define when the game ends. This will happen when an enemy collides with the player.

We will use the pygame.sprite.spritecollide() function, which checks if a specific sprite (the player) collides with any sprite from a group (the enemies). If it returns a list of collisions (i.e., if the list is not empty), we will set the running variable to False, ending the main game loop.

Congratulations! You have created a complete, albeit simple, game with Pygame. You can now extend this code by adding sounds, more enemy types, power-ups, or a start screen.


# (Μέσα στο τμήμα Update του game loop, μετά τον έλεγχο για τις σφαίρες)

# Έλεγχος σύγκρουσης παίκτη με εχθρό
player_hits = pygame.sprite.spritecollide(player, enemies, False) # Το False σημαίνει να μην διαγραφεί ο εχθρός
if player_hits:
    running = False # Τέλος παιχνιδιού

# (Στο τέλος του αρχείου, μετά τον game loop)
pygame.quit()
print(f"Game Over! Final Score: {score}")

Project Completion & Next Steps

Congratulations! You have completed the path and now have the full code for the project.

This is the final, complete code for the application. You can copy it, run it locally on your computer (after installing the necessary libraries with `pip`), and experiment by adding your own features!


# space_invaders.py
import pygame
import random

pygame.init()

SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
WHITE, BLACK, RED, GREEN = (255, 255, 255), (0, 0, 0), (255, 0, 0), (0, 255, 0)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Space Invaders")
clock = pygame.time.Clock()

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([50, 40]); self.image.fill(GREEN)
        self.rect = self.image.get_rect(centerx=SCREEN_WIDTH / 2, bottom=SCREEN_HEIGHT - 10)
        self.speed_x = 0
    def update(self):
        self.speed_x = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT]: self.speed_x = -8
        if keystate[pygame.K_RIGHT]: self.speed_x = 8
        self.rect.x += self.speed_x
        if self.rect.right > SCREEN_WIDTH: self.rect.right = SCREEN_WIDTH
        if self.rect.left < 0: self.rect.left = 0
    def shoot(self):
        bullet = Bullet(self.rect.centerx, self.rect.top)
        all_sprites.add(bullet); bullets.add(bullet)

class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([40, 30]); self.image.fill(RED)
        self.rect = self.image.get_rect(x=random.randrange(SCREEN_WIDTH - 40), y=random.randrange(-100, -40))
        self.speed_y = random.randrange(1, 4)
    def update(self):
        self.rect.y += self.speed_y
        if self.rect.top > SCREEN_HEIGHT + 10:
            self.rect.x = random.randrange(SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([5, 15]); self.image.fill(WHITE)
        self.rect = self.image.get_rect(centerx=x, bottom=y)
        self.speed_y = -10
    def update(self):
        self.rect.y += self.speed_y
        if self.rect.bottom < 0: self.kill()

all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player(); all_sprites.add(player)
for _ in range(8):
    enemy = Enemy(); all_sprites.add(enemy); enemies.add(enemy)
score, font = 0, pygame.font.Font(None, 36)

running = True
while running:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: player.shoot()
    all_sprites.update()
    if pygame.sprite.groupcollide(enemies, bullets, True, True):
        score += 10
        new_enemy = Enemy(); all_sprites.add(new_enemy); enemies.add(new_enemy)
    if pygame.sprite.spritecollide(player, enemies, False): running = False
    screen.fill(BLACK)
    all_sprites.draw(screen)
    screen.blit(font.render(f"Score: {score}", True, WHITE), (10, 10))
    pygame.display.flip()

pygame.quit()
print(f"Game Over! Final Score: {score}")