python game development (Snake game, Gobang game, big ball eating small ball game)

Posted by phillfox on Thu, 06 Jan 2022 15:35:30 +0100

catalogue

1, Game development using Pygame

1. Make game window

2. Drawing in a window

3. Load image

4. Achieve animation effect

5. Collision detection

6. Event handling

2, Gobang

3, Snake game

1, Game development using Pygame

Pygame is an open source Python module, which is specially used for the development of multimedia applications (such as video games), including support for images, sounds, videos, events, collisions, etc. Pygame is built on SDL SDL is a cross platform multimedia development library, which is implemented in C language. It is widely used in the development of games, simulators, players and so on. Pygame makes game developers no longer bound by the underlying language and can pay more attention to the functions and logic of the game.

Now let's finish a simple game. The name of the game is "big ball eats small ball"

1. Make game window

import pygame


def main():
    # Initialize the modules in the imported pygame
    pygame.init()
    # Initializes the window for display and sets the window size
    screen = pygame.display.set_mode((800, 600))
    # Sets the title of the current window
    pygame.display.set_caption('Big balls eat small balls')
    running = True
    # Start an event loop to handle the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

2. Drawing in a window

You can draw on the window through the function of the draw module in pygame. The graphics you can draw include lines, rectangles, polygons, circles, ellipses, arcs, etc. It should be noted that the screen coordinate system sets the upper left corner of the screen as the coordinate origin (0, 0), the right is the positive direction of the x axis, and the down is the positive direction of the y axis. When indicating the position or setting the size, our default unit is pixel . The so-called pixel is a point on the screen. You can use the image browsing software to try to enlarge a picture several times, and you can see these points. pygame uses color light to represent color Three primary colors Representation, that is, the RGB values of colors are specified through a tuple or list, and each value is between 0 and 255, because each primary color is represented by an 8-bit value. The three colors are equivalent to a total of 24 bits, which is often referred to as "24 bit color representation".

import pygame


def main():
    # Initialize the modules in the imported pygame
    pygame.init()
    # Initializes the window for display and sets the window size
    screen = pygame.display.set_mode((800, 600))
    # Sets the title of the current window
    pygame.display.set_caption('Big balls eat small balls')
    # Set the background color of the window (the color is a tuple composed of red, green and blue primary colors)
    screen.fill((242, 242, 242))
    # Draw a circle (parameters are: screen, color, center position, radius, 0 indicates filling circle)
    pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0)
    # Refresh the current window (the rendering window renders the drawn image)
    pygame.display.flip()
    running = True
    # Start an event loop to handle the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

3. Load image

If you need to load the image directly into the window, you can use the function of the image module in pygame to load the image, and then render the image through the blit method of the previously obtained window object. The code is as follows.

import pygame


def main():
    # Initialize the modules in the imported pygame
    pygame.init()
    # Initializes the window for display and sets the window size
    screen = pygame.display.set_mode((800, 600))
    # Sets the title of the current window
    pygame.display.set_caption('Big balls eat small balls')
    # Set the background color of the window (the color is a tuple composed of red, green and blue primary colors)
    screen.fill((255, 255, 255))
    # Loads the image with the specified file name
    ball_image = pygame.image.load('./res/ball.png')
    # Rendering an image on a window
    screen.blit(ball_image, (50, 50))
    # Refresh the current window (the rendering window renders the drawn image)
    pygame.display.flip()
    running = True
    # Start an event loop to handle the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

4. Achieve animation effect

Speaking of animation This word is not unfamiliar to everyone. In fact, to achieve animation effect, the principle itself is very simple, that is, play discontinuous pictures continuously. As long as a certain number of frames per second is reached, you can make a relatively smooth animation effect. If you want to move the small ball in the above code, you can use variables to represent the position of the small ball, modify the position of the small ball in the loop, and then refresh the whole window.

import pygame


def main():
    # Initialize the modules in the imported pygame
    pygame.init()
    # Initializes the window for display and sets the window size
    screen = pygame.display.set_mode((800, 600))
    # Sets the title of the current window
    pygame.display.set_caption('Big balls eat small balls')
    # Define variables to represent the position of the ball on the screen
    x, y = 50, 50
    running = True
    # Start an event loop to handle the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        screen.fill((255, 255, 255))
        pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0)
        pygame.display.flip()
        # Change the position of the ball every 50 milliseconds and refresh the window
        pygame.time.delay(50)
        x, y = x + 5, y + 5


if __name__ == '__main__':
    main()

5. Collision detection

Usually, many objects appear in a game, and the "collision" between these objects is inevitable, such as shells hitting aircraft, boxes hitting the ground, etc. Collision detection is a crucial problem that must be handled in most games. The sprite module of pygame provides support for collision detection. Here we will not introduce the functions provided by the sprite module for the time being, because it is very simple to detect whether two balls collide, Just check whether the distance between the center of the ball is less than the sum of the radii of the two balls. In order to create more small balls, we can create small balls with random color, size and moving speed at the click of the mouse through the processing of mouse events. Of course, to do this, we can apply the object-oriented knowledge we learned before.

from enum import Enum, unique
from math import sqrt
from random import randint

import pygame


@unique
class Color(Enum):
    """colour"""

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        """Get random colors"""
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return (r, g, b)


class Ball(object):
    """ball"""

    def __init__(self, x, y, radius, sx, sy, color=Color.RED):
        """Initialization method"""
        self.x = x
        self.y = y
        self.radius = radius
        self.sx = sx
        self.sy = sy
        self.color = color
        self.alive = True

    def move(self, screen):
        """move"""
        self.x += self.sx
        self.y += self.sy
        if self.x - self.radius <= 0 or \
                self.x + self.radius >= screen.get_width():
            self.sx = -self.sx
        if self.y - self.radius <= 0 or \
                self.y + self.radius >= screen.get_height():
            self.sy = -self.sy

    def eat(self, other):
        """Eat other balls"""
        if self.alive and other.alive and self != other:
            dx, dy = self.x - other.x, self.y - other.y
            distance = sqrt(dx ** 2 + dy ** 2)
            if distance < self.radius + other.radius \
                    and self.radius > other.radius:
                other.alive = False
                self.radius = self.radius + int(other.radius * 0.146)

    def draw(self, screen):
        """Draw the ball on the window"""
        pygame.draw.circle(screen, self.color,
                           (self.x, self.y), self.radius, 0)

6. Event handling

The mouse event can be processed in the event loop. The event type can be determined through the type attribute of the event object, and then the position of the mouse click can be obtained through the pos attribute. If you want to handle keyboard events, it is also in this place, which is similar to handling mouse events.

def main():
    # Defines the container used to hold all balls
    balls = []
    # Initialize the modules in the imported pygame
    pygame.init()
    # Initializes the window for display and sets the window size
    screen = pygame.display.set_mode((800, 600))
    # Sets the title of the current window
    pygame.display.set_caption('Big balls eat small balls')
    running = True
    # Start an event loop to handle the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            # Code for handling mouse events
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # Get the mouse click position
                x, y = event.pos
                radius = randint(10, 100)
                sx, sy = randint(-10, 10), randint(-10, 10)
                color = Color.random_color()
                # Create a ball at the click of the mouse (random size, speed and color)
                ball = Ball(x, y, radius, sx, sy, color)
                # Add ball to list container
                balls.append(ball)
        screen.fill((255, 255, 255))
        # Take out the ball in the container, draw it if it is not eaten, and remove it if it is eaten
        for ball in balls:
            if ball.alive:
                ball.draw(screen)
            else:
                balls.remove(ball)
        pygame.display.flip()
        # Change the position of the ball every 50 milliseconds and refresh the window
        pygame.time.delay(50)
        for ball in balls:
            ball.move(screen)
            # Check if the ball eats any other balls
            for other in balls:
                ball.eat(other)


if __name__ == '__main__':
    main()

2, Gobang

The game program is as follows

import pygame

EMPTY = 0
BLACK = 1
WHITE = 2

black_color = [0, 0, 0]
white_color = [255, 255, 255]


class RenjuBoard(object):

    def __init__(self):
        self._board = [[]] * 15
        self.reset()

    def reset(self):
        for row in range(len(self._board)):
            self._board[row] = [EMPTY] * 15

    def move(self, row, col, is_black):
        if self._board[row][col] == EMPTY:
            self._board[row][col] = BLACK if is_black else WHITE
            return True
        return False

    def draw(self, screen):
        for index in range(1, 16):
            pygame.draw.line(screen, black_color,
                             [40, 40 * index], [600, 40 * index], 1)
            pygame.draw.line(screen, black_color,
                             [40 * index, 40], [40 * index, 600], 1)
        pygame.draw.rect(screen, black_color, [36, 36, 568, 568], 4)
        pygame.draw.circle(screen, black_color, [320, 320], 5, 0)
        pygame.draw.circle(screen, black_color, [160, 160], 5, 0)
        pygame.draw.circle(screen, black_color, [480, 480], 5, 0)
        pygame.draw.circle(screen, black_color, [480, 160], 5, 0)
        pygame.draw.circle(screen, black_color, [160, 480], 5, 0)
        for row in range(len(self._board)):
            for col in range(len(self._board[row])):
                if self._board[row][col] != EMPTY:
                    ccolor = black_color \
                        if self._board[row][col] == BLACK else white_color
                    pos = [40 * (col + 1), 40 * (row + 1)]
                    pygame.draw.circle(screen, ccolor, pos, 20, 0)


def main():
    board = RenjuBoard()
    is_black = True
    pygame.init()
    pygame.display.set_caption('Gobang')
    screen = pygame.display.set_mode([640, 640])
    screen.fill([255, 255, 0])
    board.draw(screen)
    pygame.display.flip()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYUP:
                pass
            elif event.type == pygame.MOUSEBUTTONDOWN\
                    and event.button == 1:
                x, y = event.pos
                row = round((y - 40) / 40)
                col = round((x - 40) / 40)
                if board.move(row, col, is_black):
                    is_black = not is_black
                    screen.fill([255, 255, 0])
                    board.draw(screen)
                    pygame.display.flip()
    pygame.quit()


if __name__ == '__main__':
    main()

3, Snake game

The code is as follows

from abc import ABCMeta, abstractmethod
from enum import Enum, unique
from random import randrange
from threading import Thread

import pygame


class Color(object):
    """colour"""

    GRAY = (242, 242, 242)
    BLACK = (0, 0, 0)
    GREEN = (0, 255, 0)
    PINK = (255, 20, 147)


@unique
class Direction(Enum):
    """direction"""

    UP = 0
    RIGHT = 1
    DOWN = 2
    LEFT = 3


class GameObject(object, metaclass=ABCMeta):
    """Objects in the game"""

    def __init__(self, x=0, y=0, color=Color.BLACK):
        """
        Initialization method
        :param x: Abscissa
        :param y: Ordinate
        :param color: colour
        """
        self._x = x
        self._y = y
        self._color = color

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

    @abstractmethod
    def draw(self, screen):
        """
        draw
        :param screen: screen
        """
        pass


class Wall(GameObject):
    """enclosing wall"""

    def __init__(self, x, y, width, height, color=Color.BLACK):
        """
        Initialization method
        :param x: Abscissa
        :param y: Ordinate
        :param width: width
        :param height: height
        :param color: colour
        """
        super().__init__(x, y, color)
        self._width = width
        self._height = height

    @property
    def width(self):
        return self._width

    @property
    def height(self):
        return self._height

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._width, self._height), 4)


class Food(GameObject):
    """food"""

    def __init__(self, x, y, size, color=Color.PINK):
        """
        Initialization method
        :param x: Abscissa
        :param y: Ordinate
        :param size: size
        :param color: colour
        """
        super().__init__(x, y, color)
        self._size = size
        self._hidden = False

    def draw(self, screen):
        if not self._hidden:
            pygame.draw.circle(screen, self._color,
                               (self._x + self._size // 2, self._y + self._size // 2),
                               self._size // 2, 0)
        self._hidden = not self._hidden


class SnakeNode(GameObject):
    """Node on Snake"""

    def __init__(self, x, y, size, color=Color.GREEN):
        """
        Initialization method
        :param x: Abscissa
        :param y: Ordinate
        :param size: size
        :param color: colour
        """
        super().__init__(x, y, color)
        self._size = size

    @property
    def size(self):
        return self._size

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._size, self._size), 0)
        pygame.draw.rect(screen, Color.BLACK,
                         (self._x, self._y, self._size, self._size), 1)


class Snake(GameObject):
    """snake"""

    def __init__(self, x, y, size=20, length=5):
        """
        Initialization method
        :param x: Abscissa
        :param y: Ordinate
        :param size: size
        :param length: Initial length
        """
        super().__init__()
        self._dir = Direction.LEFT
        self._nodes = []
        self._alive = True
        self._new_dir = None
        for index in range(length):
            node = SnakeNode(x + index * size, y, size)
            self._nodes.append(node)

    @property
    def dir(self):
        return self._dir

    @property
    def alive(self):
        return self._alive

    @property
    def head(self):
        return self._nodes[0]

    def change_dir(self, new_dir):
        """
        Change direction
        :param new_dir: New direction
        """
        if new_dir != self._dir and \
                (self._dir.value + new_dir.value) % 2 != 0:
            self._new_dir = new_dir

    def move(self):
        """move"""
        if self._new_dir:
            self._dir, self._new_dir = self._new_dir, None
        snake_dir = self._dir
        x, y, size = self.head.x, self.head.y, self.head.size
        if snake_dir == Direction.UP:
            y -= size
        elif snake_dir == Direction.RIGHT:
            x += size
        elif snake_dir == Direction.DOWN:
            y += size
        else:
            x -= size
        new_head = SnakeNode(x, y, size)
        self._nodes.insert(0, new_head)
        self._nodes.pop()

    def collide(self, wall):
        """
        Hit the wall
        :param wall: enclosing wall
        """
        head = self.head
        if head.x < wall.x or head.x + head.size > wall.x + wall.width \
                or head.y < wall.y or head.y + head.size > wall.y + wall.height:
            self._alive = False

    def eat_food(self, food):
        """
        Eat food
        :param food: food
        :return: Eat food and return True Otherwise return False
        """
        if self.head.x == food.x and self.head.y == food.y:
            tail = self._nodes[-1]
            self._nodes.append(tail)
            return True
        return False

    def eat_self(self):
        """Bite yourself"""
        for index in range(4, len(self._nodes)):
            node = self._nodes[index]
            if node.x == self.head.x and node.y == self.head.y:
                self._alive = False

    def draw(self, screen):
        for node in self._nodes:
            node.draw(screen)


def main():

    def refresh():
        """Refresh game window"""
        screen.fill(Color.GRAY)
        wall.draw(screen)
        food.draw(screen)
        snake.draw(screen)
        pygame.display.flip()

    def handle_key_event(key_event):
        """Handle key events"""
        key = key_event.key
        if key == pygame.K_F2:
            reset_game()
        elif key in (pygame.K_a, pygame.K_w, pygame.K_d, pygame.K_s):
            if snake.alive:
                if key == pygame.K_w:
                    new_dir = Direction.UP
                elif key == pygame.K_d:
                    new_dir = Direction.RIGHT
                elif key == pygame.K_s:
                    new_dir = Direction.DOWN
                else:
                    new_dir = Direction.LEFT
                snake.change_dir(new_dir)

    def create_food():
        """Create food"""
        unit_size = snake.head.size
        max_row = wall.height // unit_size
        max_col = wall.width // unit_size
        row = randrange(0, max_row)
        col = randrange(0, max_col)
        return Food(wall.x + unit_size * col, wall.y + unit_size * row, unit_size)

    def reset_game():
        """Reset game"""
        nonlocal food, snake
        food = create_food()
        snake = Snake(250, 290)

    def background_task():
        nonlocal running, food
        while running:
            if snake.alive:
                refresh()
            clock.tick(10)
            if snake.alive:
                snake.move()
                snake.collide(wall)
                if snake.eat_food(food):
                    food = create_food()
                snake.eat_self()

    """
    class BackgroundTask(Thread):
        def run(self):
            nonlocal running, food
            while running:
                if snake.alive:
                    refresh()
                clock.tick(10)
                if snake.alive:
                    snake.move()
                    snake.collide(wall)
                    if snake.eat_food(food):
                        food = create_food()
                    snake.eat_self()
    """

    wall = Wall(10, 10, 600, 600)
    snake = Snake(250, 290)
    food = create_food()
    pygame.init()
    screen = pygame.display.set_mode((620, 620))
    pygame.display.set_caption('Greedy snake')
    # Create a clock that controls the number of frames per second of the game
    clock = pygame.time.Clock()
    running = True
    # Start the background thread, which is responsible for refreshing the window and moving the snake
    # BackgroundTask().start()
    Thread(target=background_task).start()
    # Message loop for handling events
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                handle_key_event(event)
    pygame.quit()


if __name__ == '__main__':
    main()

https://github.com/jackfrued/Python-100-Dayshttps://github.com/jackfrued/Python-100-Days

Topics: Python Algorithm data structure pygame