Search code examples
pythonpython-3.xeventspygamepygame-surface

Image position not updating / updates slower while moving mouse


(trying to make a whack-a-mole game)Whenever I move the mouse, the position of the image of the mole seems to move 3-5x slower than when I'm not moving the mouse, and I'm not sure what's causing it since the position should update based on how much time has passed.

The screen for the game is 500x500 pixels, the images are 50x50 pixels and there's a 10x10 array that acts as a map to decide where the moles are allowed to appear

The code:

  1. get a random position from a 10x10 map

  2. update the position of the mole picture by one pixel every 30 ticks

  3. get the position of the mouse (one the screen of 500x500 pixels)

  4. get the position of the block the mole is supposed to go (on the 10x10 map)

  5. order in which the images are drawn on the screen:

    • the map

    • the hammer while moving

    • the block above the mole

    • the mole (goes up by 1 pixel)

    • the block at the original position of the mole

    • the hammer while not moving

the issue is, the mole goes up much slower when I'm moving the mouse and I'm not sure what's the problem. I also used print statements to check.

    def moleGoUp(self):
        nbPixel = 0
        #returns a random position
        initialPos = self.returnRandPosition()
        while nbPixel < 50:
            tickCounter = pygame.time.get_ticks() % 30
            if tickCounter == 0:
                nbPixel += 1
            #gets position of mouse
            mousePos = pygame.mouse.get_pos()
            #blits the background block that the mole is supposed to go to
            blockAbovePos = [initialPos[1] * 50, initialPos[0] * 50 - 50]

            #blits the mole at position (goes up by one pixel every 20 ticks)
            newPos = [initialPos[1] * 50, (initialPos[0]*50 - nbPixel)]
            initPosToBlit = [initialPos[1] * 50, initialPos[0] * 50]
            for event in pygame.event.get():
                mousePos = pygame.mouse.get_pos()
                if event.type == pygame.QUIT:
                    sys.exit()
                #draws the map
                self.drawMap()
                # blits the hammer
                display_surf.blit(imagePlayer, tuple(mousePos))
                # counts how many ticks has passed
                tickCounter = pygame.time.get_ticks() % 30
                print("in event loop")

            display_surf.blit(imageWall, tuple(blockAbovePos))
            display_surf.blit(imageTarget, tuple(newPos))
            #blits the background at the original position of the mole
            display_surf.blit(imageWall,tuple(initPosToBlit))
            #blits the hammer
            display_surf.blit(imagePlayer, tuple(mousePos))
            print("out of event loop")

            #blits the background over the mole
            if nbPixel == 50:
                display_surf.blit(imageWall, (initialPos[1]*50, initialPos[0]*50 - nbPixel))
            pygame.display.update()

output from the print:

in event loop
out of event loop
in event loop
out of event loop
in event loop
out of event loop
in event loop
out of event loop
in event loop
out of event loop


Solution

  • The performance drop is a caused because you are calling self.drawMap() in the event loop. The event loop is called once per event. Multiple events per frame can occur, especially when the mouse is moved.
    I recommend creating the map only when needed. Render the map into a pygame.Surface object and blit the map Surface onto the display in each frame. When the map has changed then recreate the map Surface.

    Create a "draw" method that renders on a target Surface rather than directly on the display Surface:

    def drawMap(self, traget_surf):
        # draw on traget_surf
        # [...]
    

    Add a variable map_surf and map_changed = True. Render the map in the application loop if map_changed is set and set map_changed == False. blit the map_surf Surface to the display in each frame. Whenever the map needs to be changed, it is sufficient to set map_changed = True:

    map_surf = pygame.Surface(display_surf.get_size())
    map_changed = True
    
    while nbPixel < 50:
    
        # [...]
    
        if map_changed:
            self.drawMap(map_surf)
            map_changed = False
    
    
        # [...]
    
        display_surf.blit(map_surf, (0, 0))
    
        display_surf.blit(imageWall, tuple(blockAbovePos))
        display_surf.blit(imageTarget, tuple(newPos))
        display_surf.blit(imageWall,tuple(initPosToBlit))
        display_surf.blit(imagePlayer, tuple(mousePos))