Pygame第10课——俄罗斯方块

发布于:2025-03-29 ⋅ 阅读:(33) ⋅ 点赞:(0)

Pygame基础专栏目录

在这里插入图片描述

专栏导读

  • 🌸 欢迎来到Pygame基础专栏—大家可以在这里学习pygame基础知识

  • 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页

  • 👍 该系列文章专栏:请点击——>Pygame基础专栏

  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏

  • ❤️ 欢迎各位佬关注! ❤️

完整代码

import pygame
import random
import time

# 初始化pygame
pygame.init()

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
ORANGE = (255, 165, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
PURPLE = (128, 0, 128)
RED = (255, 0, 0)

# 游戏设置
BLOCK_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
SCREEN_WIDTH = BLOCK_SIZE * (GRID_WIDTH + 8)
SCREEN_HEIGHT = BLOCK_SIZE * GRID_HEIGHT
GAME_AREA_LEFT = BLOCK_SIZE * 4
GAME_AREA_TOP = 0

# 方块形状定义
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1], [1, 1]],  # O
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]],  # J
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]]  # Z
]

# 方块颜色
SHAPE_COLORS = [CYAN, YELLOW, PURPLE, ORANGE, BLUE, GREEN, RED]

# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("俄罗斯方块")

# 游戏时钟
clock = pygame.time.Clock()


class Tetromino:
    def __init__(self):
        self.shape_index = random.randint(0, len(SHAPES) - 1)
        self.shape = SHAPES[self.shape_index]
        self.color = SHAPE_COLORS[self.shape_index]
        self.x = GRID_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def rotate(self):
        # 旋转方块
        rows = len(self.shape)
        cols = len(self.shape[0])
        rotated = [[0 for _ in range(rows)] for _ in range(cols)]

        for r in range(rows):
            for c in range(cols):
                rotated[c][rows - 1 - r] = self.shape[r][c]

        return rotated

    def draw(self, surface):
        for y, row in enumerate(self.shape):
            for x, cell in enumerate(row):
                if cell:
                    rect = pygame.Rect(
                        GAME_AREA_LEFT + (self.x + x) * BLOCK_SIZE,
                        GAME_AREA_TOP + (self.y + y) * BLOCK_SIZE,
                        BLOCK_SIZE, BLOCK_SIZE
                    )
                    pygame.draw.rect(surface, self.color, rect)
                    pygame.draw.rect(surface, WHITE, rect, 1)


class TetrisGame:
    def __init__(self):
        # 绘制下一个方块预览
        self.font = pygame.font.SysFont('simhei', 13)
        self.grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
        self.current_piece = Tetromino()
        self.next_piece = Tetromino()
        self.game_over = False
        self.score = 0
        self.level = 1
        self.lines_cleared = 0
        self.fall_time = 0
        self.fall_speed = 0.5  # 初始下落速度(秒)
        self.last_fall_time = time.time()

    def draw_grid(self, surface):
        # 绘制游戏区域背景
        pygame.draw.rect(surface, BLACK, (GAME_AREA_LEFT, GAME_AREA_TOP,
                                          GRID_WIDTH * BLOCK_SIZE,
                                          GRID_HEIGHT * BLOCK_SIZE))

        # 绘制网格线
        for x in range(GRID_WIDTH + 1):
            pygame.draw.line(surface, WHITE,
                             (GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP),
                             (GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP + GRID_HEIGHT * BLOCK_SIZE))

        for y in range(GRID_HEIGHT + 1):
            pygame.draw.line(surface, WHITE,
                             (GAME_AREA_LEFT, GAME_AREA_TOP + y * BLOCK_SIZE),
                             (GAME_AREA_LEFT + GRID_WIDTH * BLOCK_SIZE, GAME_AREA_TOP + y * BLOCK_SIZE))

        # 绘制已放置的方块
        for y in range(GRID_HEIGHT):
            for x in range(GRID_WIDTH):
                if self.grid[y][x]:
                    color_idx = self.grid[y][x] - 1
                    rect = pygame.Rect(
                        GAME_AREA_LEFT + x * BLOCK_SIZE,
                        GAME_AREA_TOP + y * BLOCK_SIZE,
                        BLOCK_SIZE, BLOCK_SIZE
                    )
                    pygame.draw.rect(surface, SHAPE_COLORS[color_idx], rect)
                    pygame.draw.rect(surface, WHITE, rect, 1)

    def draw_info(self, surface):
        # 绘制信息区域背景
        info_area = pygame.Rect(0, 0, GAME_AREA_LEFT, SCREEN_HEIGHT)
        pygame.draw.rect(surface, (50, 50, 50), info_area)


        next_text = self.font.render("下一个:", True, WHITE)
        surface.blit(next_text, (20, 20))

        # 绘制下一个方块
        next_piece_surface = pygame.Surface((len(self.next_piece.shape[0]) * BLOCK_SIZE,
                                             len(self.next_piece.shape) * BLOCK_SIZE))
        next_piece_surface.fill((50, 50, 50))
        for y, row in enumerate(self.next_piece.shape):
            for x, cell in enumerate(row):
                if cell:
                    rect = pygame.Rect(
                        x * BLOCK_SIZE,
                        y * BLOCK_SIZE,
                        BLOCK_SIZE, BLOCK_SIZE
                    )
                    pygame.draw.rect(next_piece_surface, self.next_piece.color, rect)
                    pygame.draw.rect(next_piece_surface, WHITE, rect, 1)

        surface.blit(next_piece_surface, (20, 60))

        # 绘制分数
        score_text = self.font.render(f"分数: {self.score}", True, WHITE)
        surface.blit(score_text, (20, 180))

        # 绘制等级
        level_text = self.font.render(f"等级: {self.level}", True, WHITE)
        surface.blit(level_text, (20, 220))

        # 绘制已消除行数
        lines_text = self.font.render(f"消除行: {self.lines_cleared}", True, WHITE)
        surface.blit(lines_text, (20, 260))

        # 绘制操作说明
        controls_text = [
            "操作说明:",
            "←→: 左右移动",
            "↑: 旋转",
            "↓: 加速下落",
            "空格: 直接落下",
            "P: 暂停游戏",
            "R: 重新开始"
        ]

        for i, text in enumerate(controls_text):
            ctrl_text = self.font.render(text, True, WHITE)
            surface.blit(ctrl_text, (20, 320 + i * 30))

    def draw_game_over(self, surface):
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        overlay.set_alpha(150)
        overlay.fill(BLACK)
        surface.blit(overlay, (0, 0))

        font = pygame.font.SysFont(None, 50)
        game_over_text = self.font.render("游戏结束!", True, WHITE)
        restart_text = self.font.render("按R键重新开始", True, WHITE)

        surface.blit(game_over_text,
                     (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2,
                      SCREEN_HEIGHT // 2 - 50))
        surface.blit(restart_text,
                     (SCREEN_WIDTH // 2 - restart_text.get_width() // 2,
                      SCREEN_HEIGHT // 2 + 10))

    def is_collision(self, shape, x, y):
        for row_idx, row in enumerate(shape):
            for col_idx, cell in enumerate(row):
                if cell:
                    # 检查是否超出边界
                    if (x + col_idx < 0 or x + col_idx >= GRID_WIDTH or
                            y + row_idx >= GRID_HEIGHT):
                        return True
                    # 检查是否与已放置的方块重叠
                    if y + row_idx >= 0 and self.grid[y + row_idx][x + col_idx]:
                        return True
        return False

    def merge_piece(self):
        # 将当前方块合并到网格中
        for y, row in enumerate(self.current_piece.shape):
            for x, cell in enumerate(row):
                if cell and 0 <= self.current_piece.y + y < GRID_HEIGHT:
                    self.grid[self.current_piece.y + y][self.current_piece.x + x] = self.current_piece.shape_index + 1

    def clear_lines(self):
        lines_to_clear = []
        for y in range(GRID_HEIGHT):
            if all(self.grid[y]):
                lines_to_clear.append(y)

        for line in lines_to_clear:
            del self.grid[line]
            self.grid.insert(0, [0 for _ in range(GRID_WIDTH)])

        # 更新分数和等级
        if lines_to_clear:
            self.lines_cleared += len(lines_to_clear)
            self.score += [100, 300, 500, 800][min(len(lines_to_clear) - 1, 3)] * self.level
            self.level = min(10, 1 + self.lines_cleared // 10)
            self.fall_speed = max(0.05, 0.5 - (self.level - 1) * 0.05)

        return len(lines_to_clear)

    def move(self, dx, dy):
        if not self.is_collision(self.current_piece.shape,
                                 self.current_piece.x + dx,
                                 self.current_piece.y + dy):
            self.current_piece.x += dx
            self.current_piece.y += dy
            return True
        return False

    def rotate_piece(self):
        rotated = self.current_piece.rotate()
        if not self.is_collision(rotated, self.current_piece.x, self.current_piece.y):
            self.current_piece.shape = rotated
            return True

        # 尝试墙踢(wall kick)
        for dx in [-1, 1, -2, 2]:
            if not self.is_collision(rotated, self.current_piece.x + dx, self.current_piece.y):
                self.current_piece.x += dx
                self.current_piece.shape = rotated
                return True

        return False

    def drop_piece(self):
        while self.move(0, 1):
            pass

    def new_piece(self):
        self.current_piece = self.next_piece
        self.next_piece = Tetromino()

        # 检查游戏是否结束
        if self.is_collision(self.current_piece.shape,
                             self.current_piece.x,
                             self.current_piece.y):
            self.game_over = True

    def update(self):
        current_time = time.time()
        if current_time - self.last_fall_time > self.fall_speed:
            if not self.move(0, 1):
                self.merge_piece()
                self.clear_lines()
                self.new_piece()
            self.last_fall_time = current_time

    def reset(self):
        self.__init__()


def main():
    game = TetrisGame()
    paused = False

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    game.reset()
                    paused = False

                if game.game_over:
                    continue

                if event.key == pygame.K_p:
                    paused = not paused

                if paused:
                    continue

                if event.key == pygame.K_LEFT:
                    game.move(-1, 0)
                elif event.key == pygame.K_RIGHT:
                    game.move(1, 0)
                elif event.key == pygame.K_DOWN:
                    game.move(0, 1)
                elif event.key == pygame.K_UP:
                    game.rotate_piece()
                elif event.key == pygame.K_SPACE:
                    game.drop_piece()

        if not game.game_over and not paused:
            game.update()

        # 绘制游戏
        screen.fill((50, 50, 50))
        game.draw_grid(screen)
        game.draw_info(screen)

        if not game.game_over:
            game.current_piece.draw(screen)

        if game.game_over:
            game.draw_game_over(screen)

        pygame.display.flip()
        clock.tick(60)


if __name__ == "__main__":
    main()

总结

  • 希望对初学者有帮助

  • 致力于办公自动化的小小程序员一枚

  • 希望能得到大家的【一个免费关注】!感谢

  • 求个 🤞 关注 🤞

  • 此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏

  • 求个 ❤️ 喜欢 ❤️

  • 此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏

  • 求个 👍 收藏 👍

  • 此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏


网站公告

今日签到

点亮在社区的每一天
去签到