在某些情况下,在两个不同的事件中更改一个字典会导致 KeyError(因为键事件的时间 + 由于 dt 导致的事件)

Changing one dictionary at two different events leads to KeyError in some cases (because of timing of key event + event due to dt)

提问人:Rabinzel 提问时间:10/20/2021 最后编辑:DharmanRabinzel 更新时间:10/20/2021 访问量:248

问:

底部最后一个问题的副本: “我不知道如何解决这个错误。一个动作与时间相关,另一个动作与玩家输入相关。有人有想法吗?python 中是否有一些东西可以处理一个对象在不同时间从不同地点更改的情况?

我在 Pygame 中编写自己的俄罗斯方块。目前,我的目标是从这里重建游戏模式“生存”: https://jstris.jezevec10.com/?play=4 玩家需要尽可能长时间地生存,而每 1 秒就会在底部添加一条只有 1 个空闲位置的新线,并将游戏领域向顶部移动。

我的问题是,有一个 key.event,玩家掉落一个棋子并用该动作清除一行,另一方面,还有一个时间事件,每隔一秒就会随机添加一行到字段中。这两个事件都会更改字典中的值。通常,此代码有效,但是根据操作,代码会抛出错误的时间窗口非常小。

我的游戏的简短说明: 有一个网格(列表列表 10x20),其中每个元素都是 RGB 颜色 (0,0,0),每次都以黑色开头,并将填充当前掉落的棋子的信息(颜色)或底部的字段锁定棋子。网格将每帧绘制一次。已经放置的棋子存储在字典中。网格每次在绘制之前都会请求锁定部分的字典。

screenshot of the original game.

在这张图片中,您看到了原始游戏。此时,字段中已经添加了 6 行。 每条线都将在字段底部生成灰色,并带有一个自由的随机点。每次生成一行时,信息都会添加到我的“锁定”字典中 ( key = (x,y) : value = (153,153,153) ),并且一个函数将每个键中的 y 值更改 1 到顶部。 同时,如果玩家清除了一行(在这张图片中,它是从底部开始计数的第 6 行),则有一个函数会在“锁定”字典中删除该清除的行,并将所有大于该行的 y 值更改 1 到底部。

以下是两个函数:

def clear_rows(grid, locked): # (0,0) is at the top left, x increases right, y increases down

    
    i = len(grid) - 1  # last row
    rows_cleared = 0
    while i > -1:
        row = grid[i]

        if BLACK not in row: #which means the row is full and can be cleared
            i_new = i + rows_cleared
            for j in range(len(row)):
                del locked[(j, i_new)]
            rows_cleared += 1

            for key in sorted(list(locked), key=lambda x: x[1])[::-1]:
                x_coord, y_coord = key
                if y_coord < i_new:
                    new_key = (x_coord, y_coord + 1)
                    locked[new_key] = locked.pop(key)
            i -= 1
        else:
            i -= 1

    return rows_cleared

def survival_add_line(locked):
    for key in sorted(list(locked)):
        x, y = key
        new_key = (x, y - 1)
        locked[new_key] = locked.pop(key)

    gap = random.randint(0,9)
    for i in range(10):
        if not i == gap:
            locked[(i, 19)] = SURVIVAL_GRAY

这是我的主循环中的代码:

if level_time / 1000 >= 1:
            survival_add_line(locked)
            level_time = 0
if change_piece:
    for pos in tetrimino_position:
        locked[(pos[0], pos[1])] = current_piece.color      
    current_piece = next_pieces[0]
    next_pieces.pop(0)
    next_pieces.append(new_tetromino())
    change_piece = False
    cleared_lines += clear_rows(grid, locked)

我注意到这个错误只能发生在字段的最底部。如果我理解正确的话,当玩家在 survival_add_line() 已经被调用但在 change_piece 之前的那一刻掉落一个棋子时,就会发生这种情况。 Survival_add_line() 将每一行都移到顶部,然后 clear_row 函数想要删除最后一行(现在是倒数第二行),并且找不到最后一行中的键来删除 --> KeyError。

我不知道如何解决这个错误。一个动作与时间相关,另一个动作与玩家输入相关。 有人有想法吗? python 中是否有一些东西可以处理一个对象在不同时间从不同位置更改的情况?

字典 pygame的 可变

评论

1赞 cookertron 10/20/2021
怎么样:如果change_piece:等等 elif level_time / 1000 >= 1:等等等等
0赞 Rabinzel 10/20/2021
但那将是一个要么......或者不对的事情,不是吗?两个块都是独立的。玩家可以在每个点上掉落一个棋子,反之,1秒后,无论是否更换棋子,都会添加一行。
0赞 cookertron 10/20/2021
是的,我明白你的意思。如果交换 if 语句,以便首先测试change_pieces测试,然后测试level_time会发生什么?
0赞 Rabinzel 10/20/2021
我按这个顺序先吃了。那时我不会收到错误,但我的字段被颜色/黑色/灰色弄得一团糟。同样的问题仅在另一个方向上。字典将获得有关坐标及其颜色的错误信息
0赞 Rabinzel 10/20/2021
但你的第一个答案可能是一条路要走。我用 elif 尝试了 50 次,但无法获得错误情况。如果我这样做,那么所发生的一切就是如果时间是 >1000,我会跳过 1 帧的更改部分,然后在下一帧中它会被检测到。代码以 60fps 的速度运行,所以这应该不会对游戏速度产生影响,对吧?

答:

0赞 Dharman #1

if/elif 块不是用 if 调用这两个语句,而是完成工作!(change_piece现在可以延迟 1 帧,但这对玩家来说并不明显)

代表提问者发布