PyGame: 平移平铺地图导致间隙
PyGame: Panning tiled map causes gaps
我在 Python 3.4.3 (Win8.1) 上使用 PyGame 1.9.2。
我的游戏 window 包含一张使用 48x48 像素方块的方块地图。游戏框架使用以下游戏框架方法根据玩家输入移动方块 class:
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 值然后 "int'ed" 用作 rect 和 pos 的 x-/y-values(后者用于 blitting)。
我将完全相同的数量添加到每个图块的 x/y 值中,然后我对它们使用 "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
不能保留每个值,我们遇到的情况是 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
这是一个应该效果更好的解决方案。只需在玩家移动时将 velocity
添加到 panning
向量中,不要移动方块。然后将平移添加到图块的 rect.topleft
位置,当它们被块化以获得正确的位置时。您必须先将平移转换为整数(创建另一个矢量,在示例中称为 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()
另一种选择是在程序启动时将图块块化到大背景表面上。然后你就不会得到差距,这也提高了性能。
我认为主要问题是您的游戏资产没有 padding。
填充在您的资产中非常重要,尤其是当您使用相机和浮点值时。
有时,当使用相机时,浮点值无法提供所需的准确性,并可能导致纹理渗色 或您在游戏中不时看到的线条。
解决方案是为您的资产添加填充。
我的做法是这样的:
下载并安装 GIMP
安装一个可以从 here.
添加填充的插件
加载每个资产并使用 GIMP 中的插件添加填充。
无需更改任何资产的名称。只需添加填充并覆盖即可。
希望这对您有所帮助,如果您有任何其他问题,请随时 post 在下方发表评论!
我在 Python 3.4.3 (Win8.1) 上使用 PyGame 1.9.2。
我的游戏 window 包含一张使用 48x48 像素方块的方块地图。游戏框架使用以下游戏框架方法根据玩家输入移动方块 class:
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 值然后 "int'ed" 用作 rect 和 pos 的 x-/y-values(后者用于 blitting)。
我将完全相同的数量添加到每个图块的 x/y 值中,然后我对它们使用 "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
不能保留每个值,我们遇到的情况是 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
这是一个应该效果更好的解决方案。只需在玩家移动时将 velocity
添加到 panning
向量中,不要移动方块。然后将平移添加到图块的 rect.topleft
位置,当它们被块化以获得正确的位置时。您必须先将平移转换为整数(创建另一个矢量,在示例中称为 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()
另一种选择是在程序启动时将图块块化到大背景表面上。然后你就不会得到差距,这也提高了性能。
我认为主要问题是您的游戏资产没有 padding。
填充在您的资产中非常重要,尤其是当您使用相机和浮点值时。
有时,当使用相机时,浮点值无法提供所需的准确性,并可能导致纹理渗色 或您在游戏中不时看到的线条。
解决方案是为您的资产添加填充。
我的做法是这样的:
下载并安装 GIMP
安装一个可以从 here.
添加填充的插件
加载每个资产并使用 GIMP 中的插件添加填充。
无需更改任何资产的名称。只需添加填充并覆盖即可。
希望这对您有所帮助,如果您有任何其他问题,请随时 post 在下方发表评论!