Open In App

Create a 2D Game with Python and the Arcade Library

Last Updated : 12 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

We have a task to create a 2D game with Python and the Arcade library. In this article, we will demonstrate how to create a 2D game using Python and the Arcade library.

What is Arcade Library?

The Arcade library in Python is a modern framework designed for creating 2D games and graphical applications. It provides a user-friendly and intuitive interface for handling game development tasks, including input handling, rendering, and sound. Arcade simplifies the game development process with its clean and straightforward API, making it suitable for both beginners and experienced developers.

Create a 2D Game with Python and the Arcade Library

Here, we will explain how to create a 2D game with Python and the Arcade library:

Create a Virtual Environment

First, create the virtual environment using the below commands

python -m venv env 
.\env\Scripts\activate.ps1

Install Arcade Library

To create the 2D game with Python, the initial step involves installing the Arcade library. Use the following command for installation:

pip install arcade

Code Explanation

Here, the step-by-step explanation of 2D game with Python and the Arcade library in Python:

Step 1: Library Import and Constants

Below code defines constants for the dimensions of the game window, ball, paddles, and their respective speeds. It also imports the arcade library, which is used for creating the game.

Python3
import arcade

# Constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BALL_RADIUS = 10
PADDLE_WIDTH = 10
PADDLE_HEIGHT = 100
PADDLE_SPEED = 10
BALL_SPEED = 5

Step 2: PongGame Class Initialization

Here, in below code a class PongGame is defined, which inherits from arcade.Window. The __init__ method initializes various attributes representing the game state, such as player positions, ball position, scores, and game-related parameters.

Python3
class PongGame(arcade.Window):
    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Pong")
        self.player1_y = SCREEN_HEIGHT // 2
        self.player2_y = SCREEN_HEIGHT // 2
        self.ball_x = SCREEN_WIDTH // 2
        self.ball_y = SCREEN_HEIGHT // 2
        self.ball_dx = BALL_SPEED
        self.ball_dy = BALL_SPEED
        self.player1_score = 0
        self.player2_score = 0
        self.keys_pressed = set()
        self.winning_score = 5
        self.game_over_printed = False

Step 3: Drawing the Game

In below code on_draw method is called by the game engine to render the game. It uses arcade methods to draw text displaying scores, rectangles for paddles, and a filled circle for the ball.

Python3
def on_draw(self):
    arcade.start_render()
    arcade.draw_text(f"Player 1: {self.player1_score}",
                     10, SCREEN_HEIGHT - 30, arcade.color.WHITE, 20)
    arcade.draw_text(f"Player 2: {self.player2_score}", SCREEN_WIDTH -
                     140, SCREEN_HEIGHT - 30, arcade.color.WHITE, 20)
    arcade.draw_rectangle_filled(
        PADDLE_WIDTH / 2, self.player1_y, PADDLE_WIDTH, PADDLE_HEIGHT, arcade.color.WHITE)
    arcade.draw_rectangle_filled(SCREEN_WIDTH - PADDLE_WIDTH / 2,
                                 self.player2_y, PADDLE_WIDTH, PADDLE_HEIGHT, arcade.color.WHITE)
    arcade.draw_circle_filled(self.ball_x, self.ball_y,
                              BALL_RADIUS, arcade.color.WHITE)

Step 4: Game Logic Update

In below code update method is called by the game engine to update the game logic. It includes the movement of paddles, ball, collision detection with walls and paddles, scoring, and handling game-over conditions

Python3
def update(self, delta_time):
    if self.player1_score >= self.winning_score or self.player2_score >= self.winning_score:
        return  # Stop updating the game if it's over

    self.move_paddles()
    self.ball_x += self.ball_dx
    self.ball_y += self.ball_dy

    # Collision detection with top and bottom walls
    if self.ball_y <= BALL_RADIUS or self.ball_y >= SCREEN_HEIGHT - BALL_RADIUS:
        self.ball_dy *= -1

    # Collision detection with paddles
    if (self.ball_x - BALL_RADIUS <= PADDLE_WIDTH and self.player1_y - PADDLE_HEIGHT / 2 <= self.ball_y <= self.player1_y + PADDLE_HEIGHT / 2) \
            or (self.ball_x + BALL_RADIUS >= SCREEN_WIDTH - PADDLE_WIDTH and self.player2_y - PADDLE_HEIGHT / 2 <= self.ball_y <= self.player2_y + PADDLE_HEIGHT / 2):
        self.ball_dx *= -1

    # Scoring
    if self.ball_x <= 0:
        self.player2_score += 1
        self.reset_ball()
    elif self.ball_x >= SCREEN_WIDTH:
        self.player1_score += 1
        self.reset_ball()

    if self.player1_score >= self.winning_score or self.player2_score >= self.winning_score:
        self.game_over()

Step 5: Handling Ball Reset and Paddle Movement

In below code These methods, reset_ball and move_paddles, are responsible for resetting the ball’s position and adjusting the paddle positions based on the keys pressed by the player.

Python3
def reset_ball(self):
        self.ball_x = SCREEN_WIDTH // 2
        self.ball_y = SCREEN_HEIGHT // 2
        self.ball_dx = BALL_SPEED
        self.ball_dy = BALL_SPEED

    def move_paddles(self):
        if arcade.key.W in self.keys_pressed:
            self.player1_y += PADDLE_SPEED
        if arcade.key.S in self.keys_pressed:
            self.player1_y -= PADDLE_SPEED
        if arcade.key.UP in self.keys_pressed:
            self.player2_y += PADDLE_SPEED
        if arcade.key.DOWN in self.keys_pressed:
            self.player2_y -= PADDLE_SPEED

Step 6: Handling Game Over and Restart

In below code game_over method determines the winner, prints a game-over message, and sets a flag to prevent redundant printing. The restart_game method resets the game state for a new game, called when the player presses the ‘R’ key.

Python3
def game_over(self):
        # Determine the winner based on scores
        if self.player1_score > self.player2_score:
            winner = "Player 1"
        elif self.player2_score > self.player1_score:
            winner = "Player 2"
        else:
            winner = "It's a tie"

        # Print the winner
        if not self.game_over_printed:
            print(f"Game Over! {winner} wins!")
            print("Press 'R' to restart the game.")
            self.game_over_printed = True

    def restart_game(self):
        self.player1_y = SCREEN_HEIGHT // 2
        self.player2_y = SCREEN_HEIGHT // 2
        self.player1_score = 0
        self.player2_score = 0
        self.reset_ball()
        self.game_over_printed = False

    def on_key_press(self, key, modifiers):
        self.keys_pressed.add(key)
        if key == arcade.key.R:  # Restart game when 'R' key is pressed
            self.restart_game()

    def on_key_release(self, key, modifiers):
        self.keys_pressed.remove(key) if key in self.keys_pressed else None

Step 7 : Main Function and Execution

In below code The main function creates an instance of the PongGame class and starts the game using arcade.run() when the script is executed directly.

Python3
def main():
    game = PongGame()
    arcade.run()


if __name__ == "__main__":
    main()

Complete Code

Python3
import arcade

# Constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BALL_RADIUS = 10
PADDLE_WIDTH = 10
PADDLE_HEIGHT = 100
PADDLE_SPEED = 10
BALL_SPEED = 5

class PongGame(arcade.Window):
    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Pong")
        self.player1_y = SCREEN_HEIGHT // 2
        self.player2_y = SCREEN_HEIGHT // 2
        self.ball_x = SCREEN_WIDTH // 2
        self.ball_y = SCREEN_HEIGHT // 2
        self.ball_dx = BALL_SPEED
        self.ball_dy = BALL_SPEED
        self.player1_score = 0
        self.player2_score = 0
        self.keys_pressed = set()
        self.winning_score = 5
        self.game_over_printed = False

    def on_draw(self):
        arcade.start_render()
        arcade.draw_text(f"Player 1: {self.player1_score}", 10, SCREEN_HEIGHT - 30, arcade.color.WHITE, 20)
        arcade.draw_text(f"Player 2: {self.player2_score}", SCREEN_WIDTH - 140, SCREEN_HEIGHT - 30, arcade.color.WHITE, 20)
        arcade.draw_rectangle_filled(PADDLE_WIDTH / 2, self.player1_y, PADDLE_WIDTH, PADDLE_HEIGHT, arcade.color.WHITE)
        arcade.draw_rectangle_filled(SCREEN_WIDTH - PADDLE_WIDTH / 2, self.player2_y, PADDLE_WIDTH, PADDLE_HEIGHT, arcade.color.WHITE)
        arcade.draw_circle_filled(self.ball_x, self.ball_y, BALL_RADIUS, arcade.color.WHITE)

    def update(self, delta_time):
        if self.player1_score >= self.winning_score or self.player2_score >= self.winning_score:
            return  # Stop updating the game if it's over
        
        self.move_paddles()
        self.ball_x += self.ball_dx
        self.ball_y += self.ball_dy

        # Collision detection with top and bottom walls
        if self.ball_y <= BALL_RADIUS or self.ball_y >= SCREEN_HEIGHT - BALL_RADIUS:
            self.ball_dy *= -1

        # Collision detection with paddles
        if (self.ball_x - BALL_RADIUS <= PADDLE_WIDTH and self.player1_y - PADDLE_HEIGHT / 2 <= self.ball_y <= self.player1_y + PADDLE_HEIGHT / 2) \
                or (self.ball_x + BALL_RADIUS >= SCREEN_WIDTH - PADDLE_WIDTH and self.player2_y - PADDLE_HEIGHT / 2 <= self.ball_y <= self.player2_y + PADDLE_HEIGHT / 2):
            self.ball_dx *= -1

        # Scoring
        if self.ball_x <= 0:
            self.player2_score += 1
            self.reset_ball()
        elif self.ball_x >= SCREEN_WIDTH:
            self.player1_score += 1
            self.reset_ball()

        if self.player1_score >= self.winning_score or self.player2_score >= self.winning_score:
            self.game_over()

    def reset_ball(self):
        self.ball_x = SCREEN_WIDTH // 2
        self.ball_y = SCREEN_HEIGHT // 2
        self.ball_dx = BALL_SPEED
        self.ball_dy = BALL_SPEED

    def move_paddles(self):
        if arcade.key.W in self.keys_pressed:
            self.player1_y += PADDLE_SPEED
        if arcade.key.S in self.keys_pressed:
            self.player1_y -= PADDLE_SPEED
        if arcade.key.UP in self.keys_pressed:
            self.player2_y += PADDLE_SPEED
        if arcade.key.DOWN in self.keys_pressed:
            self.player2_y -= PADDLE_SPEED

    def game_over(self):
        # Determine the winner based on scores
        if self.player1_score > self.player2_score:
            winner = "Player 1"
        elif self.player2_score > self.player1_score:
            winner = "Player 2"
        else:
            winner = "It's a tie"

        # Print the winner
        if not self.game_over_printed:
            print(f"Game Over! {winner} wins!")
            print("Press 'R' to restart the game.")
            self.game_over_printed = True

    def restart_game(self):
        self.player1_y = SCREEN_HEIGHT // 2
        self.player2_y = SCREEN_HEIGHT // 2
        self.player1_score = 0
        self.player2_score = 0
        self.reset_ball()
        self.game_over_printed = False

    def on_key_press(self, key, modifiers):
        self.keys_pressed.add(key)
        if key == arcade.key.R:  # Restart game when 'R' key is pressed
            self.restart_game()

    def on_key_release(self, key, modifiers):
        self.keys_pressed.remove(key) if key in self.keys_pressed else None

def main():
    game = PongGame()
    arcade.run()

if __name__ == "__main__":
    main()

Output



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads