python tkinter实现俄罗斯方块基础版 —— 五、后续优化

作者自我介绍:大爽歌, b站小UP主直播编程+红警三python1对1辅导老师

1 - 答评论问——卡顿优化

之前做过python tkinter实现俄罗斯方块的教程:
https://www.bilibili.com/video/BV1eJ411h7ZV
对应博客:https://blog.csdn.net/python1639er/article/details/104069590

后来有一些观众朋友反馈:玩到后面特别的卡顿。

由于我自己玩的时候感觉不卡(三五分钟都没啥卡顿感觉)
所以当时没有细想,以为是他们电脑配置或者改了代码的原因。

直到前不久,我直播写代码的时候,有个老哥又提到了这个卡顿的问题。
当时刚好有空,仔细看了下代码,发现确实有问题会导致卡顿。

那是什么问题导致了卡顿呢

是清理方块的方式有问题

游戏里面清理方块的方式:
是通过绘制一个新的背景色的方格覆盖来实现的。

具体的来讲

  • 当俄罗斯方块下落(左右移动同理)时
    其过程如示意图
    在这里插入图片描述

每一次移动都会新增四个方块进行绘制。

  • 当下落的俄罗斯方块填满一行,要清除的时候

代码里面是重新绘制整个面板所有方块。

每一次清理都会新增R*C个方块进行绘制
代码里面行数R为12,列数C为20
所以总数为240。
即每一次清理都会新增240个。

问题来了,所有这些新增的方块从未被清理过。
那么我们不难发现,随着时间的推移,方块越来越多,游戏因此越来越卡顿。

那么这个问题怎么解决呢?

修改清理方式,之前是通过用背景色覆盖来假清理。

下来我们需要想办法,对要去除的方块,实现真正的清理。

补充说明:
用背景色覆盖来进行清理的方法,在pygame里面经常会用到。
其在pygame里面应该是不会卡顿的,因为pygame的具体机制有些不同。

tkinter Canvas 类 提供了类方法delete来实现这样的清理。

该方法的详细解释可以看我最近的博客文章:
tkinter Canvas delete 方法详解

这里我们用该方法通过tag去删除已绘制的对象
代码示例如下

canvas.create_rectangle(50, 50, 100, 150, fill="red", tag="one")  # 绘制
canvas.delete("one")  # 清除tag为"one"的绘制对象,即上面的绘制
代码思路

既然要通过tag来删除绘制对象,那么我们就需要思考应该有几种tag。
或者说,有几种要清理的场景

通过上文的分析,不难想到有两种。

  1. 正在下落的俄罗斯方块,移动时要清理移动之前的俄罗斯方块。
    那么对正在下落的俄罗斯方块,不妨记其tag为"falling"
    移动正在下落的俄罗斯方块后,先清理掉tag为"falling"的,再绘制一遍俄罗斯方块。
  2. 已经落地的俄罗斯方块,凑齐一行后需要按行来清除。
    那么对于这些方块,记其tag为"row-%s" % r

所以tag有两类

  • "falling"
  • "row-%s" % r,记其 tag_kind 为"row"

而背景色方块只需要游戏开始时绘制一遍,后面不用再绘制,所以不需要清理,不设置tag。

修改draw_cell_by_cr

原来的draw_cell_by_cr

def draw_cell_by_cr(canvas, c, r, color="#CCCCCC"):
    x0 = c * cell_size
    y0 = r * cell_size
    x1 = c * cell_size + cell_size
    y1 = r * cell_size + cell_size
    canvas.create_rectangle(x0, y0, x1, y1,fill=color, outline="white", width=2)

修改后为

def draw_cell_by_cr(canvas, c, r, color="#CCCCCC", tag_kind=""):
    x0 = c * cell_size
    y0 = r * cell_size
    x1 = c * cell_size + cell_size
    y1 = r * cell_size + cell_size
    if tag_kind == "falling":
        canvas.create_rectangle(x0, y0, x1, y1, fill=color,outline="white", width=2, tag=tag_kind)
    elif tag_kind == "row":
        canvas.create_rectangle(x0, y0, x1, y1, fill=color, outline="white", width=2, tag="row-%s" % r)
    else:
        canvas.create_rectangle(x0, y0, x1, y1, fill=color, outline="white", width=2)
修改对draw_cell_by_cr的调用
  • draw_board
    原来为
def draw_board(canvas, block_list):
    for ri in range(R):
        for ci in range(C):
            cell_type = block_list[ri][ci]
            if cell_type:
                draw_cell_by_cr(canvas, ci, ri, SHAPESCOLOR[cell_type])
            else:
                draw_cell_by_cr(canvas, ci, ri)

修改后为

# 绘制面板, 只有在第一次绘制时才绘制背景色方块
def draw_board(canvas, block_list, isFirst=False):
    # 删掉原来所有的行
    for ri in range(R):
        canvas.delete("row-%s" % ri)

    for ri in range(R):
        for ci in range(C):
            cell_type = block_list[ri][ci]
            if cell_type:
                draw_cell_by_cr(canvas, ci, ri, SHAPESCOLOR[cell_type], tag_kind="row")
            elif isFirst:
                draw_cell_by_cr(canvas, ci, ri)

同时修改原来,初始调用draw_board的代码(大概在91行左右的地方)
修改原来的draw_board(canvas, block_list)
draw_board(canvas, block_list, True)

  • draw_cells
    原来为
def draw_cells(canvas, c, r, cell_list, color="#CCCCCC"):
    for cell in cell_list:
        cell_c, cell_r = cell
        ci = cell_c + c
        ri = cell_r + r
        # 判断该位置方格在画板内部(画板外部的方格不再绘制)
        if 0 <= c < C and 0 <= r < R:
            draw_cell_by_cr(canvas, ci, ri, color)

修改后为

def draw_cells(canvas, c, r, cell_list, color="#CCCCCC"):
    for cell in cell_list:
        cell_c, cell_r = cell
        ci = cell_c + c
        ri = cell_r + r
        # 判断该位置方格在画板内部(画板外部的方格不再绘制)
        if 0 <= c < C and 0 <= r < R:
            draw_cell_by_cr(canvas, ci, ri, color, tag_kind="falling")
  • draw_block_move
    原来为
def draw_block_move(canvas, block, direction=[0, 0]):
    shape_type = block['kind']
    c, r = block['cr']
    cell_list = block['cell_list']

    # 移动前,先清除原有位置绘制的俄罗斯方块,也就是用背景色绘制原有的俄罗斯方块
    draw_cells(canvas, c, r, cell_list)

    dc, dr = direction
    new_c, new_r = c+dc, r+dr
    block['cr'] = [new_c, new_r]
    # 在新位置绘制新的俄罗斯方块就好
    draw_cells(canvas, new_c, new_r, cell_list, SHAPESCOLOR[shape_type])

修改后为

def draw_block_move(canvas, block, direction=[0, 0]):
    shape_type = block['kind']
    c, r = block['cr']
    cell_list = block['cell_list']

    # 移动前,清除原有位置绘制的俄罗斯方块
    canvas.delete("falling")

    dc, dr = direction
    new_c, new_r = c+dc, r+dr
    block['cr'] = [new_c, new_r]
    # 在新位置绘制新的俄罗斯方块就好
    draw_cells(canvas, new_c, new_r, cell_list, SHAPESCOLOR[shape_type])
  • save_block_to_list
    原来为
def save_block_to_list(block):
    shape_type = block['kind']
    cc, cr = block['cr']
    cell_list = block['cell_list']

    for cell in cell_list:
        cell_c, cell_r = cell
        c = cell_c + cc
        r = cell_r + cr
        # block_list 在对应位置记下其类型draw_cells
        block_list[r][c] = shape_type

修改后为

def save_block_to_list(block):
    # 清除原有的打上了 falling 标签的方块
    canvas.delete("falling")

    shape_type = block['kind']
    cc, cr = block['cr']
    cell_list = block['cell_list']

    for cell in cell_list:
        cell_c, cell_r = cell
        c = cell_c + cc
        r = cell_r + cr
        # block_list 在对应位置记下其类型
        block_list[r][c] = shape_type

        draw_cell_by_cr(canvas, c, r, SHAPESCOLOR[shape_type], tag_kind="row")

到这里,就改好了。
此时运行软件,应该就没有越玩越卡顿的问题了。

如果还卡,那可能又有其他bug(雾

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值