• Python课程设计之俄罗斯方块


    下载地址

    点击下载
    Python课程设计之俄罗斯方块
    软件需求:Python环境
    压缩包内含:源代码、打包好的可执行文件、文档报告
    在这里插入图片描述

    在这里插入图片描述

    运行效果

    在这里插入图片描述
    在这里插入图片描述

    基础页面

    (1)、搭建基础窗体
    使用tkinter实现基础窗体。

    import tkinter as tk#实现tkinter基础窗体,导入tkinter包取别名tk  
    win = tk.Tk()#tk库名,TK是tkinter库里面顶层窗体控件,用于表示用户主窗口  
    win.mainloop()# 维持窗体 
    
    • 1
    • 2
    • 3

    运行代码生成窗口如下
    在这里插入图片描述
    接下来需要在窗体里面,添加一个画布容器用来“装”俄罗斯方块,就是让这个画布作为面板,俄罗斯方块的移动和绘制均在这个画板上实现。
    这里我们设定一些俄罗斯方块游戏参数。行数为R(取20),列数为C(取12),俄罗斯方块的边长为cell_size(取30)
    通过tkinger的画布类Canvas实现,Canvas是tkinter库里常用的画布对象,主要用于绘制一些东西

    import tkinter as tk  #实现tkinter基础窗体,导入tkinter包取别名tk    
    cell_size = 30 #每一个方格的边长  
    C = 12 #列数  
    R = 20 #行数  
    height = R * cell_size #画布高度  
    width = C * cell_size #画布宽度   
    win = tk.Tk() #tk库名,TK是tkinter库里面顶层窗体控件,用于表示用户主窗口  
    canvas = tk.Canvas(win, width=width, height=height)#参数为宽度与高度,win是父控件或者说母控件  
    canvas.pack() #把所有的控件对象放置在指定位置,才能展示在界面窗体  
    win.mainloop() # 维持窗体  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
     此时界面的长宽就变成我们所需要的了
    
    • 1

    在这里插入图片描述

    接下来在画板上绘制俄罗斯方块空白位,首先建立一个函数draw_cell_by_cr,用于在画板上绘制单个俄罗斯方块,然后建立一个函数draw_blank_board,用于在画板上绘制所有空白方块(也就是空白方块板,为了和背景区分,所以设置空白方块为轻灰色)

    import tkinter as tk  #实现tkinter基础窗体,导入tkinter包取别名tk   
    cell_size = 30 #每一个方格的边长  
    C = 12 #列数  
    R = 20 #行数  
    height = R * cell_size #画布高度  
    width = C * cell_size #画布宽度  
    #在画板上绘制空白格子作为背景,先建立一个方法  
    def draw_cell_by_cr(canvas, c, r, color="#CCCCCC"):  
        """ 
         canvas: 画板,用于绘制一个方块的Canvas对象 
         c: 方块所在列 
         r: 方块所在行 
         color: 方块颜色,默认为#CCCCCC,轻灰色 
        """  
        x0 = c * cell_size #列数*方块大小  
        y0 = r * cell_size#行数*方块大小  
        x1 = c * cell_size + cell_size#列数*方块大小+1  
        y1 = r * cell_size + cell_size#行数*方块大小+1  
        canvas.create_rectangle(x0, y0, x1, y1, fill=color, outline="white", width=2)  
      #用canvas画板对象在指定的行、列进行绘制矩形并填充,以及白色边框,宽度为2   
    # 新建一个方法,绘制空白面板,也就是这些空白面板背景  
    def draw_blank_board(canvas): #绘制空白面板  
        for ri in range(R): #遍历所有行  
           for ci in range(C): #遍历每一行的所有列  
                    draw_cell_by_cr(canvas, ci, ri) #调用绘制函数    
    win = tk.Tk() #tk库名,TK是tkinter库里面顶层窗体控件,用于表示用户主窗口  
    canvas = tk.Canvas(win, width=width, height=height)#参数为宽度与高度,win是父控件或者说母控件  
    canvas.pack() #把所有的控件对象放置在指定位置,才能展示在界面窗体  
    draw_blank_board(canvas) #调用函数  
    win.mainloop() # 维持窗体  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述

    (2)、绘制O形俄罗斯方块
    先从最简单的o型俄罗斯方块(即田字格)
    在这里插入图片描述

    先要记录每各形状所有格子的坐标,以格子所在行序号为纵坐标,所在列序号为横坐标,以田字格中点为原点,则o型俄罗斯方块的四个格子的坐标如下图所示
    在这里插入图片描述

    用列表和元组记录为

    [  
        (-1, -1),  
        (0, -1),  
        (-1, 0),  
        (0, 0)  
    ]   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    将俄罗斯方块形状字符串和坐标列表的映射关系存到字典SHAPES里(映射关系这里可以理解为一一对应的关系),同时建立一个字典SHAPESCOLOR,来记录俄罗斯方块形状字符串和颜色的一一对应关系。由于一个形状可以看成多个方格组成的,所以我们可以新建函数draw_cells用来绘制这个形状,最后选择一个地方来绘制这个形状。

    1.# 定义形状  
    2.SHAPES = {  
    3.    "O": [(-1, -1), (0, -1), (-1, 0), (0, 0)],  
    4.}  
    5.# 定义形状的颜色  
    6.SHAPESCOLOR = {  
    7.    "O": "blue",  
    8.}  
    9.def draw_cells(canvas, c, r, cell_list, color="#CCCCCC"):  
    10.    """ 
    11.    绘制指定形状指定颜色的俄罗斯方块 
    12.     canvas: 画板 
    13.     r: 该形状设定的原点所在的行 
    14.     c: 该形状设定的原点所在的列 
    15.     cell_list: 该形状各个方格相对自身所处位置 
    16.     color: 该形状颜色 
    17.    """  
    18.    for cell in cell_list:  # 遍历这个形状的所有方格  
    19.        cell_c, cell_r = cell  # 它的实际位置坐标为相对的这个位置加上原点的行列  
    20.        ci = cell_c + c  
    21.        ri = cell_r + r  
    22.        # 判断该位置方格在画板内部(画板外部的方格不再绘制)  
    23.        if 0 <= c < C and 0 <= r < R:  # 判断该方格在画板内部,再绘制  
    24.            draw_cell_by_cr(canvas, ci, ri, color)  # 调用绘制函数  
    25.# 下面这行代码放在draw_blank_board(canvas) 下面  
    26.# 任取一个位置,如(3,3)绘制一个o型俄罗斯方块,用于展示  
    27.draw_cells(canvas, 3, 3, SHAPES['O'], SHAPESCOLOR['O'])  
    28.# 上面这行代码放在win.mainloop()上面  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    (3)、绘制其他俄罗斯方块
    俄罗斯方块主流分七种,除去上面的O型,其他六俄罗斯方块如图所示
    在这里插入图片描述
    对应的,要在SHAPES和SHAPESCOLOR中添加其他方块的坐标和颜色,添加后如下

    1.# 定义各种形状,用列表与元组记录  
    2.#用常量记录  
    3.SHAPES = {  
    4.    "Z": [(-1, -1), (0, -1), (0, 0), (1, 0)],  
    5.    "O": [(-1, -1), (0, -1), (-1, 0), (0, 0)],  
    6.    "S": [(-1, 0), (0, 0), (0, -1), (1, -1)],  
    7.    "T": [(-1, 0), (0, 0), (0, -1), (1, 0)],  
    8.    "I": [(0, 1), (0, 0), (0, -1), (0, -2)],  
    9.    "L": [(-1, 0), (0, 0), (-1, -1), (-1, -2)],  
    10.    "J": [(-1, 0), (0, 0), (0, -1), (0, -2)]  
    11.}  
    12.# 定义各种形状的颜色  
    13.SHAPESCOLOR = {  
    14.    "O": "blue",  
    15.    "Z": "Cyan",  
    16.    "S": "red",  
    17.    "T": "yellow",  
    18.    "I": "green",  
    19.    "L": "purple",  
    20.    "J": "orange",  
    21.}  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    将原来绘制O型俄罗斯方块处的代码改成如下代码,将这七种俄罗斯方块绘制出来

    1.draw_cells(canvas, 3, 3, SHAPES['O'], SHAPESCOLOR['O'])  
    2.draw_cells(canvas, 3, 8, SHAPES['S'], SHAPESCOLOR['S'])  
    3.draw_cells(canvas, 3, 13, SHAPES['T'], SHAPESCOLOR['T'])  
    4.draw_cells(canvas, 8, 3, SHAPES['I'], SHAPESCOLOR['I'])  
    5.draw_cells(canvas, 8, 8, SHAPES['L'], SHAPESCOLOR['L'])  
    6.draw_cells(canvas, 8, 13, SHAPES['J'], SHAPESCOLOR['J'])  
    7.draw_cells(canvas, 5, 18, SHAPES['Z'], SHAPESCOLOR['Z'])  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    界面动起来

    要让游戏界面动起来,需要实现两点:1、定时刷新页面 2、移动页面内部的俄罗斯方块。光实现1的话理论上页面也算动起来了,但是如果界面内容不变,每次新的页面和旧的界面看起来是一样的,给人的观看感觉跟没有动没有区别,所以必须要实现2,来更改界面内容,给人“动”起来的感觉
    
    • 1

    (1)、定时刷新页面
    实现定时刷新页面,写一个方法用于循环刷新,再定时调用,此部分代码写在原来代码最后的win.mainloop之前就好

    1.FPS = 500  # 刷新页面的毫秒间隔  
    2.def game_loop():  
    3.    win.update()  
    4.   
    5.    # ===用于展示刷新,后续会删掉===  
    6.    import time  
    7.    print(time.ctime())  
    8.    # ===========================  
    9.   
    10.    win.after(FPS, game_loop)  
    11.   
    12.win.update()  
    13.win.after(FPS, game_loop) # 在FPS 毫秒后调用 game_loop方法 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    此时运行发现命令行中会有如下输出,说明这个页面确实是每隔500ms刷新了一次

    在这里插入图片描述

    但是每次刷新后页面的内容是没有变化的,给人的感觉还是更没动一样,所以需要配合每次刷新,移动俄罗斯方块的位置
    (2)、移动俄罗斯方块
    对于某个俄罗斯方块,要绘制出来需要知道它的位置和类型,以及内部的各个方格。
    这三个数据可以用字典存起来,这里规定格式如下

    1.a_block = {  
    2.    'kind': 'O',  # 对应俄罗斯方块的类型  
    3.    'cell_list': SHAPES['O'],  # 对应俄罗斯方块的各个方格  
    4.    'cr': [3, 3]  # 对应横纵坐标,以左上角为原点,水平向右为横坐标轴正方向,竖直向下为纵坐标轴正方向  
    5.}  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时一个这样的字典其实就可以看做一个俄罗斯方块对象,然后再建一个专门的方法draw_block_move,来绘制俄罗斯方块的移动,其实移动就是清掉旧位置已经绘制的俄罗斯方块,再在新位置绘制新的俄罗斯方块就好。然后修改下game_loop方法,更新修改部分如下

    1.# 在draw_blank_board(canvas)语句下面添加如下语句  
    2.def draw_block_move(canvas, block, direction=[0, 0]):  
    3.    """ 
    4.    绘制向指定方向移动后的俄罗斯方块 
    5.    canvas: 画板 
    6.    block: 俄罗斯方块对象 
    7.    direction: 俄罗斯方块移动方向 
    8.    ""#把方块里面所有信息取出来
    9.    shape_type = block['kind']  
    10.    c, r = block['cr']  
    11.    cell_list = block['cell_list']  
    12.   
    13.    # 移动前,先清除原有位置绘制的俄罗斯方块,也就是用背景色绘制原有的俄罗斯方块  
    14.    draw_cells(canvas, c, r, cell_list)  
    15.     #移动,按照指定方向移动 
    16.    dc, dr = direction
    17.    new_c, new_r = c+dc, r+dr  
    18.    block['cr'] = [new_c, new_r]  #把更新后的信息存在方块对象里面
    19.    # 在新位置绘制新的俄罗斯方块就好  
    20.    draw_cells(canvas, new_c, new_r, cell_list, SHAPESCOLOR[shape_type])  
    21.   
    22.   
    23.a_block = {  
    24.    'kind': 'O',  # 对应俄罗斯方块的类型  
    25.    'cell_list': SHAPES['O'],  # 对应俄罗斯方块的各个方格  
    26.    'cr': [3, 3]  # 对应横纵坐标,以左上角为原点,水平向右为横坐标轴正方向,竖直向下为纵坐标轴正方向  
    27.}  
    28.   
    29.draw_block_move(canvas, a_block)  
    30.   
    31.   
    32.# 修改原来的game_loop方法如下  
    33.def game_loop():  
    34.    win.update()  
    35.   
    36.    down = [0, 1]  
    37.    draw_block_move(canvas, a_block, down)  
    38.   
    39.    win.after(FPS, game_loop)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在这里插入图片描述
    在这里插入图片描述

    生成、移动、固定、变形

    (1)、随机生成俄罗斯方块
    这里需要导入python自带的随机库random,使用其中的choice方法,从俄罗斯方块形状列表中随机选出一个。
    首先需要删除原来的位于game_loop方法上面的a_block = … 和draw_block_move(canvas, a_block)这2段代码(这两端代码俄罗斯方块是在固定位置,按照指定形状生成的,需要删掉这部分代码),然后再在原来的这2段代码所在位置,添加一个随机生成俄罗斯方块的方法generate_new_block

    1.def generate_new_block():  
    2.    # 随机生成新的俄罗斯方块  
    3.   #使用choice方法在方块形状列表中随机选取一个
    4.    kind = random.choice(list(SHAPES.keys()))  
    5.    # 对应横纵坐标,以左上角为原点,水平向右为x轴正方向,  
    6.    # 竖直向下为y轴正方向,x对应横坐标,y对应纵坐标  
    7.    cr = [C // 2, 0]  #设置初始的行列,在第一行的正中间
    8.    new_block = {  
    9.        'kind': kind,  # 对应俄罗斯方块的类型  
    10.        'cell_list': SHAPES[kind],  
    11.        'cr': cr  
    12.    }  
    13.   
    14.    return new_block  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    再将游戏运行方法game_loop修改成如下,其中,将当前的俄罗斯方块对象通过全局变量来存储,并在game_loop方法下面设置初始的俄罗斯方块为空(None),此时每次运行代码都会发现生成了随机的俄罗斯方块。

    1.def game_loop():  
    2.    win.update()  
    3.    global current_block  #获取全局变量
    4.    if current_block is None:  #如果它不存在,就生成,并原地绘制
    5.        new_block = generate_new_block()  
    6.        # 新生成的俄罗斯方块需要先在生成位置绘制出来  
    7.        draw_block_move(canvas, new_block)  
    8.        current_block = new_block  
    9.    else:  #否则向下移动这个方块对象
    10.        draw_block_move(canvas, current_block, [0, 1])  
    11.   
    12.    win.after(FPS, game_loop)  
    13.   
    14.current_block = None  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    在这里插入图片描述

    当然,此时还有问题没有解决,就是俄罗斯方块生成后,会一直往下移动,就算出了可视范围还是会往下移动(只是这时再也看不到了)。所以接下来要实现的是:当俄罗斯方块落地的时候,将它固定住,然后再去生成新的。
    (2)、固定已经失效的俄罗斯方块
    判断俄罗斯方块落地的原则是,该方块是否可以继续向下移动:这里首先需要的是一个方法check_move需要判断俄罗斯方块是否可以向指定位置移动,该generate_new_block方法的下面,添加check_move方法代码如下

    1.def check_move(block, direction=[0, 0]):#当方块落地时进行固定,然后生成新的方块
    2.    """ 
    3.        判断俄罗斯方块是否可以朝制定方向移动 
    4.        block: 俄罗斯方块对象 
    5.        direction: 俄罗斯方块移动方向 
    6.        boolean 是否可以朝制定方向移动 
    7.        """  
    8.    cc, cr = block['cr']  #先取出方块行列坐标
    9.    cell_list = block['cell_list']  #内部各个格子相对自身的坐标行列
    10.   
    11.    for cell in cell_list:  
    12.        cell_c, cell_r = cell  
    13.        c = cell_c + cc + direction[0]  
    14.        r = cell_r + cr + direction[1]  
    15.        # 判断该位置是否超出左右边界,以及下边界  
    16.        # 一般不判断上边界,因为俄罗斯方块生成的时候,可能有一部分在上边界之上还没有出来  
    17.        if c < 0 or c >= C or r >= R:  
    18.            return False  
    19.   
    20.    return True  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    再将游戏运行方法game_loop修改成如下,便可以每次俄罗斯方块落地后都会生成新的

    1.def game_loop():  
    2.    win.update()  
    3.    global current_block  #获取全局变量
    4.   
    5.    if current_block is None:  #如果它不存在,就生成,并原地绘制
    6.        new_block = generate_new_block()  
    7.        # 新生成的俄罗斯方块需要先在生成位置绘制出来  
    8.        draw_block_move(canvas, new_block)  
    9.        current_block = new_block  #并且设置当前的方块为新生成的对象
    10.    else:  #否则向下移动这个方块对象
    11.        if check_move(current_block, [0, 1]):#添加这个方法可以判断是否可以向下移动
    12.            draw_block_move(canvas, current_block, [0, 1])  
    13.        else:  
    14.            current_block = None  
    15.   
    16.    win.after(FPS, game_loop)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    此时仍然有个问题就是老的俄罗斯方块会被新的落地的俄罗斯方块重合。

    在这里插入图片描述

    这个需要处理,当俄罗斯方块发现下面有已固定的俄罗斯方块时,也不能移动。首先,我们需要添加一个变量block_list来记录已固定的俄罗斯方块。在原代码draw_blank_board(canvas)的位置的下面,添加如下代码

    1.block_list = []  #建立一个变量,计入已经固定的方块,使其不会重叠,先生成初始的
    2.for i in range(R):  #由空字符串,由R行C列组成的一个二维列表
    3.    i_row = ['' for j in range(C)]  
    4.    block_list.append(i_row)  
    然后再在固定俄罗斯方块的时候记录进去,所以一要添加一个save_block_to_list方法,将无法移动的俄罗斯方块记录进去。二要修改game_loop
    1.def save_block_to_list(block):  #计入无法移动的方块
    2.    shape_type = block['kind']  
    3.    cc, cr = block['cr']  
    4.    cell_list = block['cell_list']  
    5.   
    6.    for cell in cell_list: #在对应的行对应的列记录方块类型
    7.        cell_c, cell_r = cell  
    8.        c = cell_c + cc  
    9.        r = cell_r + cr  
    10.        # block_list 在对应位置记下其类型,也就是对应的字符串
    11.        block_list[r][c] = shape_type  
    12.   
    13.   #定义一个方法不断去刷新这个页面
    14.def game_loop():  
    15.    win.update()  
    16.    global current_block  #获取全局变量
    17.    if current_block is None:  #如果它不存在,就生成,并原地绘制
    18.        new_block = generate_new_block()  
    19.        # 新生成的俄罗斯方块需要先在生成位置绘制出来  
    20.        draw_block_move(canvas, new_block)  
    21.        current_block = new_block  #并且设置当前的方块为新生成的对象
    22.    else:  #否则向下移动这个方块对象
    23.        if check_move(current_block, [0, 1]): #添加这个方法判断是否可以向下移动 
    24.            draw_block_move(canvas, current_block, [0, 1])  
    25.        else:  
    26.            # 无法移动,记入 block_list 中  
    27.            save_block_to_list(current_block)  
    28.            current_block = None  #就让当前方块为None
    29.   
    30.    win.after(FPS, game_loop)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    然后还要再修改原来的check_move方法,就是发现下面有已固定的俄罗斯方块时,也不能移动。

    1.def check_move(block, direction=[0, 0]):  
    2.    """ 
    3.        判断俄罗斯方块是否可以朝制定方向移动 
    4.        block: 俄罗斯方块对象 
    5.        direction: 俄罗斯方块移动方向 
    6.        boolean 是否可以朝制定方向移动 
    7.        """  
    8.    cc, cr = block['cr']  
    9.    cell_list = block['cell_list']  
    10.   
    11.    for cell in cell_list:  
    12.        cell_c, cell_r = cell  
    13.        c = cell_c + cc + direction[0]  
    14.        r = cell_r + cr + direction[1]  
    15.        # 判断该位置是否超出左右边界,以及下边界  
    16.        # 一般不判断上边界,因为俄罗斯方块生成的时候,可能有一部分在上边界之上还没有出来  
    17.        if c < 0 or c >= C or r >= R:  
    18.            return False  
    19.   
    20.        # 必须要判断r不小于0才行,具体原因你可以不加这个判断,试试会出现什么效果  
    21.        if r >= 0 and block_list[r][c]:  
    22.            return False  
    23.   
    24.    return True  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    (3)、左右移动俄罗斯方块
    首先,需要新建一个左右移动俄罗斯方块的方法horizontal_move_block,其中的参数event对应键鼠事件,在save_block_to_list方法下面添加horizontal_move_block方法的代码如下

    1.def horizontal_move_block(event):  #新建一个方法,左右移动方块
    2.    """ 
    3.    左右水平移动俄罗斯方块 
    4.    """  
    5.    direction = [0, 0]  #默认方向为0
    6.    if event.keysym == 'Left':  #如果按键为left,方向向左移动一个
    7.        direction = [-1, 0]  
    8.    elif event.keysym == 'Right':  #如果按键为right,方向向右移动一个
    9.        direction = [1, 0]  
    10.    else:  
    11.        return  
    12.   
    13.    global current_block  #获取当前方块对象,如果不为空,且可以向这个方向移动
    14.    if current_block is not None and check_move(current_block, direction):  
    15.        draw_block_move(canvas, current_block, direction)  #移动绘制它
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    然后把键盘上的左右键绑定(bind)这个方法,代码如下

    1.# 在game_loop方法(之外)下面添加  
    2.canvas.focus_set() # 聚焦到canvas画板对象上  
    3.canvas.bind("<KeyPress-Left>", horizontal_move_block)  
    4.canvas.bind("<KeyPress-Right>", horizontal_move_block)  
    
    • 1
    • 2
    • 3
    • 4

    此时运行,就可以按左右方向键来移动俄罗斯方块了

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    (4)、变换俄罗斯方块
    左右键是移动俄罗斯方块,上下键我们则实现变换俄罗斯方块。按上键俄罗斯方块会进行顺时针九十度旋转。按下键俄罗斯方块会进行顺时针九十度旋转,这里需要添加旋转俄罗斯方块的方法rotate_block,以及让俄罗斯方块着陆的方法land,在horizontal_move_block方法下面添加rotate_block方法的代码如下

    1.def rotate_block(event):  #新建一个方法旋转方块,并且响应键鼠事件
    2.    global current_block  #获取当前方块对象
    3.    if current_block is None:  #作非空判断
    4.        return  
    5.   #在方块里面的所有方格做一个旋转变换
    6.    cell_list = current_block['cell_list']  
    7.    rotate_list = []  
    8.    for cell in cell_list:  
    9.        cell_c, cell_r = cell  
    10.        rotate_cell = [cell_r, -cell_c]  
    11.        rotate_list.append(rotate_cell)  
    12.   #然后新建一个旋转之后的方块对象
    13.    block_after_rotate = {  
    14.        'kind': current_block['kind'],  # 对应俄罗斯方块的类型  
    15.        'cell_list': rotate_list,  #对应方块的各个方格坐标
    16.        'cr': current_block['cr']  #对应的列与横,水平向右为列坐标,竖直向下为行坐标正方向
    17.    }  #这个字典可看作方块对象
    18.   
    19.    if check_move(block_after_rotate):  #判断是否可以进行旋转
    20.        cc, cr= current_block['cr']  
    21.        draw_cells(canvas, cc, cr, current_block['cell_list'])  #清除原有的方块,用默认颜色替换
    22.        draw_cells(canvas, cc, cr, rotate_list,SHAPESCOLOR[current_block['kind']])  #绘制旋转之后的方块
    23.        current_block = block_after_rotate  #更改当前方块对象为旋转之后的
    24.   
    25. 新建一个方法用于方块着路
    26.def land(event):  
    27.    global current_block  #获取当前方块对象
    28.    if current_block is None:  #做个非空判断
    29.        return  
    30.   #获取当前方块里面所有方格向下移动的最大行数,取其中最小的
    31.    cell_list = current_block['cell_list']  
    32.    cc, cr = current_block['cr']  
    33.    min_height = R  
    34.    for cell in cell_list:  
    35.        cell_c, cell_r = cell  
    36.        c, r = cell_c + cc, cell_r + cr  
    37.        if block_list[r][c]:  
    38.            return  
    39.        h = 0  
    40.        for ri in range(r+1, R):  #对应的方格从上到下遍历它
    41.            if block_list[ri][c]: #如果下面已经有固定的则退出 
    42.                break  
    43.            else:  
    44.                h += 1  #否则可向下移动的行数+1
    45.        if h < min_height:  #如果这个方格比之前可移动的行数小,则更新这个最小值
    46.            min_height = h  
    47.   
    48.    down = [0, min_height]  
    49.    if check_move(current_block, down):  #移动判断
    50.        draw_block_move(canvas, current_block, down)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    然后再在上一步绑定左右方向键的地方添加下面这行代码,让上键绑定(bind)这个方法

    1.canvas.bind("<KeyPress-Up>", rotate_block)  
    2.canvas.bind("<KeyPress-Down>", land)  
    
    • 1
    • 2

    在这里插入图片描述

    在这里插入图片描述

    消除与得分

    最后,要实现俄罗斯方块的消除,就是当一行满了的时候,需要消除这行,同时在通过窗口标题展示得分情况,这里需要添加check_row_completed方法判断指定行是否可以消除。同时添加check_and_clear方法检查所有行并消除,而消除之后,需要在画板上重新绘制一遍消除后剩余的俄罗斯方块,这里最好将之前的draw_blank_board方法修改为,可以绘制面板上残留的俄罗斯方块的方法,方法名改为draw_board修改代码如下

    1.# 绘制面板,也就是空白画板背景
    2.def draw_board(canvas, block_list):  
    3.    for ri in range(R):  #遍历所有行
    4.        for ci in range(C):  #遍历每一行的所有列
    5.            cell_type = block_list[ri][ci]  #取出当前位置对应的值
    6.            if cell_type:  #如果有方块,则按照对应的颜色绘制它
    7.                draw_cell_by_cr(canvas, ci, ri, SHAPESCOLOR[cell_type])  
    8.            else:  #否则,使用默认的青灰色绘制
    9.                draw_cell_by_cr(canvas, ci, ri)  #调用绘制函数
    10.   
    11.   
    12.# 新建一个方法,判断指定行是否已经可以清除
    13.def check_row_complete(row):  
    14.    for cell in row:  
    15.        if cell=='':  
    16.            return False  
    17.   
    18.    return True  
    19.   
    20.   
    21.score = 0  
    22.win.title("SCORES: %s" % score) # 窗口标题中展示分数  
    23.   
    24.   #新建一个方法,去检查并且清除
    25.def check_and_clear():  
    26.    has_complete_row =False  
    27.    for ri in range(len(block_list)):  
    28.        if check_row_complete(block_list[ri]):  
    29.            has_complete_row = True  
    30.            # 当前行可消除  
    31.            if ri > 0:  #如果不是第一行,就让当前行等于上一行
    32.                for cur_ri in range(ri, 0, -1):  #从上往下遍历
    33.                    block_list[cur_ri] = block_list[cur_ri-1][:]  
    34.        block_list[0] = ['' for j in range(C)]  
    35.            else:  #如果为第一行,就使用空行绘制
    36.                block_list[ri] = ['' for j in range(C)]  
    37.            global score  #每删除一行增加10分
    38.            score += 10  
    39.   
    40.    if has_complete_row:  #如果检查发现所有行被删除,则需要重新绘制画板
    41.        draw_board(canvas, block_list)  
    42.   
    43.        win.title("SCORES: %s" % score)  #并修改标题得分
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    同时将原有的这两行代码

    1.draw_blank_board(canvas)  
    2.   
    3.block_list = []  
    4.for i in range(R):  
    5.    i_row = ['' for j in range(C)]  
    6.    block_list.append(i_row) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    修改成

    1.block_list = []  
    2.for i in range(R):  
    3.    i_row = ['' for j in range(C)]  
    4.    block_list.append(i_row)  
    5.   
    6.draw_board(canvas, block_list)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再在game_loop方法内部的current_block = None语句后添加下面语句

    1.check_and_clear()  
    
    • 1

    在这里插入图片描述

    在这里插入图片描述

    一般来说,当新的俄罗斯方块无法移动时,游戏就算游戏结束了。这里的判断机制是,新生成的俄罗斯方块已经无法在出生点放置了,也就是出生点都已经被之前的固定的方块占用了,就算游戏结束。在代码文件开头添加

    1.from tkinter import messagebox  #用于弹窗提醒
    
    • 1

    再修改game_loop方法如下

    1.def game_loop():  
    2.    win.update()  
    3.    global current_block  
    4.    if current_block is None:  
    5.        new_block = generate_new_block()  
    6.        # 新生成的俄罗斯方块需要先在生成位置绘制出来  
    7.        draw_block_move(canvas, new_block)  
    8.        current_block = new_block  
    9.        if not check_move(current_block, [0, 0]):#如果新生成的方块无法在出生点放置,也就是出身点被占有
    10.            messagebox.showinfo("Game Over!", "Your Score is %s" % score)  
    11.              #第一个字符串是弹窗提醒,第二个字符串是弹窗内容
    12.            win.destroy()  #弹窗关闭后,销毁游戏界面
    13.            return  #并退出游戏循环
    14.    else:  #否则直接向下移动这个方块对象
    15.        if check_move(current_block, [0, 1]):#添加这个方法判断是否可以向下移动
    16.            draw_block_move(canvas, current_block, [0, 1])  
    17.        else:  
    18.            # 无法移动,记入 block_list 中  
    19.            save_block_to_list(current_block)  
    20.            current_block = None  #就让当前的方块为None
    21.            check_and_clear()  #调用清除与得分函数
    22.   
    23.    win.after(FPS, game_loop)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

  • 相关阅读:
    基于PSO粒子群优化的汽车刹车稳定性数据matlab仿真与分析
    Python中直接赋值、浅拷贝和深拷贝的区别
    [毕业设计] 基于单片机的智能快递柜设计与实现 - stm32 物联网
    2022 年广西职业院校技能大赛高职组《云计算》赛项赛卷
    vue 弹出消息框
    程桌面管理软件Apple Remote Desktop mac中文介绍说明
    最好的天线基础知识!超实用 随时查询
    css 流式布局
    网络安全笔记-DDoS攻击
    GIT_工作3年后对GIT重新总结学习
  • 原文地址:https://blog.csdn.net/m0_46669450/article/details/125491036