【问题标题】:Need advice on optimizing python 2d tileset rendering需要关于优化 python 2d 瓦片集渲染的建议
【发布时间】:2016-12-30 23:19:14
【问题描述】:

几个月来,我一直在研究基于 2D 等距图块的 MORPG,并意识到我的游戏屏幕渲染的帧速率非常低。 我已经研究和测试了几个星期,并且只能对我的帧速率进行边际提升。我使用了 cProfile 并测试了我的帧速率,我可以在程序上正常达到 100+ FPS,但是一旦我的“render()”函数被调用,它就会下降到 5 FPS。 这是该函数的(有点)精简版:

for y in range(0, 42):
        for x in range(0, 42):
            if (player.mapY + y - 21 > 0) and (player.mapY + y - 21 < 128) and (player.mapX + x - 21 > 0) and (
                                player.mapX + x - 21 < 128):
                if (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) > -64 and (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX)+halfGraphicSizeX < 1024+32 and\
                    (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY) > -32 and (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY)+halfGraphicSizeY < 600+32:
                    if self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0:

                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY),
                                            image=groundGraphics[
                                                self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21))],
                                            anchor=NW)

                    if (self.getObjectAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0):
                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34),
                                            # -34 for img height diff between ground & objects
                                            image=objectGraphics[
                                                self.getObjectAtYX(player.mapY + (y - 21), player.mapX + (x - 21))],
                                            anchor=NW)


            ghostCopy = list(gameState.itemsOnGround)
            for i in range(0, len(ghostCopy)):
                if ghostCopy[i].idNum > 0:
                    if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y ==
                        ghostCopy[i].mapY):
                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY),
                                            image=itemGraphics[ghostCopy[i].idNum],
                                            anchor=NW)

            ghostCopy = ""
            ghostCopy = list(gameState.monster)
            for i in range(0, len(ghostCopy)):
                if ghostCopy[i].active == True and ghostCopy[i].hp > 0:
                    if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y ==
                        ghostCopy[i].mapY):
                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34),
                                            # -34 for img height diff between ground & objects
                                            image=monsterGraphics[ghostCopy[i].type],
                                            anchor=NW)
                        canvas.create_rectangle(
                            (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 15,
                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 35),
                            (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16 + 33,
                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 29), fill="black",
                            width=0)
                        canvas.create_rectangle(
                            (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16,
                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 30),
                            (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16 + (
                                32 * (ghostCopy[i].hp / ghostCopy[i].maxHp)),
                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), fill="green",
                            width=0)

            ghostCopy = ""
            ghostCopy = list(gameState.sprite)
            for i in range(0, len(ghostCopy)):
                if ghostCopy[i].graphic[0:1] == "0":

                if ghostCopy[i].active == True and ghostCopy[i].username != "ME":
                    if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y ==
                        ghostCopy[i].mapY):
                        #"graphicToDraw" variable is derived from an animation state but has
                        #been removed from here to make it easier to read
                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34),
                                            # -34 for img height diff between ground & objects
                                            image=graphicToDraw,
                                            anchor=NW)

            if (y == 21):
                if (x == 21):
                    #"graphicToDraw" variable is derived from an animation state but has
                    #been removed from here to make it easier to read
                    canvas.create_image(
                        (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                        (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34),
                        # -34 for img height diff between ground & sprites
                        image=graphicToDraw,
                        anchor=NW)

            ghostCopy = ""
            ghostCopy = list(gameState.spells)
            for i in range(0, len(ghostCopy)):
                if ghostCopy[i].active:
                    if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y ==
                        ghostCopy[i].mapY):
                        canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX),
                                            (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34),
                                            image=spellGraphics[ghostCopy[i].id],
                                            anchor=NW)

render() 函数属于map 对象(self 指的是此代码段中的映射)。它有效地通过 x,y 轴上的图块 -21 .. 21,如果图块在地图图块边界 (0 .. 128) 内并且图块在屏幕尺寸 (1024x600) 之内,它会将其绘制到屏幕上.

“ghostCopy”对当前的gamestate 元素(例如拼写)进行快照,这样它就不会被接收服务器数据的线程在迭代过程中更新。

在我做的一些优化测试中,我在开始时减小了 y、x 范围,以最大限度地减少总循环迭代的数量。 我读到使用纹理图集/精灵表可以提高渲染速度,但我无法使用它来改进。

我尝试在 for 循环中手动绘制通常会在一般场景中渲染的图像数量,并获得大约 30+ fps 的速度。所以我的渲染功能比它可能慢了 25 fps。

我假设常量会检查每个循环迭代,以确定图块是否在屏幕内可以优化,但我不确定如何在不使用这样的循环的情况下做到这一点。

如果有人有任何建议,我将不胜感激。我已经被这个问题困扰了好几个星期,而且我的游戏根本没有取得任何真正的进展:(

** [编辑] ** 大多数建议似乎都是为了限制数学表达式的数量。我没有机会对此进行测试,但它是否可能只是限制数学量会显着优化帧速率?

【问题讨论】:

  • 这是在 Python2 还是 Python3 中?
  • 我正在使用 python3
  • 对象(玩家、幽灵)是否都完全在一个特定的图块中?这样您就可以确定每个对象的图块是什么?
  • 是的,但是服务器会在每次游戏状态更新时确定玩家可见的内容,以尽量减少发送的数量

标签: python loops optimization render tile


【解决方案1】:

您可以将所有涉及常量和 y 的表达式拉到外部 (for y) 循环内进行计算;例如,player.mapY + y - 21y * halfGraphicSizeXy * halfGraphicSizeY 等:每次只计算一次,放入一个变量,然后在整个代码中使用。 x 也是如此,但效果不那么好。

【讨论】:

  • 使用 Emett 的优化建议并将所有涉及 'y' 的数学运算移至上层 for 'y' 循环已产生一些帧速率增益(将 'y' 表达式移至上层 for 循环为 0.5 fps)
【解决方案2】:

这里是前 19 行代码的更新,应该可以提高性能。在这个例子中,我所做的只是减少你执行数学运算的总次数。

for y in range(0, 42):
    for x in range(0, 42):
        player_y = player.mapY + y - 21
        player_x = player.mapX + x -21

        if  player_y > 0 and player_y < 128 and player_x > 0 and player_x < 128:
            start_drawing_x_half_graphic_size = startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX
            start_drawing_y_half_graphic_size = startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY

            if start_drawing_x_half_graphic_size > -64 and start_drawing_x_half_graphic_size + halfGraphicSizeX < 1024+32 and\
                start_drawing_y_half_graphic_size > -32 and start_drawing_y_half_graphic_size + halfGraphicSizeY < 600+32:

                if self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0:

                    canvas.create_image(start_drawing_x_half_graphic_size,
                                        start_drawing_y_half_graphic_size,
                                        image=groundGraphics[
                                            self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21))],
                                        anchor=NW)

【讨论】:

  • 我对此进行了测试,在对象密集的场景中,它将帧速率从 5 fps 提高到 6 fps,在对象很少的场景中,将帧速率从 15 fps 提高到 17-20 fps。感谢您推荐的优化,我认为数学不会导致这么慢!
  • 还有很多可以提高代码速度的方法。这只是第一个合乎逻辑的起点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多