【Python】用Python写一个俄罗斯方块玩玩
- 一、引言
- 1.成品效果展示
- 二、思考准备
- 1.思考设计
- 2.代码设计
- 2.1 游戏页面
- 2.2 控件设计
- 2.2.1 方块生成
- 2.2.2 方块碰撞
- 2.2.3 方块消融
- 2.2.4 游戏主循环
- 2.2.5 游戏窗口
- 三、游戏完整版
一、引言
- 今日看到侄子在玩游戏,凑近一看,原来是俄罗斯方块。熟悉又怀念,童年的记忆瞬间涌上心头。小时候觉得这游戏老厉害了,现在想想,好像就是数组的组合和消融,便想着自己写一个试试。说干就干,冲!
1.成品效果展示
俄罗斯方块实现过程
二、思考准备
1.思考设计
- 俄罗斯方块作为风靡一时的游戏,由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏,主要是通过方块的组合、消融完成的;
- 所以得先设计有哪些方块、方块如何组合、完成一行时方块消融并下降一行、方块组合还得回旋转,单次90°等等,考虑中ing…
2.代码设计
2.1 游戏页面
- 游戏首先得有个操控页面,所以得设计一个
# 设计游戏窗口尺寸
SCREEN_WIDTH, SCREEN_HEIGHT = 300, 600
# 方块大小
GRID_SIZE = 30
# 各种图形颜色 可自定义调整
COLORS = [ # 颜色配置(含背景色+7种方块色)(0, 0, 0), # 0: 黑色背景(255, 0, 0), # 1: 红色-I型(0, 255, 0), # 2: 绿色-T型(0, 0, 255), # 3: 蓝色-J型(255, 165, 0), # 4: 橙色-L型(255, 255, 0), # 5: 黄色-O型(128, 0, 128), # 6: 紫色-S型(0, 255, 255) # 7: 青色-Z型
]
2.2 控件设计
2.2.1 方块生成
- 游戏开始最上方会有方块掉落,随机形状和颜色
def new_piece(self):"""生成新方块,随机形状和颜色"""shape = random.choice(SHAPES)return {'shape': shape, # 方块形状矩阵'x': (SCREEN_WIDTH//GRID_SIZE - len(shape[0])) // 2, # 初始水平居中'y': 0, # 初始垂直位置'color': random.randint(1, len(COLORS)-1) # 随机颜色(排除背景色)}
2.2.2 方块碰撞
- 方块会一直下降,碰撞,下降时还要符合可以多次旋转
def check_collision(self, dx=0, dy=0, rotate=False):"""碰撞检测函数:param dx: 水平移动偏移量:param dy: 垂直移动偏移量:param rotate: 是否正在旋转:return: 是否发生碰撞"""shape = self.current_piece['shape']# 旋转时生成临时形状if rotate: shape = [list(row[::-1]) for row in zip(*shape)] # 矩阵旋转算法:先转置再反转每行(顺时针90度)for y, row in enumerate(shape):for x, cell in enumerate(row):if cell: # 仅检测实体方块new_x = self.current_piece['x'] + x + dxnew_y = self.current_piece['y'] + y + dy# 边界检测(左右越界/触底/与其他方块重叠)if not (0 <= new_x < len(self.grid[0])) or new_y >= len(self.grid):return Trueif new_y >=0 and self.grid[new_y][new_x]:return Truereturn False
2.2.3 方块消融
- 方块接触时,合适的会消融、不合适的会叠加,消融了计算分数,消融的还要剔除
def merge_piece(self):"""将当前方块合并到游戏网格,并触发消行检测"""for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:# 将方块颜色写入网格对应位置self.grid[self.current_piece['y']+y][self.current_piece['x']+x] = self.current_piece['color']# 消行并更新分数lines = self.clear_lines()self.score += lines * 100def clear_lines(self):"""消除满行并返回消除行数"""lines = 0# 从底部向上扫描for i, row in enumerate(self.grid):if all(cell !=0 for cell in row): # 检测整行填满del self.grid[i] # 删除该行self.grid.insert(0, [0]*len(row)) # 在顶部插入新空行lines +=1return lines
2.2.4 游戏主循环
- 游戏主体逻辑写入,方块的处理时间、操作事项和刷新等
def run(self):"""游戏主循环"""fall_time = 0 # 下落时间累计器while True:self.screen.fill(COLORS[0]) # 用背景色清屏# 计时系统(控制自动下落速度)fall_time += self.clock.get_rawtime()self.clock.tick() # 保持帧率稳定# 自动下落逻辑(每800ms下落一格)if fall_time >= 800:if not self.check_collision(dy=1):self.current_piece['y'] +=1 # 正常下落else:self.merge_piece() # 触底合并self.current_piece = self.new_piece() # 生成新方块# 游戏结束检测(新方块无法放置)if self.check_collision():print("Game Over 你完蛋啦! Score:", self.score)returnfall_time =0 # 重置计时器# 事件处理(适配Mac键盘布局)for event in pygame.event.get():if event.type == QUIT:pygame.quit()returnif event.type == KEYDOWN:# 左右移动(带碰撞检测)if event.key == K_LEFT and not self.check_collision(dx=-1):self.current_piece['x'] -=1elif event.key == K_RIGHT and not self.check_collision(dx=1):self.current_piece['x'] +=1# 软下落(手动加速)elif event.key == K_DOWN:if not self.check_collision(dy=1):self.current_piece['y'] +=1# 旋转方块(带碰撞检测)elif event.key == K_UP and not self.check_collision(rotate=True):self.current_piece['shape'] = [list(row[::-1]) for row in zip(*self.current_piece['shape'])]# 硬下落(空格键一键到底) elif event.key == K_SPACE: while not self.check_collision(dy=1):self.current_piece['y'] +=1# 绘制逻辑,游戏网格for y, row in enumerate(self.grid):for x, color in enumerate(row):if color:# 绘制已落下方块(留1像素间隙)pygame.draw.rect(self.screen, COLORS[color], (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))# 绘制当前操作方块for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:pygame.draw.rect(self.screen, COLORS[self.current_piece['color']],((self.current_piece['x']+x)*GRID_SIZE, (self.current_piece['y']+y)*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))# 刷新画面pygame.display.flip()
2.2.5 游戏窗口
- 最后,游戏设计好了,必须有个游戏窗口来展示
def __init__(self):# 初始化游戏窗口self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))pygame.display.set_caption("Mac M1俄罗斯方块")self.clock = pygame.time.Clock() # 游戏时钟控制帧率# 游戏状态初始化self.grid = [[0]*(SCREEN_WIDTH//GRID_SIZE) for _ in range(SCREEN_HEIGHT//GRID_SIZE)] # 20x10游戏网格self.current_piece = self.new_piece() # 当前操作方块self.score = 0
三、游戏完整版
# 俄罗斯方块设计
# -*- coding: utf-8 -*-
"""
功能:俄罗斯方块,童年的回忆
作者:看海的四叔
最后更新:2025-04-16
"""import pygame
import random
from pygame.locals import *# 初始化配置
pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 300, 600
GRID_SIZE = 30
COLORS = [ (0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128), (0, 255, 255)
]SHAPES = [[[1,1,1,1]], [[1,1],[1,1]], [[0,1,0], [1,1,1]], [[1,1,1], [1,0,0]], [[1,1,1], [0,0,1]], [[0,1,1], [1,1,0]], [[1,1,0], [0,1,1]]
]class Tetris:"""游戏主控制类,处理游戏逻辑与渲染"""def __init__(self):self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))pygame.display.set_caption("Mac M1俄罗斯方块")self.clock = pygame.time.Clock() self.grid = [[0]*(SCREEN_WIDTH//GRID_SIZE) for _ in range(SCREEN_HEIGHT//GRID_SIZE)] self.current_piece = self.new_piece() self.score = 0 def new_piece(self):"""生成新方块(随机形状和颜色)"""shape = random.choice(SHAPES)return {'shape': shape, 'x': (SCREEN_WIDTH//GRID_SIZE - len(shape[0])) // 2, 'y': 0, 'color': random.randint(1, len(COLORS)-1) }def check_collision(self, dx=0, dy=0, rotate=False):"""碰撞检测函数:param dx: 水平移动偏移量:param dy: 垂直移动偏移量:param rotate: 是否正在旋转:return: 是否发生碰撞"""shape = self.current_piece['shape']if rotate: shape = [list(row[::-1]) for row in zip(*shape)] for y, row in enumerate(shape):for x, cell in enumerate(row):if cell: new_x = self.current_piece['x'] + x + dxnew_y = self.current_piece['y'] + y + dyif not (0 <= new_x < len(self.grid[0])) or new_y >= len(self.grid):return Trueif new_y >=0 and self.grid[new_y][new_x]:return Truereturn Falsedef merge_piece(self):"""将当前方块合并到游戏网格,并触发消行检测"""for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:self.grid[self.current_piece['y']+y][self.current_piece['x']+x] = self.current_piece['color']lines = self.clear_lines()self.score += lines * 100def clear_lines(self):"""消除满行并返回消除行数"""lines = 0for i, row in enumerate(self.grid):if all(cell !=0 for cell in row): del self.grid[i] self.grid.insert(0, [0]*len(row)) lines +=1return linesdef run(self):"""游戏主循环"""fall_time = 0 while True:self.screen.fill(COLORS[0]) fall_time += self.clock.get_rawtime()self.clock.tick() if fall_time >= 800:if not self.check_collision(dy=1):self.current_piece['y'] +=1 # 正常下落else:self.merge_piece() self.current_piece = self.new_piece() # 游戏结束检测(新方块无法放置)if self.check_collision():print("Game Over 你完蛋啦! Score:", self.score)returnfall_time =0 # 重置计时器for event in pygame.event.get():if event.type == QUIT:pygame.quit()returnif event.type == KEYDOWN:if event.key == K_LEFT and not self.check_collision(dx=-1):self.current_piece['x'] -=1elif event.key == K_RIGHT and not self.check_collision(dx=1):self.current_piece['x'] +=1elif event.key == K_DOWN:if not self.check_collision(dy=1):self.current_piece['y'] +=1elif event.key == K_UP and not self.check_collision(rotate=True):self.current_piece['shape'] = [list(row[::-1]) for row in zip(*self.current_piece['shape'])]elif event.key == K_SPACE: while not self.check_collision(dy=1):self.current_piece['y'] +=1for y, row in enumerate(self.grid):for x, color in enumerate(row):if color:pygame.draw.rect(self.screen, COLORS[color], (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:pygame.draw.rect(self.screen, COLORS[self.current_piece['color']],((self.current_piece['x']+x)*GRID_SIZE, (self.current_piece['y']+y)*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))pygame.display.flip() if __name__ == "__main__":game = Tetris()game.run()