【问题标题】:PyGame: Panning tiled map causes gapsPyGame:平移平铺地图会导致间隙
【发布时间】:2017-12-30 12:42:26
【问题描述】:

我在 Python 3.4.3 (Win8.1) 上使用 PyGame 1.9.2。

我的游戏窗口包含一个使用 48x48px 瓷砖的瓷砖地图。游戏框架使用游戏框架类的以下方法根据玩家输入移动图块:

def moveTiles(self):
    xmove = self.parent.dt * self.panning[0] * self.panning_speed
    ymove = self.parent.dt * self.panning[1] * self.panning_speed
    for tile in self.game_map:
        tile.x += xmove
        tile.y += ymove
        tile.rect.x = int(tile.x)
        tile.rect.y = int(tile.y)
        tile.pos = (tile.rect.x, tile.rect.y)

虽然“self.panning_speed”很容易解释,但 self.panning 是一个包含 x 和 y 平移的两个值的列表(例如 [0, 0] = 不平移,[-1, 1] = 向下平移 -左等)。

然后将 tile.x/tile.y 值“整合”以用作 rect 和 pos(后者用于 blitting)的 x-/y-值。

我将完全相同的数量添加到每个图块的 x/y 值,然后我在它们上使用“int”,据我所知,它总是将浮点数降低到下一个较低的 int。所以从技术上看,每个图块的 x/y 值的相对变化与任何其他图块的 x/y 值完全相同。

然而:我仍然时不时地在行或列之间出现间隙!这些间隙不规则地显示,但非常明显,当您停止平移而间隙可见时,该间隙将一直存在,直到您再次平移。

请参阅此图片以获取示例: See this image for an example (black line in the red square is the gap; gold and pink are the tiles)

我的代码中哪里会出现这些空白?

【问题讨论】:

  • float number 不能保留每个值,所以我们遇到0.1 + 0.2 != 0.3 的情况。
  • 我只会使用整数值。或decimal。或整数值,并在一个变量中保持 int 和 float 之间的差异以累积该差异,然后在累积值大于 1 时添加到所有变量 1。
  • 请提供minimal, complete and verifiable example,以便我们为您提供合适的解决方案。

标签: python pygame tile


【解决方案1】:

这是一个应该会更好的解决方案。只需在玩家移动时将velocity 添加到panning 向量中,并且永远不要移动瓷砖。然后,平移将添加到瓷砖的rect.topleft 位置,当它们被blitted 以获得正确的位置时。您必须先将平移转换为整数(创建另一个向量,在示例中称为 offset),然后才能将其添加到平铺位置,否则您仍然会出现间隙。

import itertools
import pygame as pg
from pygame.math import Vector2


TILE_SIZE = 44
TILE_IMG1 = pg.Surface((TILE_SIZE, TILE_SIZE))
TILE_IMG1.fill(pg.Color('dodgerblue3'))
TILE_IMG2 = pg.Surface((TILE_SIZE, TILE_SIZE))
TILE_IMG2.fill(pg.Color('aquamarine3'))


class Tile(pg.sprite.Sprite):

    def __init__(self, pos, image):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect(topleft=pos)
        self.pos = Vector2(pos)


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    dt = 0

    image_cycle = itertools.cycle((TILE_IMG1, TILE_IMG2))
    game_map = pg.sprite.Group()
    # Create tile instances and add them to the game map group.
    for y in range(16):
        for x in range(20):
            image = next(image_cycle)
            game_map.add(Tile((x*TILE_SIZE, y*TILE_SIZE), image))
        next(image_cycle)

    panning = Vector2(0, 0)
    velocity = Vector2(0, 0)

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_a:
                    velocity.x = 100
                elif event.key == pg.K_d:
                    velocity.x = -100
            elif event.type == pg.KEYUP:
                if event.key == pg.K_d and velocity.x < 0:
                    velocity.x = 0
                elif event.key == pg.K_a and velocity.x > 0:
                    velocity.x = 0

        # Add the velocity to the panning when we're moving.
        if velocity.x != 0 or velocity.y != 0:
            panning += velocity * dt

        game_map.update()

        screen.fill((30, 30, 30))
        # Assigning screen.blit to a local variable improves the performance.
        screen_blit = screen.blit
        # Convert the panning to ints.
        offset = Vector2([int(i) for i in panning])
        # Blit the tiles.
        for tile in game_map:
            # Add the offset to the tile's rect.topleft coordinates.
            screen_blit(tile.image, tile.rect.topleft+offset)

        pg.display.flip()
        dt = clock.tick(30) / 1000


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()

另一种选择是在程序启动时将图块粘贴到大背景表面上。这样你就不会出现差距,这也会提高性能。

【讨论】:

  • 非常感谢您的回答!在一个大背景上使用 blitting 的问题是瓷砖是动画的。如果我正确理解 PyGame,我将不得不“双重 blit”,即我先在表面上 blit 瓷砖,然后在屏幕上粘贴表面。这实际上不会更消耗还是我在这里误解了一些东西?顺便说一句,我现在将使用“相机”方法。像这样,我移动相机而不是瓷砖,只相对于相机来移动它们。
  • 对于动画图块,您可能无法将解决方案与背景图像一起使用。您究竟是如何为图块设置动画的?
  • 我已经编辑了答案,因为仍然存在差距。您需要将panning(顺便说一句,这是一个非常基本的相机)转换为整数,然后再将其添加到平铺位置。
  • 非常感谢您的努力!我的相机方法实际上与您在此处显示的非常相似。尽管其他人也给了我重要的见解,但我将此作为公认的答案。我在您的代码中注意到一个细节:您使用 fps cap 和 dt?这是比只使用 dt 更好的方法吗?
  • 如果您不调用clock.tick,游戏将以您的计算机可以达到的最大帧速率运行,因此通常应该每帧调用一次。由于它还返回增量时间,我们可以直接使用它而不是自己计算它。顺便说一句,如果你将速度乘以dt,那么你就有一个“可变时间步长”,这对在线多人游戏不利(我认为这对单人游戏来说没问题)。这是一个有趣的链接:gameprogrammingpatterns.com/game-loop.html
【解决方案2】:

我认为问题是float 不能保留每个值,而我们有0.1 + 0.2 != 0.3 的情况,它可以在预期结果和获得的结果之间产生差异。

您只能使用整数值来更改所有图块的位置,即。

tile.rect.x = int(xmove)
tile.rect.y = int(ymove)

这种方式你只能丢失小的值(小于一个像素),但如果你累积许多小值,那么你可以得到几个像素。

因此您可以尝试累积浮点值和整数值之间的差异。

我无法检查这是否正常工作,但我会做这样的事情

def moveTiles(self):

    # add moves to already cumulated differences betweeen floats and integers
    self.xcululated += self.parent.dt * self.panning[0] * self.panning_speed
    self.ycululated += self.parent.dt * self.panning[1] * self.panning_speed

    # get only integer values
    xmove = int(self.xcululated)
    ymove = int(self.ycululated)

    # remove integers from floats
    self.xcululated += xmove
    self.ycululated += ymove

    for tile in self.game_map:
        # use the same integer values for all tiles
        tile.rect.x += xmove
        tile.rect.y += tmove

【讨论】:

  • 非常感谢您的回答。我决定采用相机的方法。我没有移动瓷砖,而是相对于相机位置对它们进行 blit。像这样我没有差距。
【解决方案3】:

我认为主要问题是您的游戏资产没有填充

在您的资产中添加填充非常重要,尤其是当您使用相机和浮点值时。

有时在使用相机时,浮点值无法提供所需的精度,并可能导致纹理渗色或您在游戏中不时看到的线条。

解决方案是为您的资产添加填充。

我这样做的方法如下:

  1. 下载并安装 GIMP

  2. 安装一个可以从here添加填充的插件。

  3. 使用 GIMP 中的插件加载每个资产并添加填充。

无需更改任何资产的名称。只需添加填充并覆盖即可。

我希望这对您有所帮助,如果您有任何其他问题,请随时在下面发表评论!

【讨论】:

  • 感谢您的回答。我通过使用简单的相机移动地图解决了这个问题。到目前为止,这工作得很好。然而,给我的瓷砖填充是我追求了一段时间的另一个想法。我也去看看。
  • 是的,如果将来需要,添加填充将允许相机进行更复杂的移动,请务必在未来考虑!
  • 谢谢,我去看看!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 2014-10-23
  • 1970-01-01
相关资源
最近更新 更多