Create a Game with Pygame
Tags: python, pygame, gamedev
Published: 2024-04-19 08:04:31

"What if I make the game I want and play it myself?"

Anyone who likes games would have thought about it at least once. Personally, one of the main reasons I decided to pursue a career as a software engineer was because I wanted to make games with my own hands someday. During the boot camp, I learned a few languages, and the first thing I searched was "Is there a game made with this language?" That's how I naturally discovered pygame. During the Phase period, I was too busy following classes and overwhelmed with various assignments and quizzes to think properly. So, through the last process of Phase, which is blog writing, I decided to look into it. Well, I can't make a game with my own hands right away now (surely some people can!), but I intend to try making one with the help of AI.

1. install Pygame

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m22u3rovgq9wzsp4bs2s.gif)
                           

>                            Please!

I asked AI very gently, "I want to make a game with Pygame, could you help me, please?" Of course, the answer was positive, and he first told me how to install the library.

`pip install pygame`

Okay, now I can make a game with my own hands!

2. Initialize game window

Now starting coding. Using Pygame to create and initialize the game window.

```
import pygame
import sys
from random import randint

# Initialize Pygame
pygame.init()

# Set screen size
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))

# Set the game window title
pygame.display.set_caption("Space Shooter")
```

The command import pygame brings the library to the current Python script. Now I can use all modules, functions, and classes through the library!

sys is imported to use its functions when exiting the game.

The line from random import randint is a code that brings a tool that allows you to easily generate random numbers in a Python program.

pygame.init() is a function that initializes pygame. It is absolutely necessary to use various modules of pygame. In simple terms, it prepares various hardware and software components internally.

Next, the screen size was specified as a variable, and then the main screen was created through the function and assigned to the screen variable. The screen object provides the ability to draw or change pictures on the screen.

Next, the game window title was set. The title is "Space Shooter"... It's a cliché title... I'll have to change it if I get a chance.

3. Create game loop

In this step, set up a game loop that handles the main events of the game, updates the game state, and refreshes the screen.

```
# Game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet = Bullet(player.rect.centerx, player.rect.top)
                all_sprites.add(bullet)
                bullets.add(bullet)


      # Update game state
    all_sprites.update()

    # Check collision between bullets and enemies
    hits = pygame.sprite.groupcollide(bullets, enemies, True, True)

    # Generate new enemies when all are shot down
    if len(enemies) == 0:
        for _ in range(8):
            enemy = Enemy()
            all_sprites.add(enemy)
            enemies.add(enemy)

    # Refresh the screen
    screen.fill((0, 0, 0))  # Fill the screen with black
    all_sprites.draw(screen)
    pygame.display.flip()

# Exit Pygame
pygame.quit()
sys.exit()
```

- Game loop setup

This part can be said to be the core of the game. The game loop runs continuously from the start to the end of the game, and it is responsible for updating the game state and refreshing the screen.

First, declare a variable to control the execution of the game loop and set its initial value to True. Then the game continues to run as long as this variable is True.

Use the while loop to repeatedly execute the code inside the while loop while the variable running is true. This structure keeps the game running continuously.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k5bfn3dhykhcs8wb2tw6.gif)


>                           Dormammu!

- Event handling

Next, for event in pygame.event.get() helps to retrieve all events that occurred in the pygame event queue and handle them. Each event is stored in the event queue in order, and the above function allows you to extract events sequentially.
Wait a minute! What is an event queue??
The event queue stores all inputs from the user while the game or application is running as events. This queue acts as a buffer, helping the program to process them in order.

So it is said.

- Game exit event

The pygame.QUIT event occurs when the user clicks the close button on the game window.
If this condition is met, set the running variable to False to end the game loop.

- Key input event

pygame.KEYDOWN occurs when a user presses a key on the keyboard.

event.key == pygame.K_SPACE checks if the pressed key is the space bar. If the space bar is pressed, the following code block is executed.

pygame.KEYDOWN occurs when a user presses a key on the keyboard.

event.key == pygame.K_SPACE checks if the pressed key is the space bar. If the space bar is pressed, the following code block is executed.

- Create bullets and add to group

Bullet(player.rect.centerx, player.rect.top) creates an instance of the Bullet class. Here, player.rect.centerx and player.

rect.top provide the location parameters for the bullet to start at the player's position.

all_sprites.add(bullet) adds the created bullet instance to the all_sprites group. This group manages all sprites in the game and is typically used when drawing on the screen.

bullets.add(bullet) adds the bullet to the bullets group. This group only manages bullets and can be used for collision detection or other purposes.

- Update all sprites

all_sprites.update() calls the update method for all sprites in the all_sprites group. This updates the state of each sprite (player, enemy, bullet, etc.), performing tasks such as changing position, animation frame changes, etc.

- Check collision between bullets and enemies

The pygame.sprite.groupcollide() function checks for collisions between sprites in two groups. Here, sprites in the bullets group and enemies group are compared to find colliding objects.

The True, True parameters specify that the collided sprites should be removed from the group. That is, both the bullet and the enemy are removed from the group upon collision.

- Generate new enemies when all are shot down

len(enemies) == 0 checks if there are no enemies in the enemies group. If all enemies are shot down, this condition is true, and the code block is executed.

for _ in range(8): is a loop to create 8 new enemies.
Enemy() creates a new enemy sprite and adds it to the all_sprites and enemies groups. This provides an element of continuous challenge in the game.

- Refresh the screen

screen.fill((0, 0, 0)) fills the game screen with black. This is used to erase the previous frame before drawing a new one.
all_sprites.draw(screen) draws all sprites in the all_sprites group on the screen. This method renders the current position of each sprite on the screen.

pygame.display.flip() updates everything drawn on the screen to the actual display, showing it to the user. This uses double buffering to provide smooth animation without screen flickering.

- Exit Pygame

The pygame.quit() function releases all initializations related to pygame and properly shuts down all pygame libraries. In other words, it performs the necessary cleanup when the game is about to exit.

As mentioned earlier, the sys.exit() function is used to exit the program. This function exits the Python interpreter and can pass an exit status code if necessary.

Okay. Let's move on to the next step!

4. Create players and enemies

Let's graphically represent and implement the movement of players, enemies, and bullets!

- Create players

```
# Player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.image = pygame.image.load('player.png')
        self.rect = self.image.get_rect()
        self.rect.centerx = screen_width // 2
        self.rect.bottom = screen_height - 10

    def update(self):
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT]:
            self.rect.x -= 5
        if key[pygame.K_RIGHT]:
            this.rect.x += 5

# Create player object and add to game loop
player = Player()
all_sprites = pygame.sprite.Group(player)
```

The Player class inherits from pygame.sprite.Sprite and implements the player character's sprite in the game. This class manages the player's image, position settings, and player movements.

- Class definition and constructor

class Player(pygame.sprite.Sprite): means that the Player class inherits from pygame.sprite.Sprite. This allows the Player instance to use all features of the sprite.

def __init__(self): is the constructor of the Player class, which is automatically called when an object is created.

super(Player, self).__init__() calls the constructor of the inherited Sprite class to ensure the initialization of the sprite.

- Image and rectangle settings

self.image = pygame.image.load('player.png') loads the 'player.png' image file and stores it in the image attribute of the Player object. This image is used as the visual representation of the player.

self.rect = self.image.get_rect() retrieves the rectangular area of the loaded image and stores it in self.rect. This rectangle is used to define the player's position, size, and boundaries.

- Initial position settings

self.rect.centerx = screen_width // 2 sets the player's initial x-coordinate to the center based on the screen width. This positions the player horizontally in the middle of the screen.

self.rect.bottom = screen_height - 10 sets the player's bottom 10 pixels above the screen height. This positions the player near the bottom of the screen.

- Update method

def update(self): is the update method of the Player class. It is called periodically in the main game loop of Pygame to update the player's state.

key = pygame.key.get_pressed() returns the state of all currently pressed keys as a list. This list represents the press state of each key as a boolean value.

if key[pygame.K_LEFT]: checks if the left arrow key is pressed. If it is pressed, the player's x-coordinate is moved 5 pixels to the left (self.rect.x -= 5).

if key[pygame.K_RIGHT]: checks if the right arrow key is pressed. If it is pressed, the player's x-coordinate is moved 5 pixels to the right (self.rect.x += 5).

This way, the Player class reactively manages the player's movements based on key inputs, allowing for player control within the game.

- Create enemies

```
# Enemy class
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super(Enemy, self).__init__()
        self.image = pygame.image.load('enemy.png')
        self.rect = self.image.get_rect()
        self.rect.x = randint(20, screen_width - 20)
        self.rect.y = randint(-150, -100)

    def update(self):
        self.rect.y += 2
        if self.rect.top > screen_height:
            self.rect.x = randint(20, screen_width - 20)
            self.rect.y = randint(-150, -100)
```
The Enemy class inherits from pygame.sprite.Sprite and implements the enemy sprite in the game. This class manages the creation, position settings, and movements of enemies.

Class definition and constructor
class Enemy(pygame.sprite.Sprite) means that the Enemy class inherits from pygame.sprite.Sprite. This allows the Enemy instance to use all features of the sprite.

def __init__(self): is the constructor of the Enemy class, which is automatically called when an object is created.

super(Enemy, self).__init__() calls the constructor of the inherited Sprite class to ensure the initialization of the sprite.

- Image and rectangle settings

self.image = pygame.image.load('enemy.png') loads the 'enemy.png' image file and stores it in the image attribute of the Enemy object. This image is used as the visual representation of the enemy.

self.rect = self.image.get_rect() retrieves the rectangular area of the loaded image and stores it in self.rect. This rectangle is used to define the enemy's position, size, and boundaries.

- Initial position settings

self.rect.x = randint(20, screen_width - 20) randomly sets the enemy's initial x-coordinate within the screen width. Here, 20 and screen_width - 20 prevent the enemy from being created too close to the edges of the screen.

self.rect.y = randint(-150, -100) randomly sets the enemy's initial y-coordinate above the top of the screen. This creates the effect of the enemy descending from above the screen.

- Update method

self.rect.y += 2 moves the enemy's y-coordinate down by 2 pixels each frame. This makes the enemy continuously descend.

if self.rect.top > screen_height: checks if the enemy has completely disappeared below the screen. self.rect.top indicates the top position of the rectangle.

self.rect.x = randint(20, screen_width - 20) and self.rect.y = randint(-150, -100) reposition the enemy above the top of the screen when it has completely descended below, allowing the enemy to continue descending from the screen.

This class defines the behavior of enemy sprites, adding dynamic elements to the game and providing challenges for the player to face.

Okay. Let's now create bullets.

- Create bullets

```
# Bullet class
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super(Bullet, self).__init__()
        self.image = pygame.image.load('bullet.png')
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y

    def update(self):
        self.rect.y -= 10
```
The Bullet class inherits from pygame.sprite.Sprite and implements the bullet sprite in the game. This class manages the creation, position settings, and movements of bullets.

- Class definition and constructor

class Bullet(pygame.sprite.Sprite) means that the Bullet class inherits from pygame.sprite.Sprite. This allows the Bullet instance to use all features of the sprite.

def __init__(self, x, y): is the constructor of the Bullet class, which is automatically called when an object is created. x and y are parameters that receive the coordinates where the bullet will be created.

super(Bullet, self).__init__() calls the constructor of the inherited Sprite class to ensure the initialization of the sprite.

- Image and rectangle settings

self.image = pygame.image.load('bullet.png') loads the 'bullet.png' image file and stores it in the image attribute of the Bullet object. This image is used as the visual representation of the bullet.

self.rect = self.image.get_rect() retrieves the rectangular area of the loaded image and stores it in self.rect. This rectangle is used to define the bullet's position, size, and boundaries.

- Initial position settings

self.rect.centerx = x and self.rect.centery = y set the initial position of the bullet. x and y are the coordinates passed by the constructor, defining the exact starting position of the bullet on the screen.

- Update method

def update(self): is the update method of the Bullet class. It is called periodically in the main game loop of Pygame to update the bullet's state.

self.rect.y -= 10 moves the bullet up by 10 pixels each update. This implements the bullet's ascending motion after being fired.

These settings enable the Bullet class to effectively manage bullets within the game. The bullet continues to move upward after being fired, hitting enemies or exiting the screen until it no longer moves.

Alright, let's move on to the next step!

5. Create sprite groups

```
# Create sprite groups
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
bullets = pygame.sprite.Group()

player = Player()
all_sprites.add(player)

# Create and add enemy sprites
for _ in range(8):
    enemy = Enemy()
    all_sprites.add(enemy)
    enemies.add(enemy)
```

This code shows the process of creating sprite groups in the game and adding player and enemy sprites to these groups. Sprite groups in Pygame allow for efficient management of multiple sprites and handle updates and rendering in batches.

- Create sprite groups

pygame.sprite.Group() is a constructor provided by Pygame for creating sprite groups. Using this, three groups are created: all_sprites, enemies, and bullets.
all_sprites is a group that includes all sprites appearing in the game. This group allows for batch processing of updates and rendering for all sprites.

enemies includes all enemy sprites appearing in the game. This group allows for enemy-specific logic to be handled, such as collision checking.

bullets manages all bullets fired in the game. This group is used for bullet collision checking and rendering.

- Create and add player sprites

The Player() constructor is called to create a player object.
all_sprites.add(player) adds the created player sprite to the all_sprites group. This allows the player sprite to also be updated and rendered in batch in the game's main loop.

- Create and add enemy sprites
for _ in range(8) is a loop that repeats 8 times. This loop creates multiple enemy sprites and adds them to the game.
The Enemy() constructor is called to create a new enemy object.

all_sprites.add(enemy) adds the created enemy sprite to the all_sprites group.

enemies.add(enemy) adds the same enemy sprite to the enemies group. This is useful for processing logic specific to enemy sprites, such as collision with bullets.

This code plays a crucial role in the initial setup phase of the game. Using sprite groups allows for effective management of multiple sprites in the game's main loop and facilitates the application of specific logic to each group. This structure contributes to maintaining game performance and enhancing code readability.

6. Let's play!

ok, now it is time to test the game. first of all, I put some imagae from google for player, enemy, and bullet.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pb3x93rb2pchmmiacr34.png)

put file name in console....


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lhg2ma06n61jlv5845u.jpeg)

>                       Let's pray!




ready... and...
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/su07iw24op7r4yq0k0sm.gif)

YES!!!!!! It's a success for now! Although the sizes of the characters and the bullets are a mess... Anyway...

6. Conclusion

Although I relied heavily on AI to help with most of the game development, it allowed me to quickly and easily experience how programs are created and operate. It seemed that the AI only coded the essential parts quickly, probably because I vaguely requested "I want to make a game, help me please!" without specifying detailed requirements. I expect that if I input more specific requirements next time, a better game could be developed. Although many aspects were lacking, I found the experience interesting and plan to use this as a basis to create more enjoyable and polished games. That's all for now. Thanks.

*. Suggestion to modify:

- Start and exit buttons
- Adjustment of game element sizes
- Scoring system
- Difficulty adjustment over time
- Responses when encountering enemies (game over or loss of life)
- Addition and implementation of various feature-rich items
- Database construction and score saving system
- Addition of backgrounds and design of various elements
- Etc.