I'm using PyGame 1.9.2 on Python 3.4.3 (Win8.1).
My game window consists of a tiled map using 48x48px tiles. The game's framework moves the tiles according to player input using the following method of the game's framwork 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)
While "self.panning_speed" is pretty self-explanatory, self.panning is a list containing two values for x and y panning (e.g. [0, 0] = no panning, [-1, 1] = panning down-left, etc.).
The tile.x/tile.y values are then "int'ed" to be used as x-/y-values of the rect and the pos (the latter used for blitting).
I add the exact same amount to every single tile's x/y value, then I use "int" on them which, to my knowledge, always floors the float down to the next lower int. So technically seen the relative change to every single tile's x/y value is the exactly same as to any other tile's x/y value.
Yet: I still get gaps between rows or columns from time to time! Those gaps appear irregularly but are very visible and when you stop panning while a gap is visible that gap will stay until you pan again.
See this image for an example: See this image for an example (black line in the red square is the gap; gold and pink are the tiles)
Where in my code can these gaps occur?
Here's a solution that should work better. Just add the velocity
to the panning
vector when the player is moving and never move the tiles. The panning is then added to the rect.topleft
position of the tiles when they're blitted to get the correct position. You have to convert the panning to integers first (create another vector, called offset
in the example) before you add it to the tile positions or you'll still get gaps.
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()
Another alternative would be to blit the tiles onto a big background surface at program start. Then you won't get gaps and that also improves the performance.