Tuesday, October 29, 2024

A Step-by-step Guide to Creating an Intense FPS Game like The Space Fighter in Python

Chances are, you loved playing video games as a kid. Maybe you even dreamed of creating your own games one day, inspired by the thrill of leveling up and achieving high scores. Well, that day might be today! What are your thoughts on simple FPS – First Person Shooter games, like the ones you often see on Tik-Tok Shorts? All you need is a few hours of your time, some .png images or your own creations, a few sound effects like space sounds, laser blasts, and explosions, and of course, our Python coding help. Python, with its simple syntax and powerful libraries like pygame, makes it easy to create both simple and complex games. The rest is up to your creativity! When you say "game" today, it's a very broad term. There are countless ways to create a game, using many different programming languages and devices. It's not the same whether you're programming a small, medium, or large game.

Whether it's a desktop, web, mobile, or console game. Whether you're creating it alone or with a whole team, for yourself, friends, a client, a company, or a specific market. The possibilities are endless, and today you can start your own startup for just one game, launch your own company, and hire your own experts. But no matter what plans you have in mind, think of this tutorial as the beginning of a small, self-contained desktop game that can be expanded and upgraded to become one of the most popular games on the market. The game we'll be creating today is called The Space Fighter. It's a very popular type of game, especially for those learning the Python programming language, but also for those who are exclusively involved in game creation, regardless of specific requirements like the programming language.

Smiling gamers playing video games

Smiling gamers playing video games

The Space Fighter is a typical action-packed FPS game set in space. Earth is under attack by flying saucers, and your mission is to stop their landing with your rocket. The goal is to survive as long as possible, eliminating enemies and avoiding collisions. The game we'll be creating today is called The Space Fighter. It's a very popular type of game, especially for those learning the Python programming language, but also for those who are exclusively involved in game creation, regardless of specific requirements like the programming language. The Space Fighter is a typical action-packed FPS game set in space. Earth is under attack by flying UFOs, and your mission is to stop their landing with your rocket. For each destroyed UFO, you gain 10 points. You start the game with 5 lives, but you're also rewarded with a new life every 100 points. You lose a life whenever a UFO passes you. The game ends when you lose all your lives.

While the basic game is straightforward and ready to play, there's a vast potential for customization. The game is designed to be a starting point. It's simple enough to understand and play quickly, but it's also flexible enough to be expanded upon. You can enhance the game with features like user logins, high score tracking, and difficulty levels. Imagine adding a menu system, allowing players to choose their preferred difficulty and customize their spaceship. You could introduce various enemy types with different attack patterns and health bars. Or perhaps you'd like to implement a level system with increasing challenges. The sky's the limit when it comes to the possibilities for this game. It's important to note that this game, despite its simplicity, can be easily adapted to mobile platforms using frameworks like Kivy and BeeWare without the need to rewrite the core Python code. This flexibility makes it a great candidate for commercialization, too.

Under the Hood of The Space Fighter: A Deep Dive into Programming and Design

Let's get ready to blast off! Before we dive into the Python code for The Space Fight, we'll need to gather some essential assets. Here's what you'll need:

Visuals:

A 50x50 PNG of your spaceship

A 50x30 PNG of a UFO for you to shoot down

A 10x20 PNG of a bullet

An 800x600 JPG of a cosmic background

A sequence of PNGs for a spectacular explosion

Audio:

A WAV file to create the atmosphere of space

A WAV file for your laser gun

A WAV file for epic explosions

The images you choose will greatly impact the visual appeal of your game, so choose carefully. Try to find high-quality images and sounds from NASA to make your game feel more realistic. Create a new folder called The Space Fight and inside it, create subfolders named images and sounds. Organize your assets accordingly. Create a virtual environment and name it .venv. Open this project in Visual Studio Code or your preferred development environment. Install pygame library in your project. Create a new Python file called space_fighter.py to start writing your game's code. While we're using Ubuntu Linux, you can use any operating system. Python is cross-platform, so your game will work on any OS.

manuel@manuel-virtual-machine:~$ sudo apt update

manuel@manuel-virtual-machine:~$ sudo apt upgrade

manuel@manuel-virtual-machine:~$ clear

manuel@manuel-virtual-machine:~$ cd tutorials

manuel@manuel-virtual-machine:~/tutorials$ mkdir the_space_fighter

manuel@manuel-virtual-machine:~/tutorials$ cd the_space_fighter

manuel@manuel-virtual-machine:~/tutorials/the_space_fighter$ python3 -m venv .venv

manuel@manuel-virtual-machine:~/tutorials/the_space_fighter$ cd .venv

manuel@manuel-virtual-machine:~/tutorials/the_space_fighter/.venv$ cd bin

manuel@manuel-virtual-machine:~/tutorials/the_space_fighter/.venv/bin$ source activate

(.venv) manuel@manuel-virtual-machine:~/tutorials/the_space_fighter/.venv/

bin$ cd ..

(.venv) manuel@manuel-virtual-machine:~/tutorials/the_space_fighter/.venv$ cd ..

(.venv) manuel@manuel-virtual-machine:~/tutorials/the_space_fighter$ code .

Install the pygame library in your project with the following command in the integrated terminal ​​of Visual Studio Code.

(.venv) manuel@manuel-virtual-machine:~/tutorials/the_space_fighter$ pip install pygame

This assumes that you already have pip manager installed, along with Python installed on your operating system. Then, create a new Python file called space_fighter.py to start writing your game's code.

All preparation done before coding the game

All preparation done before coding the game 

First, we write the code that represents the initialization of the game 'The Space Fighter'. This code utilizes fundamental elements of the Pygame library to set up the screen, sounds, and load images. The 'random' library is an additional library that enables the generation of random values, which will be used later to create random positions, movements of UFOs, etc. We create a game window with specified dimensions of 800x600 and set the title of the form. We load the background music to loop infinitely. Meanwhile, we load the sounds of lasers and explosions for later use in the code.

import pygame

import random

# Initializing pygame

pygame.init()

# Setting up the screen

screen_width = 800

screen_height = 600

screen = pygame.display.set_mode((screen_width, screen_height))

pygame.display.set_caption("The Space Fighter")

# Load and start background music

pygame.mixer.music.load("sounds/space_sound.wav")

pygame.mixer.music.play(-1)

# Load laser sound

laser_sound = pygame.mixer.Sound("sounds/laser.wav")

# Load explosion sound

explosion_sound = pygame.mixer.Sound("sounds/explosion.wav")

We define two basic colors, white and black, which will be used for drawing text, the background, and other elements in the game. Next, we create an object that allows us to control the game's speed using FPS - frames per second, which helps ensure smooth gameplay. Then, we load the images we have prepared for use in the game. Pay attention to the list of images for the explosion animation, which is loaded using a list of file names that will later be used to create the explosion animation.

# Colors

white_color = (255, 255, 255)

black_color = (0, 0, 0)

# The clock for frame control

clock = pygame.time.Clock()

# Loading images

background_image = pygame.image.load("images/space.jpg")

spaceship_image = pygame.image.load("images/ship.png")

ufo_image = pygame.image.load("images/ufo.png")

bullet_image = pygame.image.load("images/bullet.png")

eksplosion_images = [pygame.image.load(f"images/exp_{i}.png") for i in range(1, 9)]

Once we've loaded all necessary assets, we define four classes for in-game elements: enemy ship, bullet, player's missile, and explosion. The UfoShip class sets a random initial position for the UFO outside the top of the screen. It also sets a random movement speed for the UFO, while its update method moves the UFO downwards and resets it off-screen. If the UFO passes the bottom edge of the screen, it resets to the top with a new random horizontal position.

The Bullet class sets the basic characteristics of a bullet. It sets the initial position of the bullet, so that it exits from the center of the player's missile, while its update method moves the bullet upwards. If the bullet exits the top edge of the screen, it is removed from the game.

The Spaceship class provides horizontal movement for the missile. It sets movement boundaries so that the missile cannot go off-screen. This class is also responsible for adjusting the missile's speed, stopping the missile by setting its speed to 0, and moving the missile left or right.

Finally, the Explosion class is responsible for updating the explosion animation. It checks if enough time has passed for the next frame, and when all frames have been displayed, it removes the explosion from the game.

# UfoShip class

class UfoShip(pygame.sprite.Sprite):

    def __init__(self):

        super().__init__()

        self.image = ufo_image

        self.rect = self.image.get_rect()

        self.rect.x = random.randint(0, screen_width- self.rect.width)

        self.rect.y = random.randint(-100, -40)

        self.speed = random.randint(1, 2)

     def update(self):

        self.rect.y += self.speed

        if self.rect.top > screen_height:

            self.rect.x = random.randint(0, screen_width - self.rect.width)

            self.rect.y = random.randint(-100, -40)

# Bullet class

class Bullet(pygame.sprite.Sprite):

    def __init__(self, x, y):

        super().__init__()

        self.image = bullet_image

        self.rect = self.image.get_rect()

        self.rect.bottom = y

        self.rect.centerx = x

        self.speed = -10

    def update(self):

        self.rect.y += self.speed

        if self.rect.bottom < 0:

            self.kill()

# Spaceship class

class Spaceship(pygame.sprite.Sprite):

    def __init__(self):

        super().__init__()

        self.image = spaceship_image

        self.rect = self.image.get_rect()

        self.rect.centerx = screen_width // 2

        self.rect.bottom = screen_height - 10

        self.speed = 0

    def update(self):

        self.rect.x += self.speed

        if self.rect.left < 0:

            self.rect.left = 0

        if self.rect.right > screen_width:

            self.rect.right = screen_width

    def left(self):

        self.speed = -3

    def right(self):

        self.speed = 3

    def stop(self):

        self.speed = 0

# Explosion class

class Explosion(pygame.sprite.Sprite):

    def __init__(self, centar):

        super().__init__()

        self.images = eksplosion_images

        self.current_frame = 0

        self.image = self.images[self.current_frame]

        self.rect = self.image.get_rect()

        self.rect.center = centar

        self.last_update = pygame.time.get_ticks()

        self.frame_rate = 50  # Speed animation

    def update(self):

        now = pygame.time.get_ticks()

        if now - self.last_update > self.frame_rate:

            self.last_update = now

            self.current_frame += 1

            if self.current_frame == len(self.images):

                self.kill() # Removes the sprite when the animation is finished

            else:

                self.image = self.images[self.current_frame]

                self.rect = self.image.get_rect(center=self.rect.center)


The following code sets up the main groups and variables for the game, as well as initializing objects such as the player's spaceship and UFOs. We create groups to organize different sprites in the game. A group containing all sprites in the game makes it easier to update and display them. A specific group for enemy UfoShips is used to detect collisions with bullets or the player's ship. A group containing all bullets fired by the player's spaceship allows easy control and collision checking with UFOs. It tracks the player's total score, which likely increases when the player destroys an enemy.

Variables track the number of player lives. It decreases if the player loses their spaceship due to a collision with an enemy. It counts how many enemy ships have been hit, which can be useful for progressing through levels or tracking player performance. These code sections lay the foundation for interactions between the player, enemies, and bullets, as well as for tracking scores and lives throughout the game

# Groups of sprites

all_sprites = pygame.sprite.Group()

ufo_ships = pygame.sprite.Group()

bullets = pygame.sprite.Group()

# Spaceship creation

spaceship = Spaceship()

all_sprites.add(spaceship)

# UfoShip creation

for i in range(5):

    ufoship = UfoShip()

    all_sprites.add(ufoship)

    ufo_ships.add(ufoship)

# Variables for points and lives

points = 0

lives = 5

hit_ufo_ships = 0


The following code section sets up two functions for displaying the initial and final screens of the game, allowing the user to start or end the game through on-screen animations and instructions. The initial screen displays a starting screen with an animated blinking title and instructions for starting the game. The final screen displays an ending screen after the game is over, offering the option to restart the game by pressing the R key or quit the game entirely by pressing the Q key.

# Start screen with title flashing animation

def start_screen():

    screen.fill(black_color)

    font_title = pygame.font.Font(None, 74)

    font_instruction = pygame.font.Font(None, 36)

    title_color = (0, 0, 255)

    change = True

    timer = pygame.time.get_ticks()

    wait = True

    while wait:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                pygame.quit()

                exit()

            elif event.type == pygame.KEYDOWN:

                if event.key == pygame.K_SPACE:

                    wait = False

         # Title blink animation

        if pygame.time.get_ticks() - timer > 500:

            if change:

                title_color = (0, 255, 0)

            else:

                title_color = (0, 0, 255)

            change = not change

            timer = pygame.time.get_ticks()

        screen.blit(background_image, (0, 0))

        title = font_title.render("THE SPACE FIGHTER", True, title_color)

        screen.blit(title, (screen_width // 2 - title.get_width() // 2, screen_height // 2 - 200))

        text_instruction = font_instruction.render("Press SPACE to start", True, white_color)

        screen.blit(text_instruction, (screen_width // 2 - text_instruction.get_width() // 2, screen_height // 2))

        pygame.display.flip()

# Function to display the end game screen

def end_screen():

    screen.fill(black_color)

    font_end = pygame.font.Font(None, 74)

    text_end = font_end.render("END THE GAME", True, white_color)

    screen.blit(text_end, (screen_width // 2 - text_end.get_width() // 2, screen_height // 2 - 100))

    font_points = pygame.font.Font(None, 36)

    text_congratulation = font_points.render(f"Congratulations, you have achieved {points} points", True, white_color)

    screen.blit(text_congratulation, (screen_width // 2 - text_congratulation.get_width () // 2, screen_height // 2))

    text_instruction = font_points.render("Press R to replay or Q to quit", True, white_color)

    screen.blit(text_instruction, (screen_width // 2 - text_instruction.get_width() // 2, screen_height // 2 + 50 ))

    pygame.display.flip()

    wait = True

    while wait:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                pygame.quit()

                exit()

            elif event.type == pygame.KEYDOWN:          

                if event.key == pygame.K_r:

                    wait = False

                    return True  # Restart the game

                elif event.key == pygame.K_q:

                    pygame.quit()

The final part of the code implements the main game loop, where player movements, bullet firing, object updates, collision detection, and on-screen score display are controlled. If the player loses all lives, the game stops, and an end screen is displayed with the option to restart. Note that the start screen is initiated before the main loop. The main loop must also ensure initial game values are set if the player decides to restart the game without closing it.

# We call the start screen function before the main loop

start_screen()

# The main loop of the game

start = True

while start:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            event = False

        elif event.type == pygame.KEYDOWN:

            if event.key == pygame.K_LEFT:

                spaceship.left()

            elif event.key == pygame.K_RIGHT:

                spaceship.right()

            elif event.key == pygame.K_SPACE:

                bullet = Bullet(spaceship.rect.centerx, spaceship.rect.top)

                all_sprites.add(bullet)

                bullets.add(bullet)

                laser_sound.play()  # Play the laser sound

        elif event.type == pygame.KEYUP:

            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:

                spaceship.stop() 

    # Update all sprites

    all_sprites.update()

    # If the ufo ship goes to the bottom

    for ufoship in ufo_ships:

        if ufoship.rect.bottom > screen_height:

            lives -= 1

            ufoship.rect.y = random.randint(-100, -40)

            ufoship.rect.x = random.randint(0, screen_width - ufoship.rect.width)

            if lives == 0:

                start = False # Ends the game if all lives are lost

    # Collision check

    collisions = pygame.sprite.groupcollide(bullets, ufo_ships, True, True)

    for collision in collisions:

        ufoship = UfoShip()

        all_sprites.add(ufoship)

        ufo_ships.add(ufoship)

        points += 10 # Add points for each ufo ship hit

        hit_ufo_ships += 1

        if hit_ufo_ships == 10:

            lives += 1

            hit_ufo_ships = 0  # Resets the hit count

        explosion = Explosion(collision.rect.center)

        all_sprites.add(explosion)

        explosion_sound.play()  # Play the explosion sound

    screen.blit(background_image, (0, 0))

    font = pygame.font.Font(None, 36)

    text_points = font.render("Score: " + str(points), True, white_color)

    text_lives = font.render("Lives: " + str(lives), True, white_color)

    screen.blit(text_lives, (10, 10))

    screen.blit(text_points, (screen_width - text_points.get_width() - 10, 10))

    all_sprites.draw(screen)

    pygame.display.flip()

    # Controlling the frame

    clock.tick(60)

    # Showing the end game screen

    if lives == 0:

        again = end_screen()

        if again:

            # Reset the game

            points = 0

            lives = 5

            hit_ufo_ships = 0

            all_sprites.empty()

            bullets.empty()

            ufo_ships.empty()

            spaceship = Spaceship()

            all_sprites.add(spaceship)

            for i in range(5):

                ufoship = UfoShip()

                all_sprites.add(ufoship)

                ufo_ships.add(ufoship)

            start = True

pygame.quit()


If you've typed all the code correctly, keep in mind that coding a large number of Python lines often leads to a higher probability of errors. Pay special attention to the specific indentation, and if English is not your native language, it can cause confusion. For example, in Serbian, center is translated as centar, while in English, both center and centre are correct. If you mistakenly use both translations, it will create chaos in your code. Additionally, using IntelliSense in Visual Studio Code, instead of helping, can completely misinterpret your typing or, for example, duplicate a command. For instance, it might subtract the screen width value twice instead of just once. Otherwise, if you've done everything successfully, the result should be as shown in the following image.
 
The Space Fighter Game

The Space Fighter Game

You can see how this whole process looks like in the following video, too.


How to Make Your Own FPS Game - The Space Fighter in Python



 

 

 

 

 



No comments:

Post a Comment