Python+tkinter实现俄罗斯方块游戏

游戏界面:

一个方块区域(游戏板),显示当前分数和最高分的标签,以及控制按钮(重启、暂停、显示排行榜)。玩家可以通过点击"Restart"按钮重启游戏,点击"Pause"按钮暂停游戏,点击"Show Leaderboard"按钮查看排行榜(最高的十个分数)。

游戏规则

玩家需要通过移动和旋转从屏幕顶部掉落的方块,使它们在屏幕底部堆积成完整的行。 游戏中有7种不同形状的方块:I形、O形、T形、S形、Z形、L形和J形,每种形状都有不同的颜色。

  • 使用键盘方向键来控制方块的移动和旋转:

    • 左箭头键:向左移动方块。
    • 右箭头键:向右移动方块。
    • 下箭头键:向下移动方块。
    • 上箭头键:旋转方块。
  • 当方块堆积到屏幕顶部,无法再放置新的方块时,游戏结束。

  • 每清除一行,玩家会获得100分。游戏结束时,当前分数会保存到文件中,如果当前分数超过最高分,则更新最高分。

游戏代码

python 复制代码
import tkinter as tk
import random
import os

# 游戏配置
CELL_SIZE = 35  # 每个方块的大小
COLUMNS = 10  # 游戏板的列数
ROWS = 20  # 游戏板的行数
INITIAL_DELAY = 500  # 初始延迟时间(毫秒)
DELAY = 500  # 每次更新的延迟时间(毫秒)
SCORE_FILE = "scores.txt"  # 存储分数的文件

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

# 方块颜色
COLORS = ["cyan", "yellow", "purple", "green", "red", "orange", "blue"]

class Tetris:
    def __init__(self, root):
        self.root = root
        self.score = 0  # 当前分数
        self.delay = INITIAL_DELAY
        self.high_score = self.get_high_score()  # 最高分

        # 创建一个单独的框架来放置按钮
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(side=tk.TOP, pady=10)

        # 创建并放置排行榜按钮
        self.leaderboard_button = tk.Button(self.button_frame, text="Show Leaderboard", command=self.show_leaderboard)
        self.leaderboard_button.pack(side=tk.LEFT, padx=0)  # 将排行榜按钮放在页面顶端
        self.leaderboard_button.pack_forget()  # 初始化时隐藏排行榜按钮

        # 创建并放置重启按钮
        self.restart_button = tk.Button(self.button_frame, text="Restart", command=self.restart_game)
        self.restart_button.pack(side=tk.LEFT, padx=0)  # 将重启按钮放在页面顶端

        # 创建并放置暂停按钮
        self.pause_button = tk.Button(self.button_frame, text="Pause", command=self.toggle_pause)
        self.pause_button.pack(side=tk.LEFT, padx=0)  # 将暂停按钮放在页面顶端

        self.score_label = tk.Label(root, text=f"Score: {self.score}  High Score: {self.high_score}")
        self.score_label.pack()
        self.canvas = tk.Canvas(root, width=COLUMNS*CELL_SIZE, height=ROWS*CELL_SIZE)
        self.canvas.pack()
        self.game_over = False  # 游戏结束标志
        self.paused = False  # 游戏暂停标志
        self.board = [[0] * COLUMNS for _ in range(ROWS)]  # 初始化游戏板
        self.current_shape = self.new_shape()  # 当前方块形状
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]  # 当前方块位置
        self.root.bind("<Key>", self.key_press)  # 绑定键盘事件
        self.update()  # 开始游戏更新循环

    def new_shape(self):
        """生成一个新的方块形状"""
        shape = random.choice(SHAPES)  # 随机选择一个方块形状
        color = COLORS[SHAPES.index(shape)]  # 获取对应的颜色
        return [[(cell and color) for cell in row] for row in shape]  # 返回带颜色的方块形状

    def draw_board(self):
        """绘制游戏板和当前方块"""
        self.canvas.delete("all")  # 清空画布
        for r in range(ROWS):
            for c in range(COLUMNS):
                if self.board[r][c]:
                    self.canvas.create_rectangle(c*CELL_SIZE, r*CELL_SIZE, (c+1)*CELL_SIZE, (r+1)*CELL_SIZE, fill=self.board[r][c])
                self.canvas.create_rectangle(c*CELL_SIZE, r*CELL_SIZE, (c+1)*CELL_SIZE, (r+1)*CELL_SIZE, outline="gray")
        for r, row in enumerate(self.current_shape):
            for c, cell in enumerate(row):
                if cell:
                    self.canvas.create_rectangle((self.current_position[1]+c)*CELL_SIZE, (self.current_position[0]+r)*CELL_SIZE, (self.current_position[1]+c+1)*CELL_SIZE, (self.current_position[0]+r+1)*CELL_SIZE, fill=cell)

    def move(self, delta_row, delta_col):
        """移动当前方块"""
        new_position = [self.current_position[0] + delta_row, self.current_position[1] + delta_col]
        if self.valid_position(new_position):
            self.current_position = new_position
            return True
        return False

    def rotate(self):
        """旋转当前方块"""
        new_shape = list(zip(*self.current_shape[::-1]))  # 顺时针旋转方块
        if self.valid_position(self.current_position, new_shape):
            self.current_shape = new_shape

    def valid_position(self, position, shape=None):
        """检查方块在给定位置是否有效"""
        if shape is None:
            shape = self.current_shape
        for r, row in enumerate(shape):
            for c, cell in enumerate(row):
                if cell:
                    if r + position[0] >= ROWS or c + position[1] < 0 or c + position[1] >= COLUMNS or self.board[r + position[0]][c + position[1]]:
                        return False
        return True

    def freeze(self):
        """冻结当前方块并生成新方块"""
        for r, row in enumerate(self.current_shape):
            for c, cell in enumerate(row):
                if cell:
                    self.board[self.current_position[0] + r][self.current_position[1] + c] = cell
        self.clear_lines()
        self.current_shape = self.new_shape()
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]
        if not self.valid_position(self.current_position):
            self.game_over = True

    def clear_lines(self):
        """清除已填满的行并更新分数"""
        new_board = [row for row in self.board if any(cell == 0 for cell in row)]
        lines_cleared = ROWS - len(new_board)
        self.board = [[0] * COLUMNS for _ in range(lines_cleared)] + new_board
        self.score += lines_cleared * 100  # 每消除一行增加100分
        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")

    def update(self):
        """更新游戏状态"""
        if not self.game_over and not self.paused:
            if not self.move(1, 0):
                self.freeze()
            self.draw_board()
            self.root.after(self.delay, self.update)
        elif self.game_over:
            self.save_score()
            self.canvas.create_text(COLUMNS*CELL_SIZE//2, ROWS*CELL_SIZE//2, text="GAME OVER", fill="red", font=("Helvetica", 24))
            self.pause_button.pack_forget()  # 隐藏暂停按钮
            self.leaderboard_button.pack(side=tk.LEFT)  # 显示排行榜按钮

    def key_press(self, event):
        """处理键盘按键事件"""
        if event.keysym == "Left":
            self.move(0, -1)
        elif event.keysym == "Right":
            self.move(0, 1)
        elif event.keysym == "Down":
            self.move(1, 0)
        elif event.keysym == "Up":
            self.rotate()
        self.draw_board()

    def toggle_pause(self):
        """切换游戏暂停状态"""
        self.paused = not self.paused
        if not self.paused:
            self.update()

    def save_score(self):
        """保存当前分数到文件"""
        with open(SCORE_FILE, "a") as file:
            file.write(f"{self.score}\n")
        if self.score > self.high_score:
            self.high_score = self.score

    def get_high_score(self):
        """获取最高分"""
        if os.path.exists(SCORE_FILE):
            with open(SCORE_FILE, "r") as file:
                scores = [int(line.strip()) for line in file.readlines()]
            if scores:
                return max(scores)
        return 0

    def get_leaderboard_text(self):
        """获取排行榜文本"""
        if os.path.exists(SCORE_FILE):
            with open(SCORE_FILE, "r") as file:
                scores = [int(line.strip()) for line in file.readlines()]
            scores.sort(reverse=True)
            top_scores = scores[:10]
            leaderboard = "\n".join([f"{i+1}. {score}" for i, score in enumerate(top_scores)])
        else:
            leaderboard = "No scores available."
        return leaderboard

    def show_leaderboard(self):
        """显示排行榜窗口"""
        leaderboard_window = tk.Toplevel(self.root)
        leaderboard_window.title("Leaderboard")
        leaderboard_text = self.get_leaderboard_text()
        tk.Label(leaderboard_window, text=leaderboard_text).pack()

    def restart_game(self):
        """重启游戏"""
        self.score = 0
        self.game_over = False
        self.paused = False
        self.board = [[0] * COLUMNS for _ in range(ROWS)]
        self.current_shape = self.new_shape()
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]
        self.delay = INITIAL_DELAY  # 重置延迟时间

        # 隐藏排行榜按钮
        self.leaderboard_button.pack_forget()
        
        # 重新布局按钮,确保它们在同一行
        self.button_frame.pack_forget()
        self.button_frame.pack(side=tk.TOP, pady=10)
        self.restart_button.pack(side=tk.LEFT, padx=0)
        self.pause_button.pack(side=tk.LEFT, padx=0)
        
        # 确保按钮框架在网格之前被布局
        self.canvas.pack_forget()
        
        # 更新分数标签并放置在按钮和网格之间
        self.score_label.pack_forget()
        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")
        self.score_label.pack()
        
        self.canvas.pack()

        self.update()

        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")
        self.update()

root = tk.Tk()
root.title("Tetris")
root.geometry(f"{COLUMNS*CELL_SIZE+20}x{ROWS*CELL_SIZE+100}+{(root.winfo_screenwidth()//2)-(COLUMNS*CELL_SIZE//2)}+{(root.winfo_screenheight()//2)-(ROWS*CELL_SIZE//2)}")
game = Tetris(root)
root.mainloop()

页面效果


相关推荐
阿泽不想掉光头发3 分钟前
C#实现调用DLL 套壳读卡程序(桌面程序开发)
java·开发语言·后端·websocket·http·c#
m0_748240029 分钟前
Python毕业设计选题:基于协同过滤的动漫推荐系统设计与实现_django+hive+spider
python·django·课程设计
bglmmz9 分钟前
JPA查询部分字段的最佳实践
java·开发语言
Marzlam17 分钟前
C# IDisposable接口 与析构函数
开发语言·c#
高锰酸钾_20 分钟前
Python数据可视化小项目
python·信息可视化·数据分析
GesLuck22 分钟前
C#控件开发3—文本显示、文本设值
前端·c#
轻口味28 分钟前
【每日学点鸿蒙知识】屏幕高度、证书签名、深色模式对上架影响、Taskpool上下文、List触底加载更多
前端·华为·harmonyos
峰子201235 分钟前
Go语言实现守护进程的挑战
开发语言·后端·面试·架构·golang·go
m0_7482371538 分钟前
PHP实现登录和注册(附源码)
开发语言·php
tealcwu1 小时前
【游戏设计原理】32 - 消费者剩余
java·开发语言·游戏