python 基于pygame实现俄罗斯方块


Posted in Python onMarch 02, 2021

一、简单说明

80、90后的小伙伴都玩过“俄罗斯方块”,那种“叱咤风云”场景 偶尔闪现在脑海 真的是太爽了;如果没有来得及玩过的同学,这次可以真正的自己做一个了

本实例用的是Python3(当然了Python3.5 3.6 3.7....都行 )+ pygame实现的

运行之前需要安装pygame模块,安装命令如下

pip install pygame -i https://mirrors.aliyun.com/pypi/simple/

二、运行效果

python 基于pygame实现俄罗斯方块

python 基于pygame实现俄罗斯方块

三、完整代码

文件main.py代码如下:

"""
作者:it项目实例网
更多项目实例,请访问:www.itprojects.cn
"""

import random
import sys
import time

import pygame

from blocks import block_s, block_i, block_j, block_l, block_o, block_t, block_z

SCREEN_WIDTH, SCREEN_HEIGHT = 450, 750
BG_COLOR = (40, 40, 60) # 背景色
BLOCK_COL_NUM = 10 # 每行的方格数
SIZE = 30 # 每个小方格大小
BLOCK_ROW_NUM = 25 # 每列的方个数
BORDER_WIDTH = 4 # 游戏区边框宽度
RED = (200, 30, 30) # 红色,GAME OVER 的字体颜色


def judge_game_over(stop_all_block_list):
 """
 判断游戏是否结束
 """
 if "O" in stop_all_block_list[0]:
  return True


def change_speed(score):
 speed_level = [("1", 0.5, 0, 20), ("2", 0.4, 21, 50), ("3", 0.3, 51, 100), ("4", 0.2, 101, 200), ("5", 0.1, 201, None)]
 for speed_info, speed, score_start, score_stop in speed_level:
  if score_stop and score_start <= score <= score_stop:
   return speed_info, speed
  elif score_stop is None and score >= score_start:
   return speed_info, speed


def judge_lines(stop_all_block_list):
 """
 判断是否有同一行的方格,如果有则消除
 """
 # 记录刚刚消除的行数
 move_row_list = list()
 # 消除满格的行
 for row, line in enumerate(stop_all_block_list):
  if "." not in line:
   # 如果这一行没有. 那么就意味着全部是O,则消除这一行
   stop_all_block_list[row] = ['.' for _ in range(len(line))]
   move_row_list.append(row)

 # 如果没有满格的行,则结束此函数
 if not move_row_list:
  return 0

 # 移动剩余的行到下一行
 for row in move_row_list:
  stop_all_block_list.pop(row)
  stop_all_block_list.insert(0, ['.' for _ in range(len(line))])

 return len(move_row_list) * 10


def add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col):
 """
 将当前已经停止移动的block添加到列表中
 """
 for row, line in enumerate(current_block):
  for col, block in enumerate(line):
   if block != '.':
    stop_all_block_list[current_block_start_row + row][current_block_start_col + col] = "O"


def change_current_block_style(current_block):
 """
 改变图形的样式
 """
 # 计算出,当前图形样式属于哪个图形
 current_block_style_list = None
 for block_style_list in [block_s, block_i, block_j, block_l, block_o, block_t, block_z]:
  if current_block in block_style_list:
   current_block_style_list = block_style_list

 # 得到当前正在用的图形的索引(下标)
 index = current_block_style_list.index(current_block)
 # 它的下一个图形的索引
 index += 1
 # 防止越界
 index = index % len(current_block_style_list)
 # 返回下一个图形
 return current_block_style_list[index]


def judge_move_right(current_block, current_block_start_col):
 """
 判断是否可以向右移动
 """
 # 先判断列的方式是从右到左
 for col in range(len(current_block[0]) - 1, -1, -1):
  # 得到1列的所有元素
  col_list = [line[col] for line in current_block]
  # 判断是否碰到右边界
  if 'O' in col_list and current_block_start_col + col >= BLOCK_COL_NUM:
   return False
 return True


def judge_move_left(current_block, current_block_start_col):
 """
 判断是否可以向左移动
 """
 # 先判断列的方式是从左到右
 for col in range(len(current_block[0])):
  # 得到1列的所有元素
  col_list = [line[col] for line in current_block]
  # 判断是否碰到右边界
  if 'O' in col_list and current_block_start_col + col < 0:
   return False
 return True


def judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
 """
 判断是否碰撞到其它图形或者底边界
 """
 # 得到其它图形所有的坐标
 stop_all_block_position = list()
 for row, line in enumerate(stop_all_block_list):
  for col, block in enumerate(line):
   if block != ".":
    stop_all_block_position.append((row, col))
 # print(stop_all_block_position)

 # 判断碰撞
 for row, line in enumerate(current_block):
  if 'O' in line and current_block_start_row + row >= BLOCK_ROW_NUM:
   # 如果当前行有0,且从起始行开始算+当前显示的行,超过了总行数,那么就认为碰到了底部
   return False
  for col, block in enumerate(line):
   if block != "." and (current_block_start_row + row, current_block_start_col + col) in stop_all_block_position:
    return False

 return True


def get_block():
 """
 创建一个图形
 """
 block_style_list = random.choice([block_s, block_i, block_j, block_l, block_o, block_t, block_z])
 return random.choice(block_style_list)


def main():
 pygame.init()
 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
 pygame.display.set_caption('俄罗斯方块')

 current_block = get_block() # 当前图形
 current_block_start_row = -2 # 当前图片从哪一行开始显示图形
 current_block_start_col = 4 # 当前图形从哪一列开始显示
 next_block = get_block() # 下一个图形
 last_time = time.time()
 speed = 0.5 # 降落的速度
 speed_info = '1' # 显示的速度等级

 # 定义一个列表,用来存储所有的已经停止移动的形状
 stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]

 # 字体
 font = pygame.font.Font('yh.ttf', 24) # 黑体24
 game_over_font = pygame.font.Font("yh.ttf", 72)
 game_over_font_width, game_over_font_height = game_over_font.size('GAME OVER')
 game_again_font_width, game_again_font_height = font.size('鼠标点击任意位置,再来一局')

 # 得分
 score = 0

 # 标记游戏是否结束
 game_over = False

 # 创建计时器(防止while循环过快,占用太多CPU的问题)
 clock = pygame.time.Clock()
 while True:
  for event in pygame.event.get():
   if event.type == pygame.QUIT:
    sys.exit()
   elif event.type == pygame.KEYDOWN:
    if event.key == pygame.K_LEFT:
     if judge_move_left(current_block, current_block_start_col - 1):
      current_block_start_col -= 1
    elif event.key == pygame.K_RIGHT:
     if judge_move_right(current_block, current_block_start_col + 1):
      current_block_start_col += 1
    elif event.key == pygame.K_UP:
     current_block_next_style = change_current_block_style(current_block)
     if judge_move_left(current_block_next_style, current_block_start_col) and \
       judge_move_right(current_block_next_style, current_block_start_col) and \
       judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
      # 判断新的样式没有越界
      current_block = current_block_next_style
    elif event.key == pygame.K_DOWN:
     # 判断是否可以向下移动,如果碰到底部或者其它的图形就不能移动了
     if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
      current_block_start_row += 1
   elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
    if game_over:
     # 重置游戏用到的变量
     current_block = get_block() # 当前图形
     current_block_start_row = -2 # 当前图片从哪一行开始显示图形
     current_block_start_col = 4 # 当前图形从哪一列开始显示
     next_block = get_block() # 下一个图形
     stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
     score = 0
     game_over = False

  # 判断是否修改当前图形显示的起始行
  if not game_over and time.time() - last_time > speed:
   last_time = time.time()
   # 判断是否可以向下移动,如果碰到底部或者其它的图形就不能移动了
   if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
    current_block_start_row += 1
   else:
    # 将这个图形存储到统一的列表中,这样便于判断是否成为一行
    add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col)
    # 判断是否有同一行的,如果有就消除,且加上分数
    score += judge_lines(stop_all_block_list)
    # 判断游戏是否结束(如果第一行中间有O那么就表示游戏结束)
    game_over = judge_game_over(stop_all_block_list)
    # 调整速度
    speed_info, speed = change_speed(score)
    # 创建新的图形
    current_block = next_block
    next_block = get_block()
    # 重置数据
    current_block_start_col = 4
    current_block_start_row = -2

  # 画背景(填充背景色)
  screen.fill(BG_COLOR)

  # 画游戏区域分隔线
  pygame.draw.line(screen, (100, 40, 200), (SIZE * BLOCK_COL_NUM, 0), (SIZE * BLOCK_COL_NUM, SCREEN_HEIGHT), BORDER_WIDTH)

  # 显示当前图形
  for row, line in enumerate(current_block):
   for col, block in enumerate(line):
    if block != '.':
     pygame.draw.rect(screen, (20, 128, 200), ((current_block_start_col + col) * SIZE, (current_block_start_row + row) * SIZE, SIZE, SIZE), 0)

  # 显示所有停止移动的图形
  for row, line in enumerate(stop_all_block_list):
   for col, block in enumerate(line):
    if block != '.':
     pygame.draw.rect(screen, (20, 128, 200), (col * SIZE, row * SIZE, SIZE, SIZE), 0)

  # 画网格线 竖线
  for x in range(BLOCK_COL_NUM):
   pygame.draw.line(screen, (0, 0, 0), (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
  # 画网格线 横线
  for y in range(BLOCK_ROW_NUM):
   pygame.draw.line(screen, (0, 0, 0), (0, y * SIZE), (BLOCK_COL_NUM * SIZE, y * SIZE), 1)

  # 显示右侧(得分、速度、下一行图形)
  # 得分
  score_show_msg = font.render('得分: ', True, (255, 255, 255))
  screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 10))
  score_show_msg = font.render(str(score), True, (255, 255, 255))
  screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 50))
  # 速度
  speed_show_msg = font.render('速度: ', True, (255, 255, 255))
  screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 100))
  speed_show_msg = font.render(speed_info, True, (255, 255, 255))
  screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 150))
  # 下一个图形(文字提示)
  next_style_msg = font.render('下一个: ', True, (255, 255, 255))
  screen.blit(next_style_msg, (BLOCK_COL_NUM * SIZE + 10, 200))
  # 下一个图形(图形)
  for row, line in enumerate(next_block):
   for col, block in enumerate(line):
    if block != '.':
     pygame.draw.rect(screen, (20, 128, 200), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE, SIZE, SIZE), 0)
     # 显示这个方格的4个边的颜色
     # 左
     pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), 1)
     # 上
     pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), 1)
     # 下
     pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
     # 右
     pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)

  # 显示游戏结束画面
  if game_over:
   game_over_tips = game_over_font.render('GAME OVER', True, RED)
   screen.blit(game_over_tips, ((SCREEN_WIDTH - game_over_font_width) // 2, (SCREEN_HEIGHT - game_over_font_height) // 2))
   # 显示"鼠标点击任意位置,再来一局"
   game_again = font.render('鼠标点击任意位置,再来一局', True, RED)
   screen.blit(game_again, ((SCREEN_WIDTH - game_again_font_width) // 2, (SCREEN_HEIGHT - game_again_font_height) // 2 + 80))

  # 刷新显示(此时窗口才会真正的显示)
  pygame.display.update()
  # FPS(每秒钟显示画面的次数)
  clock.tick(60) # 通过一定的延时,实现1秒钟能够循环60次


if __name__ == '__main__':
 main()

文件blocks.py代码如下:

# S形方块
block_s = [['.OO',
   'OO.',
   '...'],
   ['O..',
   'OO.',
   '.O.']]
# Z形方块
block_z = [['OO.',
   '.OO',
   '...'],
   ['.O.',
   'OO.',
   'O..']]
# I型方块
block_i = [['.O..',
   '.O..',
   '.O..',
   '.O..'],
   ['....',
   '....',
   'OOOO',
   '....']]
# O型方块
block_o = [['OO',
   'OO']]
# J型方块
block_j = [['O..',
   'OOO',
   '...'],
   ['.OO',
   '.O.',
   '.O.'],
   ['...',
   'OOO',
   '..O'],
   ['.O.',
   '.O.',
   'OO.']]
# L型方块
block_l = [['..O',
   'OOO',
   '...'],
   ['.O.',
   '.O.',
   '.OO'],
   ['...',
   'OOO',
   'O..'],
   ['OO.',
   '.O.',
   '.O.']]
# T型方块
block_t = [['.O.',
   'OOO',
   '...'],
   ['.O.',
   '.OO',
   '.O.'],
   ['...',
   'OOO',
   '.O.'],
   ['.O.',
   'OO.',
   '.O.']]

以上就是python 基于pygame实现俄罗斯方块的详细内容,更多关于python 俄罗斯方块的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python中的异常处理简明介绍
Apr 13 Python
使用Python的Zato发送AMQP消息的教程
Apr 16 Python
Python 实现数据库(SQL)更新脚本的生成方法
Jul 09 Python
Python使用matplotlib绘制正弦和余弦曲线的方法示例
Jan 06 Python
Python通过调用mysql存储过程实现更新数据功能示例
Apr 03 Python
python opencv 读取本地视频文件 修改ffmpeg的方法
Jan 26 Python
Python字符串对象实现原理详解
Jul 01 Python
对Python _取log的几种方式小结
Jul 25 Python
Python下opencv图像阈值处理的使用笔记
Aug 04 Python
如何理解python对象
Jun 21 Python
python操作redis数据库的三种方法
Sep 10 Python
python用tkinter实现一个简易能进行随机点名的界面
Sep 27 Python
使用Python快速打开一个百万行级别的超大Excel文件的方法
Mar 02 #Python
Autopep8的使用(python自动编排工具)
Mar 02 #Python
python 将Excel转Word的示例
Mar 02 #Python
Python字节单位转换(将字节转换为K M G T)
Mar 02 #Python
Python使用cn2an实现中文数字与阿拉伯数字的相互转换
Mar 02 #Python
jupyter notebook指定启动目录的方法
Mar 02 #Python
python实现发送邮件
Mar 02 #Python
You might like
粗略计算在线时间,bug:ip相同
2006/12/09 PHP
PHP性能优化准备篇图解PEAR安装
2011/12/05 PHP
php自定义urlencode,urldecode函数实例
2015/03/24 PHP
浅析PHP中的闭包和匿名函数
2017/12/25 PHP
PHP抽象类与接口的区别实例详解
2019/05/09 PHP
解决Laravel 不能创建 migration 的问题
2019/10/09 PHP
js的event详解。
2006/09/06 Javascript
extjs form textfield的隐藏方法
2008/12/29 Javascript
[原创]IE view-source 无法查看看源码 JavaScript看网页源码
2009/07/19 Javascript
IE7中javascript操作CheckBox的checked=true不打勾的解决方法
2009/12/07 Javascript
用XMLDOM和ADODB.Stream实现base64编码解码实现代码
2010/11/28 Javascript
Extjs TimeField 显示正常时间格式的代码
2011/06/28 Javascript
Jquery判断form表单数据是否变化
2016/03/30 Javascript
JSON中key动态设置及JSON.parse和JSON.stringify()的区别
2016/12/29 Javascript
如何使用Bootstrap 按钮实例详解
2017/03/29 Javascript
JavaScript数组push方法使用注意事项
2017/10/30 Javascript
nodejs使用socket5进行代理请求的实现
2020/02/21 NodeJs
vue 添加和编辑用同一个表单,el-form表单提交后清空表单数据操作
2020/08/03 Javascript
[15:15]教你分分钟做大人:狙击手
2014/10/30 DOTA
用PyQt进行Python图形界面的程序的开发的入门指引
2015/04/14 Python
在Python的Django框架中用流响应生成CSV文件的教程
2015/05/02 Python
python简单图片操作:打开\显示\保存图像方法介绍
2017/11/23 Python
Python实现求解一元二次方程的方法示例
2018/06/20 Python
python turtle工具绘制四叶草的实例分享
2020/02/14 Python
Python 爬取淘宝商品信息栏目的实现
2021/02/06 Python
html5中JavaScript removeChild 删除所有节点
2014/05/16 HTML / CSS
Eyeko美国:屡获殊荣的睫毛膏、眼线笔和眉妆
2018/07/05 全球购物
Laura Geller官网:美国彩妆品牌
2018/12/29 全球购物
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
总经理办公室主任岗位职责
2013/11/12 职场文书
采购文员岗位职责
2013/11/20 职场文书
优秀学生干部先进事迹材料
2014/05/26 职场文书
师范类求职信
2014/06/21 职场文书
综合素质评价个性与发展自我评价
2015/03/06 职场文书
nginx如何将http访问的网站改成https访问
2021/03/31 Servers
java高级用法JNA强大的Memory和Pointer
2022/04/19 Java/Android