俄罗斯方块Tetris(tkinter)
本帖最后由 pyzyd 于 2025-7-21 12:35 编辑代码实现:
感觉写了一坨屎山{:10_282:}
源代码:
import tkinter as tk
from tkinter import messagebox
import random
import time
class Game(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('Tetris')
# 固定边框
self.resizable(False,False)
# 设置边长、像素
self.width = 12
self.height = 20
self.px = 30
# 设置不同形状的格子
self.shapes = {
'O': [(-1, -1), (0, -1), (-1, 0), (0, 0)],
'I': [(0, 1), (0, 0), (0, -1), (0, -2)],
'T': [(-1, -1), (0, -1), (0, 0), (1, -1)],
'Z': [(-1, 0), (0, 0), (0, 1), (1, 1)],
'S': [(-1, 1), (0, 1), (0, 0), (1, 0)],
'L': [(1, 1), (0, 1), (0, 0), (0, -1)],
'J': [(-1, 1), (0, 1), (0, 0), (0, -1)],
}
# 设置不同格子的颜色
self.colors = ['#FF0000', '#FF7F00', "#D4D432", "#00FF00", "#0000FF", '#4B008B', "#D3007F"]
self.shape_index = random.choice(list(self.shapes.keys()))
self.shape_color = random.choice(self.colors)
self.shape = self.shapes
# 表示固定的格子
self.fixed_shape = {}
self.fixed_set = set()
# 创建画布
self.canvas = tk.Canvas(
self, width=self.width*self.px,
height=self.height*self.px)
self.canvas.grid(row=0,column=0,columnspan=3,rowspan=4, padx=10, pady=10)
self.btnr = tk.Button(self, text='<', command=lambda:self.move((-1, 0)))
self.btnr.grid(row=4, column=0, padx=10, pady=10, sticky='nsew')
self.btn_rotate = tk.Button(self, text='-', command=self.rotate_shape)
self.btn_rotate.grid(row=4, column=1, padx=10, pady=10, sticky='nsew')
self.btnd = tk.Button(self, text='v', command=lambda:self.move((0, 1)))
self.btnd.grid(row=5, column=1, padx=10, pady=10, sticky='nsew')
self.btnl = tk.Button(self, text='>', command=lambda:self.move((1, 0)))
self.btnl.grid(row=4, column=2, padx=10, pady=10, sticky='nsew')
self.score = 0
self.v = tk.StringVar()
self.v.set(f'当前分数:{self.score}')
self.label1 = tk.Label(self, textvariable=self.v,font=('微软雅黑', 24))
self.label1.grid(row=0, column=3, padx=10, pady=10, sticky='nsew')
self.best_score = self.read_best_score()
self.v2 = tk.StringVar()
self.v2.set(f'最高分:{self.best_score}')
self.label2 = tk.Label(self, textvariable=self.v2,font=('微软雅黑', 24))
self.label2.grid(row=1, column=3, padx=10, pady=10, sticky='nsew')
self.text = tk.Text(self, height=10, width=20, font=('微软雅黑', 18))
self.text.grid(row=2, column=3, padx=10, pady=10, sticky='nsew')
self.text.insert(tk.END, '游戏规则:\n')
self.text.insert(tk.END, '1. 使用方向键控制方块的移动\n')
self.text.insert(tk.END, '2. 使用方向上键(Up键)旋转方块\n')
self.text.insert(tk.END, '3. 消除满行即可获得分数\n')
self.text.insert(tk.END, '4. 超出画布即游戏结束\n')
self.bind('<Left>', lambda event:self.move((-1, 0)))
self.bind('<Right>', lambda event:self.move((1, 0)))
self.bind('<Up>', lambda event:self.rotate_shape())
self.bind('<Down>', lambda event:self.move((0, 1)))
# 设置画布的点集(范围)
self.canvas_set = set()
for i in range(0, self.width):
for j in range(0, self.height):
self.canvas_set.add((i,j))
# 运行游戏
self.run_game()
# 读取最佳分数
def read_best_score(self):
"""读取最高分"""
try:
# 打开best_score.txt文件,以只读模式
with open('best_score.txt', 'r') as f:
# 读取文件内容,并转换为整数
return int(f.read())
except:
# 如果出现异常,返回0
return 0
# 定义一个方法,用于将最高分写入文件
def write_best_score(self, score):
"""写入最高分"""
# 打开文件best_score.txt,以写入模式
with open('best_score.txt', 'w') as f:
# 将最高分转换为字符串,并写入文件
f.write(str(score))
# 运行游戏
def run_game(self):
# 初始化游戏
self.initialize()
# 自动下落
self.auto_down()
# 进入主循环
self.mainloop()
def initialize(self):
"""初始化界面"""
# 绘制背景
self.draw_bg()
# 生成初始格子
self.generate_shape()
# 绘制初始
self.draw_shape(self.shape, self.x, self.y, self.shape_color)
def generate_shape(self):
"""生成新的格子"""
self.shape_index = random.choice(list(self.shapes.keys()))
self.shape_color = random.choice(self.colors)
self.shape = self.shapes
# 设置初始位置
self.x = random.randint(self.width//4, self.width//4*3)
self.y = -4
def auto_down(self):
"""自动下移"""
# 翻转方块
self.flip()
# 移动方块
self.move()
# 每隔500毫秒调用一次auto_down方法
self.after(500, self.auto_down)
def move(self, direction=(0, 1)):
"""移动格子"""
# 获取移动方向
dx, dy = direction
# 更新格子的坐标
self.x += dx
self.y += dy
# 如果格子超出边界,则回退到原来的位置
if self.is_out():
self.x -= dx
self.y -= dy
# 如果格子到达底部,则将格子固定在底部,并生成新的格子
if self.is_bottom():
# 如果格子颜色已经在固定格子的集合中,则更新该格子的坐标
if self.shape_color in self.fixed_shape:
self.fixed_shape.update([(self.x+dx,self.y+dy) for dx,dy in self.shape])
# 如果格子颜色不在固定格子的集合中,则将格子添加到固定格子的集合中
else:
self.fixed_shape = set([(self.x+dx,self.y+dy) for dx,dy in self.shape])
# 将格子的坐标添加到固定格子的集合中
for x, y in self.shape:
self.fixed_set.add((self.x + x, self.y + y))
# 生成新的格子
self.generate_shape()
def is_over(self):
"""判断游戏是否结束"""
for x, y in self.fixed_set:
if y < 0:
return True
return False
def rotate_shape(self):
"""旋转格子"""
if self.shape != self.shapes['O']:
temp = [(-dy, dx) for dx, dy in self.shape]
for dx, dy in temp:
if self.x + dx < 0:
self.x += 1
elif self.x + dx >= self.width:
self.x -= 1
# 检查旋转后的形状是否与已经固定的方块重叠
if not self.is_overlap():
self.shape = temp
def is_overlap(self):
"""检查新的格子是否与已经固定的格子重叠"""
for dx, dy in self.shape:
if (self.x + dx, self.y + dy) in self.fixed_set:
return True
return False
def is_out(self):
"""判断是否越界"""
for dx, dy in self.shape:
if self.x + dx < 0 or self.x + dx >= self.width or self.y + dy >= self.height:
return True
if self.is_overlap():
return True
return False
def is_bottom(self):
"""判断是否到底"""
for dx, dy in self.shape:
if self.y + dy == self.height - 1:
return True
if (self.x + dx, self.y + dy + 1) in self.fixed_set:
return True
return False
def flip(self):
"""更新画布"""
# 删除画布上所有元素
self.canvas.delete("all")
# 重绘元素
self.draw_bg()
y_s = self.is_delete()
if y_s:
index_y = y_s
l = len(y_s)
self.score += l * 10
self.v.set(f'当前分数:{self.score}')
if self.score > self.best_score:
self.best_score = self.score
self.v2.set(f'最高分:{self.best_score}')
self.write_best_score(self.best_score)
for shape_color, _ in self.fixed_shape.items():
temp_list = list(_)
for x, y in _:
if y < index_y:
temp_list.remove((x, y))
temp_list.append((x, y + l))
self.fixed_set.discard((x, y))
self.fixed_set.add((x, y + l))
self.fixed_shape = set(temp_list)
for shape_color, _ in self.fixed_shape.items():
for x, y in _:
self.draw_rect(x, y, shape_color)
self.draw_shape(self.shape, self.x, self.y, self.shape_color)
# 判断是否结束
if self.is_over():
# 游戏结束
self.game_over()
def draw_bg(self):
""""绘制像素"""
# 设置背景像素颜色
fill = '#CCCCCC'
outline = 'white'
px = self.px
for x, y in list(self.canvas_set):
self.draw_rect(x, y, fill, outline)
def draw_rect(self, x, y, color, outline="white"):
""""绘制像素"""
px = self.px
# 绘制像素
self.canvas.create_rectangle(
x * px, y * px,
(x + 1) * px, (y + 1) * px,
fill=color, outline=outline
)
def draw_shape(self, shape, x, y, color):
"""绘制不同的格子"""
for dx, dy in shape:
self.draw_rect(x + dx, y + dy, color)
def is_delete(self):
"""判断是否可以删除"""
y_s = []
for y in range(self.height):
if all((x, y) in self.fixed_set for x in range(self.width)):
for x in range(self.width):
self.fixed_set.remove((x, y))
for _ in self.fixed_shape.values():
_.discard((x, y))
y_s.append(y)
return y_s
def game_over(self):
"""游戏结束"""
self.canvas.create_text(
self.width * self.px / 2,
self.height * self.px / 2,
text="Game Over",
font=("Arial", 36),
fill="red"
)
self.canvas.update()
time.sleep(1)
yesno = messagebox.askyesno("提示", "是否重新开始游戏?")
self.destroy()
if yesno:
self.__init__()
if __name__ == '__main__':
game = Game()
打包好的文件:
https://wwrx.lanzoum.com/iAsuh31hj84d
密码:b5pi
我仔细分析了你的俄罗斯方块代码,发现几个关键问题需要修复。以下是主要问题和解决方案:
### 主要问题及修复方案
1. **形状定义缺失**:`self.shapes` 字典内容未定义
2. **颜色列表缺失**:`self.colors` 未初始化
3. **方块旋转逻辑错误**:旋转函数不完整
4. **固定方块存储结构混乱**:`fixed_shape` 和 `fixed_set` 使用混乱
5. **行消除逻辑缺陷**:`is_delete()` 函数未正确实现
6. **按键绑定错误**:方向键事件字符串缺失
### 修复后的核心代码
import tkinter as tk
from tkinter import messagebox
import random
import time
class Game(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('Tetris')
self.resizable(False, False)
self.width = 12
self.height = 20
self.px = 30
# 1. 补全方块形状定义
self.shapes = {
'O': [(0,0), (0,1), (1,0), (1,1)],
'I': [(0,-1), (0,0), (0,1), (0,2)],
'T': [(0,0), (-1,0), (1,0), (0,1)],
'Z': [(0,0), (-1,0), (0,1), (1,1)],
'S': [(0,0), (1,0), (0,1), (-1,1)],
'L': [(0,0), (0,1), (0,2), (1,2)],
'J': [(0,0), (0,1), (0,2), (-1,2)]
}
# 2. 添加颜色列表
self.colors = ['red', 'yellow', 'blue', 'green', 'purple', 'orange', 'cyan']
# 3. 初始化当前方块
self.shape_index = random.choice(list(self.shapes.keys()))
self.shape_color = random.choice(self.colors)
self.shape = self.shapes
# 4. 简化固定方块存储结构
self.fixed_blocks = {}# 格式: {(x,y): color}
# 画布初始化(保持不变)
self.canvas = tk.Canvas(
self, width=self.width*self.px,
height=self.height*self.px)
self.canvas.grid(row=0,column=0,columnspan=3,rowspan=4, padx=10, pady=10)
# 5. 修复按钮和按键绑定
self.btn_left = tk.Button(self, text='←', command=lambda:self.move((-1, 0)))
self.btn_left.grid(row=4, column=0, padx=10, pady=10)
self.btn_right = tk.Button(self, text='→', command=lambda:self.move((1, 0)))
self.btn_right.grid(row=4, column=2, padx=10, pady=10)
# 绑定键盘事件
self.bind('<Left>', lambda event:self.move((-1, 0)))
self.bind('<Right>', lambda event:self.move((1, 0)))
self.bind('<Up>', lambda event:self.rotate_shape())
self.bind('<Down>', lambda event:self.move((0, 1)))
# 其余初始化代码保持不变...
self.run_game()
# 6. 修复旋转函数
def rotate_shape(self):
if self.shape_index == 'O':# O型方块不旋转
return
# 计算旋转后的新形状 (90度顺时针)
new_shape = [(y, -x) for (x, y) in self.shape]
# 备份当前状态
old_shape = self.shape
old_x = self.x
# 尝试旋转
self.shape = new_shape
# 旋转后越界则恢复
if self.is_out():
self.shape = old_shape
# 7. 修复方块固定逻辑
def move(self, direction=(0, 1)):
dx, dy = direction
self.x += dx
self.y += dy
if self.is_out() or self.is_overlap():
self.x -= dx
self.y -= dy
if dy > 0:# 如果是向下移动被阻挡
self.fix_block()
self.check_lines()
self.generate_shape()
if self.is_overlap():# 新方块重叠即结束
self.game_over()
self.update_display()
def fix_block(self):
for dx, dy in self.shape:
x, y = self.x + dx, self.y + dy
if 0 <= y < self.height:
self.fixed_blocks[(x, y)] = self.shape_color
# 8. 重写行消除逻辑
def check_lines(self):
lines_cleared = 0
for y in range(self.height-1, -1, -1):
if all((x, y) in self.fixed_blocks for x in range(self.width)):
lines_cleared += 1
# 删除该行
for x in range(self.width):
del self.fixed_blocks[(x, y)]
# 上方方块下移
for yy in range(y-1, -1, -1):
for x in range(self.width):
if (x, yy) in self.fixed_blocks:
color = self.fixed_blocks.pop((x, yy))
self.fixed_blocks[(x, yy+1)] = color
if lines_cleared:
self.score += lines_cleared * 10
self.v.set(f'当前分数:{self.score}')
if self.score > self.best_score:
self.best_score = self.score
self.v2.set(f'最高分:{self.best_score}')
self.write_best_score(self.best_score)
# 9. 简化碰撞检测
def is_overlap(self):
for dx, dy in self.shape:
x, y = self.x + dx, self.y + dy
if (x, y) in self.fixed_blocks:
return True
return False
# 10. 重绘函数优化
def update_display(self):
self.canvas.delete("all")
self.draw_bg()
# 绘制固定方块
for (x, y), color in self.fixed_blocks.items():
self.draw_rect(x, y, color)
# 绘制当前方块
self.draw_shape(self.shape, self.x, self.y, self.shape_color)
# 其余函数保持基本结构,根据上述调整优化
### 主要改进说明:
1. **形状和颜色**:补全了7种标准俄罗斯方块形状定义和对应颜色
2. **数据结构**:使用单一字典 `fixed_blocks` 存储固定方块,键为坐标,值为颜色
3. **旋转逻辑**:实现90度顺时针旋转,O型方块不旋转
4. **行消除**:重写为逐行检测,消除后上方方块自动下落
5. **碰撞检测**:合并边界检测和方块重叠检测
6. **按键绑定**:修复方向键事件绑定
### 优化建议:
1. 增加游戏速度随分数提升的机制
2. 添加下一个方块预览功能
3. 实现墙壁反弹(Wall kick)旋转机制
4. 使用面向对象重构方块类
这个版本解决了原始代码的主要问题,结构更清晰。建议先测试基础功能(移动、旋转、消行),再逐步添加高级功能。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 挺好的{:9_232:} {:10_275:}古德 666 {:13_413:} {:9_231:} {:10_256:} 不错,支持~ {:10_297:} {:10_297:} {:10_256:} 狠狠赞!{:10_256:} 王昊扬 发表于 2025-7-21 13:22
挺好的
赞 921139987 发表于 2025-7-21 21:46
赞 历害啊 {:10_254:} gooddddddd 厉害 支持{:10_254:}
页:
[1]