python实现俄罗斯方块小游戏


Posted in Python onApril 24, 2020

回顾我们的python制作小游戏之路,几篇非常精彩的文章

我们用python实现了坦克大战

我们用python实现了飞船大战

我们用python实现了两种不同的贪吃蛇游戏

150行代码实现贪吃蛇游戏

我们用python实现了扫雷游戏

我们用python实现了五子棋游戏

今天我们用python来实现小时候玩过的俄罗斯方块游戏吧
具体代码与文件可以访问我的GitHub地址获取

第一步——构建各种方块

import random
from collections import namedtuple

Point = namedtuple('Point', 'X Y')
Shape = namedtuple('Shape', 'X Y Width Height')
Block = namedtuple('Block', 'template start_pos end_pos name next')

# 方块形状的设计,我最初我是做成 4 × 4,因为长宽最长都是4,这样旋转的时候就不考虑怎么转了,就是从一个图形替换成另一个
# 其实要实现这个功能,只需要固定左上角的坐标就可以了

# S形方块
S_BLOCK = [Block(['.OO',
  'OO.',
  '...'], Point(0, 0), Point(2, 1), 'S', 1),
 Block(['O..',
  'OO.',
  '.O.'], Point(0, 0), Point(1, 2), 'S', 0)]
# Z形方块
Z_BLOCK = [Block(['OO.',
  '.OO',
  '...'], Point(0, 0), Point(2, 1), 'Z', 1),
 Block(['.O.',
  'OO.',
  'O..'], Point(0, 0), Point(1, 2), 'Z', 0)]
# I型方块
I_BLOCK = [Block(['.O..',
  '.O..',
  '.O..',
  '.O..'], Point(1, 0), Point(1, 3), 'I', 1),
 Block(['....',
  '....',
  'OOOO',
  '....'], Point(0, 2), Point(3, 2), 'I', 0)]
# O型方块
O_BLOCK = [Block(['OO',
  'OO'], Point(0, 0), Point(1, 1), 'O', 0)]
# J型方块
J_BLOCK = [Block(['O..',
  'OOO',
  '...'], Point(0, 0), Point(2, 1), 'J', 1),
 Block(['.OO',
  '.O.',
  '.O.'], Point(1, 0), Point(2, 2), 'J', 2),
 Block(['...',
  'OOO',
  '..O'], Point(0, 1), Point(2, 2), 'J', 3),
 Block(['.O.',
  '.O.',
  'OO.'], Point(0, 0), Point(1, 2), 'J', 0)]
# L型方块
L_BLOCK = [Block(['..O',
  'OOO',
  '...'], Point(0, 0), Point(2, 1), 'L', 1),
 Block(['.O.',
  '.O.',
  '.OO'], Point(1, 0), Point(2, 2), 'L', 2),
 Block(['...',
  'OOO',
  'O..'], Point(0, 1), Point(2, 2), 'L', 3),
 Block(['OO.',
  '.O.',
  '.O.'], Point(0, 0), Point(1, 2), 'L', 0)]
# T型方块
T_BLOCK = [Block(['.O.',
  'OOO',
  '...'], Point(0, 0), Point(2, 1), 'T', 1),
 Block(['.O.',
  '.OO',
  '.O.'], Point(1, 0), Point(2, 2), 'T', 2),
 Block(['...',
  'OOO',
  '.O.'], Point(0, 1), Point(2, 2), 'T', 3),
 Block(['.O.',
  'OO.',
  '.O.'], Point(0, 0), Point(1, 2), 'T', 0)]

BLOCKS = {'O': O_BLOCK,
 'I': I_BLOCK,
 'Z': Z_BLOCK,
 'T': T_BLOCK,
 'L': L_BLOCK,
 'S': S_BLOCK,
 'J': J_BLOCK}


def get_block():
 block_name = random.choice('OIZTLSJ')
 b = BLOCKS[block_name]
 idx = random.randint(0, len(b) - 1)
 return b[idx]


def get_next_block(block):
 b = BLOCKS[block.name]
 return b[block.next]

第二部——构建主函数

import sys
import time
import pygame
from pygame.locals import *
import blocks

SIZE = 30 # 每个小方格大小
BLOCK_HEIGHT = 25 # 游戏区高度
BLOCK_WIDTH = 10 # 游戏区宽度
BORDER_WIDTH = 4 # 游戏区边框宽度
BORDER_COLOR = (40, 40, 200) # 游戏区边框颜色
SCREEN_WIDTH = SIZE * (BLOCK_WIDTH + 5) # 游戏屏幕的宽
SCREEN_HEIGHT = SIZE * BLOCK_HEIGHT # 游戏屏幕的高
BG_COLOR = (40, 40, 60) # 背景色
BLOCK_COLOR = (20, 128, 200) #
BLACK = (0, 0, 0)
RED = (200, 30, 30) # GAME OVER 的字体颜色


def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
 imgText = font.render(text, True, fcolor)
 screen.blit(imgText, (x, y))


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

 font1 = pygame.font.SysFont('SimHei', 24) # 黑体24
 font2 = pygame.font.Font(None, 72) # GAME OVER 的字体
 font_pos_x = BLOCK_WIDTH * SIZE + BORDER_WIDTH + 10 # 右侧信息显示区域字体位置的X坐标
 gameover_size = font2.size('GAME OVER')
 font1_height = int(font1.size('得分')[1])

 cur_block = None # 当前下落方块
 next_block = None # 下一个方块
 cur_pos_x, cur_pos_y = 0, 0

 game_area = None # 整个游戏区域
 game_over = True
 start = False # 是否开始,当start = True,game_over = True 时,才显示 GAME OVER
 score = 0 # 得分
 orispeed = 0.5 # 原始速度
 speed = orispeed # 当前速度
 pause = False # 暂停
 last_drop_time = None # 上次下落时间
 last_press_time = None # 上次按键时间

 def _dock():
 nonlocal cur_block, next_block, game_area, cur_pos_x, cur_pos_y, game_over, score, speed
 for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
 for _j in range(cur_block.start_pos.X, cur_block.end_pos.X + 1):
 if cur_block.template[_i][_j] != '.':
  game_area[cur_pos_y + _i][cur_pos_x + _j] = '0'
 if cur_pos_y + cur_block.start_pos.Y <= 0:
 game_over = True
 else:
 # 计算消除
 remove_idxs = []
 for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
 if all(_x == '0' for _x in game_area[cur_pos_y + _i]):
  remove_idxs.append(cur_pos_y + _i)
 if remove_idxs:
 # 计算得分
 remove_count = len(remove_idxs)
 if remove_count == 1:
  score += 100
 elif remove_count == 2:
  score += 300
 elif remove_count == 3:
  score += 700
 elif remove_count == 4:
  score += 1500
 speed = orispeed - 0.03 * (score // 10000)
 # 消除
 _i = _j = remove_idxs[-1]
 while _i >= 0:
  while _j in remove_idxs:
  _j -= 1
  if _j < 0:
  game_area[_i] = ['.'] * BLOCK_WIDTH
  else:
  game_area[_i] = game_area[_j]
  _i -= 1
  _j -= 1
 cur_block = next_block
 next_block = blocks.get_block()
 cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y

 def _judge(pos_x, pos_y, block):
 nonlocal game_area
 for _i in range(block.start_pos.Y, block.end_pos.Y + 1):
 if pos_y + block.end_pos.Y >= BLOCK_HEIGHT:
 return False
 for _j in range(block.start_pos.X, block.end_pos.X + 1):
 if pos_y + _i >= 0 and block.template[_i][_j] != '.' and game_area[pos_y + _i][pos_x + _j] != '.':
  return False
 return True

 while True:
 for event in pygame.event.get():
 if event.type == QUIT:
 sys.exit()
 elif event.type == KEYDOWN:
 if event.key == K_RETURN:
  if game_over:
  start = True
  game_over = False
  score = 0
  last_drop_time = time.time()
  last_press_time = time.time()
  game_area = [['.'] * BLOCK_WIDTH for _ in range(BLOCK_HEIGHT)]
  cur_block = blocks.get_block()
  next_block = blocks.get_block()
  cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y
 elif event.key == K_SPACE:
  if not game_over:
  pause = not pause
 elif event.key in (K_w, K_UP):
  # 旋转
  # 其实记得不是很清楚了,比如
  # .0.
  # .00
  # ..0
  # 这个在最右边靠边的情况下是否可以旋转,我试完了网上的俄罗斯方块,是不能旋转的,这里我们就按不能旋转来做
  # 我们在形状设计的时候做了很多的空白,这样只需要规定整个形状包括空白部分全部在游戏区域内时才可以旋转
  if 0 <= cur_pos_x <= BLOCK_WIDTH - len(cur_block.template[0]):
  _next_block = blocks.get_next_block(cur_block)
  if _judge(cur_pos_x, cur_pos_y, _next_block):
  cur_block = _next_block

 if event.type == pygame.KEYDOWN:
 if event.key == pygame.K_LEFT:
 if not game_over and not pause:
  if time.time() - last_press_time > 0.1:
  last_press_time = time.time()
  if cur_pos_x > - cur_block.start_pos.X:
  if _judge(cur_pos_x - 1, cur_pos_y, cur_block):
  cur_pos_x -= 1
 if event.key == pygame.K_RIGHT:
 if not game_over and not pause:
  if time.time() - last_press_time > 0.1:
  last_press_time = time.time()
  # 不能移除右边框
  if cur_pos_x + cur_block.end_pos.X + 1 < BLOCK_WIDTH:
  if _judge(cur_pos_x + 1, cur_pos_y, cur_block):
  cur_pos_x += 1
 if event.key == pygame.K_DOWN:
 if not game_over and not pause:
  if time.time() - last_press_time > 0.1:
  last_press_time = time.time()
  if not _judge(cur_pos_x, cur_pos_y + 1, cur_block):
  _dock()
  else:
  last_drop_time = time.time()
  cur_pos_y += 1

 _draw_background(screen)

 _draw_game_area(screen, game_area)

 _draw_gridlines(screen)

 _draw_info(screen, font1, font_pos_x, font1_height, score)
 # 画显示信息中的下一个方块
 _draw_block(screen, next_block, font_pos_x, 30 + (font1_height + 6) * 5, 0, 0)

 if not game_over:
 cur_drop_time = time.time()
 if cur_drop_time - last_drop_time > speed:
 if not pause:
  # 不应该在下落的时候来判断到底没,我们玩俄罗斯方块的时候,方块落到底的瞬间是可以进行左右移动
  if not _judge(cur_pos_x, cur_pos_y + 1, cur_block):
  _dock()
  else:
  last_drop_time = cur_drop_time
  cur_pos_y += 1
 else:
 if start:
 print_text(screen, font2,
  (SCREEN_WIDTH - gameover_size[0]) // 2, (SCREEN_HEIGHT - gameover_size[1]) // 2,
  'GAME OVER', RED)

 # 画当前下落方块
 _draw_block(screen, cur_block, 0, 0, cur_pos_x, cur_pos_y)

 pygame.display.flip()


# 画背景
def _draw_background(screen):
 # 填充背景色
 screen.fill(BG_COLOR)
 # 画游戏区域分隔线
 pygame.draw.line(screen, BORDER_COLOR,
  (SIZE * BLOCK_WIDTH + BORDER_WIDTH // 2, 0),
  (SIZE * BLOCK_WIDTH + BORDER_WIDTH // 2, SCREEN_HEIGHT), BORDER_WIDTH)


# 画网格线
def _draw_gridlines(screen):
 # 画网格线 竖线
 for x in range(BLOCK_WIDTH):
 pygame.draw.line(screen, BLACK, (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
 # 画网格线 横线
 for y in range(BLOCK_HEIGHT):
 pygame.draw.line(screen, BLACK, (0, y * SIZE), (BLOCK_WIDTH * SIZE, y * SIZE), 1)


# 画已经落下的方块
def _draw_game_area(screen, game_area):
 if game_area:
 for i, row in enumerate(game_area):
 for j, cell in enumerate(row):
 if cell != '.':
  pygame.draw.rect(screen, BLOCK_COLOR, (j * SIZE, i * SIZE, SIZE, SIZE), 0)


# 画单个方块
def _draw_block(screen, block, offset_x, offset_y, pos_x, pos_y):
 if block:
 for i in range(block.start_pos.Y, block.end_pos.Y + 1):
 for j in range(block.start_pos.X, block.end_pos.X + 1):
 if block.template[i][j] != '.':
  pygame.draw.rect(screen, BLOCK_COLOR,
   (offset_x + (pos_x + j) * SIZE, offset_y + (pos_y + i) * SIZE, SIZE, SIZE), 0)


# 画得分等信息
def _draw_info(screen, font, pos_x, font_height, score):
 print_text(screen, font, pos_x, 10, f'得分: ')
 print_text(screen, font, pos_x, 10 + font_height + 6, f'{score}')
 print_text(screen, font, pos_x, 20 + (font_height + 6) * 2, f'速度: ')
 print_text(screen, font, pos_x, 20 + (font_height + 6) * 3, f'{score // 10000}')
 print_text(screen, font, pos_x, 30 + (font_height + 6) * 4, f'下一个:')


if __name__ == '__main__':
 main()

游戏截图

python实现俄罗斯方块小游戏

运行效果

python实现俄罗斯方块小游戏

更多俄罗斯方块精彩文章请点击专题:俄罗斯方块游戏集合 进行学习。

更多有趣的经典小游戏实现专题,也分享给大家:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python装饰器的函数式编程详解
Feb 27 Python
Python使用BeautifulSoup库解析HTML基本使用教程
Mar 31 Python
Python打包可执行文件的方法详解
Sep 19 Python
python查看微信好友是否删除自己
Dec 19 Python
pyenv命令管理多个Python版本
Mar 26 Python
Python利用flask sqlalchemy实现分页效果
Aug 02 Python
对Python捕获控制台输出流的方法详解
Jan 07 Python
基于python的docx模块处理word和WPS的docx格式文件方式
Feb 13 Python
python读取当前目录下的CSV文件数据
Mar 11 Python
jupyter实现重新加载模块
Apr 16 Python
Python 实现一行输入多个数字(用空格隔开)
Apr 29 Python
Python小白学习爬虫常用请求报头
Jun 03 Python
iPython pylab模式启动方式
Apr 24 #Python
python实现扫雷小游戏
Apr 24 #Python
jupyter 使用Pillow包显示图像时inline显示方式
Apr 24 #Python
pyspark 随机森林的实现
Apr 24 #Python
Jupyter打开图形界面并画出正弦函数图像实例
Apr 24 #Python
pyspark给dataframe增加新的一列的实现示例
Apr 24 #Python
Pandas将列表(List)转换为数据框(Dataframe)
Apr 24 #Python
You might like
PHP SPL标准库之数据结构栈(SplStack)介绍
2015/05/12 PHP
通过修改配置真正解决php文件上传大小限制问题(nginx+php)
2015/09/23 PHP
CodeIgniter自定义控制器MY_Controller用法分析
2016/01/20 PHP
jquery tools系列 expose 学习
2009/09/06 Javascript
基于json的jquery地区联动效果代码
2011/07/06 Javascript
js中设置元素class的三种方法小结
2011/08/28 Javascript
jquery ajax 同步异步的执行 return值不能取得的解决方案
2012/01/08 Javascript
caller和callee的区别介绍及演示结果
2013/03/10 Javascript
JS获得URL超链接的参数值实例代码
2013/06/21 Javascript
深入理解JavaScript高级之词法作用域和作用域链
2013/12/10 Javascript
JavaScript用select实现日期控件
2015/07/17 Javascript
JavaScript仿支付宝6位数字密码输入框
2016/12/29 Javascript
微信小程序 picker 组件详解及简单实例
2017/01/10 Javascript
想用好React的你必须要知道的一些事情
2017/07/24 Javascript
layui之select的option叠加问题的解决方法
2018/03/08 Javascript
100行代码实现一个vue分页组功能
2018/11/06 Javascript
NodeJs 实现简单WebSocket即时通讯的示例代码
2019/08/05 NodeJs
一篇文章弄懂javascript中的执行栈与执行上下文
2019/08/09 Javascript
解决微信小程序中的滚动穿透问题
2019/09/16 Javascript
原生js实现文件上传、下载、封装等实例方法
2020/01/05 Javascript
vue 使用class创建和清除水印的示例代码
2020/12/25 Vue.js
python删除过期文件的方法
2015/05/29 Python
分享Python字符串关键点
2015/12/13 Python
Python 遍历列表里面序号和值的方法(三种)
2017/02/17 Python
python字符串过滤性能比较5种方法
2017/06/22 Python
python实现归并排序算法
2018/11/22 Python
对Django项目中的ORM映射与模糊查询的使用详解
2019/07/18 Python
python分布式编程实现过程解析
2019/11/08 Python
基于Python实现简单学生管理系统
2020/07/24 Python
婚礼证婚人证婚词
2014/01/13 职场文书
运动会广播稿50字
2014/01/26 职场文书
涉及车辆房产分割的离婚协议书范文
2014/10/12 职场文书
2014年学校禁毒工作总结
2014/12/23 职场文书
毕业实习指导教师评语
2014/12/31 职场文书
教导主任个人总结
2015/03/03 职场文书
商标侵权律师函
2015/05/27 职场文书