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开发的小球完全弹性碰撞游戏代码
Oct 15 Python
Python Socket编程详细介绍
Mar 23 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
python3基于TCP实现CS架构文件传输
Jul 28 Python
python 读取文件并把矩阵转成numpy的两种方法
Feb 12 Python
python 获得任意路径下的文件及其根目录的方法
Feb 16 Python
python实现的按要求生成手机号功能示例
Oct 08 Python
python range实例用法分享
Feb 06 Python
python argparse传入布尔参数false不生效的解决
Apr 20 Python
Python基于tkinter canvas实现图片裁剪功能
Nov 05 Python
python flappy bird小游戏分步实现流程
Feb 15 Python
python和C/C++混合编程之使用ctypes调用 C/C++的dll
Apr 29 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
php GD绘制24小时柱状图
2008/06/28 PHP
关于PHP中Object对象的笔记分享
2011/06/28 PHP
eaglephp使用微信api接口开发微信框架
2014/01/09 PHP
php之curl实现http与https请求的方法
2014/10/21 PHP
php获取文件名后缀常用方法小结
2015/02/24 PHP
PHP自带方法验证邮箱是否存在
2016/02/01 PHP
Jquery + Ajax调用webService实例代码(asp.net)
2010/08/27 Javascript
JavaScript在for循环中绑定事件解决事件参数不同的情况
2014/01/20 Javascript
JavaScript基础函数整理汇总
2015/01/30 Javascript
AngularJS数据源的多种获取方式汇总
2016/02/02 Javascript
移动端刮刮乐的实现方式(js+HTML5)
2017/03/23 Javascript
详解使用Vue Router导航钩子与Vuex来实现后退状态保存
2017/09/11 Javascript
vue.js模仿京东省市区三级联动的选择组件实例代码
2017/11/22 Javascript
JavaScript反射与依赖注入实例详解
2018/05/29 Javascript
js代码规范之Eslint安装与配置详解
2018/09/08 Javascript
关于RxJS Subject的学习笔记
2018/12/05 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
JavaScript 链表定义与使用方法示例
2020/04/28 Javascript
vue-i18n实现中英文切换的方法
2020/07/06 Javascript
[00:43]DOTA2小紫本全民票选福利PA至宝全方位展示
2014/11/25 DOTA
Python模拟登录12306的方法
2014/12/30 Python
详解在Python中处理异常的教程
2015/05/24 Python
Python实现图像几何变换
2015/07/06 Python
Python的SimpleHTTPServer模块用处及使用方法简介
2018/01/22 Python
python kafka 多线程消费者&amp;手动提交实例
2019/12/21 Python
python实现超级马里奥
2020/03/18 Python
python实现简单贪吃蛇游戏
2020/09/29 Python
法国娇韵诗官方旗舰店:Clarins是来自法国的天然护肤品牌
2018/06/30 全球购物
一个精品风格的世界:Atterley
2019/05/01 全球购物
优秀班集体先进事迹材料
2014/05/28 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
优秀党员申报材料
2014/12/18 职场文书
2015年护士节活动策划方案
2015/05/04 职场文书
《没有任何借口》读后感:完美的执行能力
2020/01/07 职场文书
python中 .npy文件的读写操作实例
2022/04/14 Python
JavaScript设计模式之原型模式详情
2022/06/21 Javascript