俄罗斯方块游戏设计

项目简介:设计经典游戏——俄罗斯方块。

1、项目需求

使用 Python 的第三个库 Pygame 来制作俄罗斯方块。

2、项目思路

俄罗斯方块其实就是一副能够实现人机交互的动画,可以让人来控制四格拼版个的图形样式和位置的呈现。那么动画的原理是什么呢?就以我的孩子在绘画培训班的例子来做解答,就是让孩子先画出一副一副的静态图片,然后再将一副一副的静态图片装订成册,然后快速进行快速翻书,那么就会呈现出来动画的效果。

翻书

知道了动画的原理之后,对照我们的俄罗斯方块游戏,其实就是将不同样式的四格拼版游戏区域中的不同位置分解成 N 多张静态的图片。

3、项目步骤

  1. 构建 Python 开发环境。
  2. 在窗体内构建四格拼版游戏区域
  3. 在游戏区域内活动小方格
  4. 在游戏区域内绘制不同样式的四格拼板
  5. 四格拼板的冲突和限制

4、代码展示

# -*- coding: utf-8 -*-

import pygame, random

__author__ = {
    'name': "东方鹗",
    'github': "https://github.com/eastossifrage",
    'B 站主页': "https://space.bilibili.com/194359739",
    '知乎专栏': "https://zhuanlan.zhihu.com/ousi-python",
    '简书专栏': "https://www.jianshu.com/c/2036276cf0e8",
    'version': "1.0"
}
# 定义几个颜色     R   G   B
bgcolor       = ( 30,  25,  25) # 背景设置为黑色
yellow        = (255, 245,  40) # 黄色
light_yellow  = (175, 175,  20)
white         = (255, 245, 245)
gray          = (185, 185, 185)
black         = (  0,   0,   0)
red           = (155,   0,   0)
light_red     = (175,  20,  20)
green         = (  0, 155,   0)
light_green   = ( 20, 175,  20)
blue          = (  0,   0, 155)
light_blue    = ( 20,  20, 175)

# 定义颜色元组

colors = (yellow, gray, black, red, green, blue)

# 定义屏幕大小
s_width = 800
s_height = 600

# 定义俄罗斯方块游戏区域的大小
a_width = 364
a_height = 544

# 定义正方形的大小
edge = 20

# 定义游戏区域左上角坐标
coordinate = ((s_width-a_width)//4, s_height-a_height)

# 定义游戏区域中的小方格的行列数
box_row = a_height//edge # 小方格的每列个数,也就是行数
box_col = a_width//edge  # 小方格的每行个数,也就是列数

#定义游戏区域中的小方格的开始位置
inital_pos = (0, int(box_col//2))

# 以下是七种形状的拼版按照小方格的位置所对应的坐标
I = [[(0, -1), (0, 0), (0, 1), (0, 2)],
     [(-1, 0), (0, 0), (1, 0), (2, 0)]]
J = [[(-2, 0), (-1, 0), (0, 0), (0, -1)],
     [(-1, 0), (0, 0), (0, 1), (0, 2)],
     [(0, 1), (0, 0), (1, 0), (2, 0)],
     [(0, -2), (0, -1), (0, 0), (1, 0)]]
L = [[(-2, 0), (-1, 0), (0, 0), (0, 1)],
     [(1, 0), (0, 0), (0, 1), (0, 2)],
     [(0, -1), (0, 0), (1, 0), (2, 0)],
     [(0, -2), (0, -1), (0, 0), (-1, 0)]]
O = [[(0, 0), (0, 1), (1, 0), (1, 1)]]
S = [[(-1, 0), (0, 0), (0, 1), (1, 1)],
     [(1, -1), (1, 0), (0, 0), (0, 1)]]
T = [[(0, -1), (0, 0), (0, 1), (-1, 0)],
     [(-1, 0), (0, 0), (1, 0), (0, 1)],
     [(0, -1), (0, 0), (0, 1), (1, 0)],
     [(-1, 0), (0, 0), (1, 0), (0, -1)]]
Z = [[(0, -1), (0, 0), (1, 0), (1, 1)],
     [(-1, 0), (0, 0), (0, -1), (1, -1)]]

# 定义拼版元组
# shapes_tuple = (I, J, L, O, S, T, Z)
shapes_tuple = (J, L, S, T, Z)

# 定义刷新频率
fps = 15 #设置屏幕刷新率
def box(rect, color):
    ''' 功能:画出用于组成图形的单个正方形
        参数:rect: 该参数为一个pygame.Rect对象在游戏区域的相对坐标
              color:小方格的颜色
    '''
    pygame.Surface.fill(screen, color, rect) # 填充方格内的颜色
    pygame.draw.rect(screen, white, rect, 1)  # 画出方格的线条

def area():
    ''' 功能:画出游戏区域
    '''
    rect = pygame.Rect(coordinate, (a_width, a_height))
    pygame.draw.rect(screen, light_blue, rect, 5)          # 画出游戏区域

def rect_matrix():
    ''' 功能:以游戏区域为基础,画出用于呈现方块图形的矩阵。
    '''
    # 下面一行列表推导的代码用于构造小方格的二维列表
    return [[pygame.Rect((coordinate[0] + x*edge + 2),
              (coordinate[1] + y*edge + 2), edge, edge)
            for x in range(box_col)] for y in range(box_row)]

def color_matrix():
    ''' 功能:以游戏区域为基础,画出用于呈现方块颜色的矩阵。'''
    # 下面一行列表推导的代码用于构造小方格颜色的二维列表,默认是 None
    return [[None for x in range(box_col)] for y in range(box_row)]


def show_shape(rect_matrix, row, col, shape, color):
    '''功能:显示游戏区域内的四格拼版。
        参数:matrix: 游戏区域的网格的坐标
             row:拼版的中心点相对背景网格的横坐标
             col:拼版的中心点相对背景网格的纵坐标
             shape: 四格拼版的小方格相对于中心点的坐标
             color:拼版的颜色
    '''
    for x in shape:
        if row+x[0] >= 0: # 舍弃中心点以上部分,防止从最底部出现
            box(rect_matrix[row+x[0]][col+x[1]], color)

def conflict(color_matrix, row, col, shape):
    '''功能:检测游戏区域内的四格拼版是否与底部边界或其它已存在的拼版有冲突。
        参数:matrix: 游戏区域的网格的坐标
             row:拼版的中心点相对背景网格的横坐标
             col:拼版的中心点相对背景网格的纵坐标
             shape: 四格拼版的小方格相对于中心点的坐标
    '''
    for x in shape:
        if row+x[0] >= box_row:                   # 检测四格拼版是否与
            return True                           # 游戏区域内的底部边界有冲突
        if row+x[0] >= 0 and \
        color_matrix[row+x[0]][col+x[1]] != None: # 检测四格拼板中的小方格是否
            return True                           # 与当前位置有冲突

    return False

def clear_line(color_matrix):
    '''功能:清空已连成一线的行'''
    num = 0
    for x in color_matrix:
        if None not in x:
            num = color_matrix.index(x)
    for i in reversed(range(1, num+1)): # 需要倒序输出
        color_matrix[i] = color_matrix[i-1]


if __name__ == "__main__":
    pygame.init()  # 使用 pygame 之前先进行初始化
    clock=pygame.time.Clock()  # 定义时钟
    screen = pygame.display.set_mode((s_width, s_height), pygame.RESIZABLE, 32)
    # 上面一行代码的功能为设置屏幕大小,此实例为 800*600,
    # 第二个参数是 RESIZABLE,表示打开一个可以改变大小的窗口,
    # 第三个参数是 32 位真彩色。

    pygame.display.set_caption("俄罗斯方块") # 设置窗体名称

    r_mtx = rect_matrix()
    c_mtx = color_matrix()
    row, col = inital_pos
    color = random.choice(colors)
    shapes = random.choice(shapes_tuple)
    shape =  random.choice(shapes)
    shape_num = shapes.index(shape)  # 获取四格拼板的形状列表的下标

    while True:
        #pygame.time.delay(25)
        screen.fill(bgcolor)  # 表示填充背景颜色。
        for event in pygame.event.get():
            if event.type == pygame.QUIT: # 检测退出动作
                exit()

        area()

        for r in range(box_row):
            for c in range(box_col):
                if c_mtx[r][c] is not None:
                    box(r_mtx[r][c], c_mtx[r][c])

        shape_left = min(shape, key=lambda x:x[1])[1]   # 拼版的最左边坐标值
        shape_right = max(shape, key=lambda x:x[1])[1]  # 拼版的最右边坐标值

        cft = conflict(c_mtx, row, col, shape)

        keys = pygame.key.get_pressed()  # 检测键盘动作

        if keys[pygame.K_LEFT] and not cft:      # 使用向左键来控制方块向左移动
            if col + shape_left > 0:
                col -= 1
            if conflict(c_mtx, row, col, shape): # 如果冲突
                col += 1                         # 回退一格

        if keys[pygame.K_RIGHT] and not cft:     # 使用向左键来控制方块向右移动
            if col + shape_right < box_col-1:
                col += 1
            if conflict(c_mtx, row, col, shape): # 如果冲突
                col -= 1                         # 回退一格

        if keys[pygame.K_a] and not cft:         # 使用 W 键将拼版左转
            shape_num -= 1
            shape = shapes[shape_num%len(shapes)] # 选择新拼版
            shape_left = min(shape, key=lambda x:x[1])[1]   # 新拼版的最左边坐标值
            shape_right = max(shape, key=lambda x:x[1])[1]  # 新拼版的最右边坐标值
            if col+shape_left < 0 or \
            col+shape_right > box_col-1 or \
            conflict(c_mtx, row, col, shape): # 如果冲突、超出左或右边界
            # 要注意 or 逻辑的短路顺序
                shape_num += 1                            # 回退到上一个拼版
                shape = shapes[shape_num%len(shapes)]    # 选择原拼版

        if keys[pygame.K_d] and not cft:         # 使用 D 键将拼版右转
            shape_num += 1
            shape = shapes[shape_num%len(shapes)]
            shape_left = min(shape, key=lambda x:x[1])[1]   # 拼版的最左边坐标值
            shape_right = max(shape, key=lambda x:x[1])[1]  # 拼版的最右边坐标值
            if col+shape_left < 0 or \
            col+shape_right > box_col-1 or \
            conflict(c_mtx, row, col, shape): # 如果冲突、超出左或右边界
            # 要注意 or 逻辑的短路顺序
                shape_num -= 1                            # 回退到上一个拼版
                shape = shapes[shape_num%len(shapes)]     # 选择原拼版


        if cft:
            for x in shape:
                if row+x[0] > 0:  # 舍弃中心点以上部分,防止从最底部出现
                    c_mtx[row+x[0]-1][col+x[1]] = color
            if row != 0:    # 检测拼版重叠的位置是否在第一行,如果不是,生成新拼版
                row, col = inital_pos                # 设置新拼版的起始位置
                color = random.choice(colors)        # 重新选择颜色
                shapes = random.choice(shapes_tuple)
                shape =  random.choice(shapes)       # 重新选择拼版
            else:          # 检测拼版重叠的位置是否在第一行,如果是,游戏结束
                print("GAME OVER")
                break
        else:
            clear_line(c_mtx)                         # 清空已填满的行
            show_shape(r_mtx, row, col, shape, color) # 显示游戏区域内的四格拼版
            row += 1
        clock.tick(fps)
        pygame.display.update()

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容