I didn't see any other questions like this that had an answer so I thought I'd ask in hopes this helps someone else too. I have a game I'm in the beginning stages of working on, and I've gotten to the point where I have a sprite on the screen that I am animating to make it look like it's walking.
The issue is when I move the images too quickly across the screen, the images start to get blurry. I've done a little googling, but I'm still not quite sure what's going on. See here for a video of it.
When I turn the SPEED variable up, the walking gets blurry, but when it's turned down to 10 or below, it's no longer blurry. From what I could tell this is maybe frame ghosting, but there are other phenomena it could be as well, and I'm too new to game development to know how to solve it. I have added and taken away clock.tick() to see if my sprite frame limiting function may be the cause, but it seems to be the same.
You can take a look at my repo here or take a look at the code below. p.s. I know I need to move the sprites into a sheet, and load them using list comprehension or something, just haven't gotten around to it yet.
# Import the pygame module import pygame from common_utilities import * from time import time # Import pygame.locals for easier access to key coordinates # Updated to conform to flake8 and black standards from pygame.locals import ( K_w, K_s, K_a, K_d, K_UP, K_DOWN, K_ESCAPE, KEYDOWN, QUIT, ) BLUE = (0,0,255) PURPLE = (153,50,204) GREEN = (0,128,0) MAP = [[BLUE, GREEN, BLUE], [GREEN, PURPLE, GREEN], [PURPLE, GREEN, BLUE]] DEBUG = False # Define constants for the screen width and height SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 SPEED = 15 # Setup the clock for a decent framerate clock = pygame.time.Clock() #Constants SCREENS = 3 SPRITE_SIZE = (150,198) SCREEN_SIZE = (SCREEN_WIDTH,SCREEN_HEIGHT) worldMaxX = SCREEN_SIZE[0]*SCREENS worldMaxY = SCREEN_SIZE[1]*SCREENS class FrameRate(): def __init__(self,dT): self.previousTime = 0 self.currentTime = 0 self.elapsed_time = 0 self.dT = dT def integrate_state(self,callback,*args): ''' ''' self.callback = callback self.currentTime = int(time()*1000) self.elapsed_time += self.currentTime - self.previousTime if (DEBUG): print('self.elapsed_time:',self.elapsed_time) self.previousTime = self.currentTime if (self.elapsed_time >= int(self.dT)): if (DEBUG): print('self.elapsed_time:',self.elapsed_time) #Call function after elapsed time self.callback(*args) #Reset elapsed time self.elapsed_time = 0 class Camera(): def __init__(self): self.worldX = 1000 self.worldY = 1000 self.cameraX = SCREEN_SIZE[0] self.cameraY = SCREEN_SIZE[1] self.screen = [2, 2] def worldCoordinates(self,screenX, screenY): worldX = self.screen[0]*SCREEN_SIZE[0] + screenX worldY = self.screen[1]*SCREEN_SIZE[1] + screenY return (worldX, worldY) def screenCoordinates(self,worldX, worldY): screenX = worldX - self.screen[0]*SCREEN_SIZE[0] screenY = worldY - self.screen[1]*SCREEN_SIZE[1] return (screenX,screenY) def updateCamera(self,position): #Move player to other side of screen when crossing screens if (position.x < -SPRITE_SIZE[0] and self.screen[0] != 0): position.x = SCREEN_SIZE[0] if (self.screen[0] >= 1): self.screen[0] -= 1 elif (position.x > SCREEN_SIZE[0] and self.screen[0] != 2): position.x = 0 if (self.screen[0] < SCREENS-1): self.screen[0] += 1 if (position.y < -25 and self.screen[1] != 0): position.y += SCREEN_SIZE[1] if (self.screen[1] >= 1): self.screen[1] -= 1 elif (position.y > SCREEN_SIZE[1] and self.screen[1] != 2): position.y = 0 if (self.screen[1] < SCREENS-1): self.screen[1] += 1 #Prevent player from leaving edge of map if (position.x < 0 and self.screen[0] == 0): position.x = 0 elif (position.x > SCREEN_SIZE[0]-SPRITE_SIZE[0] and self.screen[0] == 2): position.x = SCREEN_SIZE[0]-SPRITE_SIZE[0] if (position.y < 0 and self.screen[1] == 0): position.y = 0 if (position.y > SCREEN_SIZE[1]-SPRITE_SIZE[1] and self.screen[1] == 2): position.y = SCREEN_SIZE[1]-SPRITE_SIZE[1] if (DEBUG): print('self.screen:',self.screen) print('self.worldX:',self.worldX) print('self.worldY:',self.worldY) print('self.rect.x:',position.x) print('self.rect.y:',position.y) return self.screen class Player(Camera): def __init__(self): super(Player,self).__init__() self.orientation = 'Right' self.images = [] self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk1.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk2.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk3.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk4.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk5.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk6.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk7.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk8.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk9.png')) self.images.append(pygame.image.load('C:/Games/Random_World/Assets/Sprites/pumpking_walk/walk10.png')) self.index = 0 self.image = self.images[self.index] self.position = struct(x=0,y=0) def update(self, pressed_keys): global SPEED if (pressed_keys[K_UP]): SPEED += 1 print('SPEED:',SPEED,flush=True) elif (pressed_keys[K_DOWN]): SPEED -= 1 print('SPEED:',SPEED,flush=True) if (SPEED <= 0): SPEED = 0 if self.index > len(self.images)-1: self.index = 0 # Move the SPRITE_SIZE based on user keypresses if pressed_keys[K_w]: self.worldX,self.worldY = self.worldCoordinates(self.position.x,self.position.y) self.worldY -= SPEED self.orientation = 'Right' self.image = self.images[self.index] self.index += 1 self.position.x,self.position.y = self.screenCoordinates(self.worldX,self.worldY) if self.index > len(self.images)-1: self.index = 0 if pressed_keys[K_s]: self.worldX,self.worldY = self.worldCoordinates(self.position.x,self.position.y) self.worldY += SPEED self.orientation = 'Left' self.image = self.images[self.index] self.index += 1 self.position.x,self.position.y = self.screenCoordinates(self.worldX,self.worldY) if self.index > len(self.images)-1: self.index = 0 if pressed_keys[K_a]: self.worldX,self.worldY = self.worldCoordinates(self.position.x,self.position.y) self.worldX -= SPEED self.orientation = 'Left' self.image = self.images[self.index] self.index += 1 self.position.x,self.position.y = self.screenCoordinates(self.worldX,self.worldY) if self.index > len(self.images)-1: self.index = 0 if pressed_keys[K_d]: self.worldX,self.worldY = self.worldCoordinates(self.position.x,self.position.y) self.worldX += SPEED self.orientation = 'Right' self.image = self.images[self.index] self.index += 1 self.position.x,self.position.y = self.screenCoordinates(self.worldX,self.worldY) if ((not pressed_keys[K_a]) and (not pressed_keys[K_s]) and (not pressed_keys[K_w]) and (not pressed_keys[K_d])): self.image = self.images[0] def render(self,surface,position): if self.orientation == "Right": screen.blit(surface, (position.x,position.y)) elif self.orientation == "Left": screen.blit(pygame.transform.flip(surface, True, False), (position.x,position.y)) class Map(): def __init__(self,world): self.map = world self.map_view = PURPLE def update(self,coordinates): self.map_view = self.map[coordinates[0]][coordinates[1]] if (DEBUG): print('self.map_view:',self.map_view) def render(self,screen): screen.fill(self.map_view) # Initialize pygame pygame.init() # Create the screen object # The size is determined by the constant SCREEN_SIZE[0] and SCREEN_SIZE[1] screen = pygame.display.set_mode((SCREEN_SIZE[0], SCREEN_SIZE[1])) # Instantiate player. Right now, this is just a rectangle. player = Player() #Instantiate map, takes list of lists map to be rendered world = Map(MAP) # Variable to keep the main loop running running = True frame = FrameRate(100) # Main loop while running: # for loop through the event queue for event in pygame.event.get(): # Check for KEYDOWN event if event.type == KEYDOWN: # If the Esc key is pressed, then exit the main loop if event.key == K_ESCAPE: running = False # Check for QUIT event. If QUIT, then set running to false. elif event.type == QUIT: running = False # Get all the keys currently pressed pressed_keys = pygame.key.get_pressed() frame.integrate_state(player.update,pressed_keys) worldScreen = player.updateCamera(player.position) world.update(worldScreen) world.render(screen) # Draw the player on the screen player.render(player.image,player.position) # Update the display pygame.display.update()