14thDovePin
14thDovePin

Reputation: 43

Pygame drawn circle at mouse position has location mismatch and a weird display glitch

So I've been working on an ant simlulation like project and I've encountered this issue and replicated it here. In essence what I did is draw a circle every surface update at the location of the cursor using pygame.mouse.get_pos().

But somehow I'm getting this issue where the location of the circle is way off the cursur by growing intervals when you move the cursor away from the top-left side of the window. There's also this graphical glitch where it seems that the cursor brings along a fixed surface of the window itself that contains the surface update of the objects behind the circle and the circle itself moving inside it. It also blocks the object's actual surface behind this window as you can see from the link below.

https://i.gyazo.com/c8b268cf072d040bb4000764d4955cd7.mp4

I've looked around online and so far I'm not even close to solving this issue. I'm not sure if it's a surface layering problem, a problem with my logic or if it's a glitch inside pygame. In any case here's the code below to replicate the problem.

"""

import sys
from random import randint, uniform

import pygame
from pygame.sprite import Sprite


class MainGame:
    """Main pygame class."""

    def __init__(self):
        """Initialize game assets."""
        pygame.init()
        self.resolution = (1280, 720)
        self.screen = pygame.display.set_mode(
            self.resolution
            )
        self.screen_rect = self.screen.get_rect()
        pygame.display.set_caption('Circle Test')

        # Insert game objects below this line.
        self.ccirc = CursorCircle(self)
        self.sample1 = pygame.sprite.Group()
        self.sample2 = pygame.sprite.Group()

        for _ in range(10):
            self.sample1.add(SampleObject(self))
            sample = SampleObject(self)
            sample.front = 0
            self.sample2.add(sample)

    def start(self):
        """Main game loop."""
        while True:
            self._keyboard_basic()
            # Insert object update below this line.
            self.ccirc.update()

            self._update_screen()


    def _update_screen(self):
        """Game screen update."""
        self.screen.fill((77, 77, 77))
        # Insert surface update below this line. ###
        for i in self.sample2:
            i.blitme()
        self.ccirc.blitme()
        for i in self.sample1:
            i.blitme()

        pygame.display.flip()

    def _keyboard_basic(self):
        """Response to keypress and mouse events"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._event_keydown(event)
            elif event.type == pygame.KEYUP:
                self._event_keyup(event)

    def _event_keydown(self, event):
        """Keydown events."""
        if event.key == pygame.K_UP:
            print('Pressed ↑')
        elif event.key == pygame.K_DOWN:
            print('Pressed ↓')
        elif event.key == pygame.K_LEFT:
            print('Pressed ←')
        elif event.key == pygame.K_RIGHT:
            print('Pressed →')
        elif event.key == pygame.K_q:
            sys.exit()

    def _event_keyup(self, event):
        """Keyup events."""
        pass


class SampleObject(Sprite):
    """Sample object on screen."""

    def __init__(self, main_game):
        """Initialize class assets."""
        super().__init__()
        self.screen = main_game.screen
        self.screen_rect = self.screen.get_rect()

        self.image = pygame.image.load('pointer.png')
        self.image = pygame.transform.rotate(self.image, randint(1, 360))
        self.image1 = pygame.image.load('pointer-.png')
        self.image1 = pygame.transform.rotate(self.image1, randint(1, 360))
        self.rect = self.image.get_rect()

        self.rect.centerx = randint(1, main_game.resolution[0])
        self.rect.centery = randint(1, main_game.resolution[1])

        self.front = 1

    def blitme(self):
        """Surface update."""
        if self.front:
            self.screen.blit(self.image, self.rect)
        else:
            self.screen.blit(self.image1, self.rect)


class CursorCircle(Sprite):
    """Circle that follows the cursor."""

    def __init__(self, main_game):
        """Initialize class assets."""
        super().__init__()
        self.screen = main_game.screen
        self.screen_rect = self.screen.get_rect()

        self.color = (0, 102, 204)
        # self.center = [self.screen_rect.centerx, self.screen_rect.centery]
        # self.center = [0,0]
        self.radius = 100

    def update(self):
        """Loop update."""
        self.center = pygame.mouse.get_pos()

    def blitme(self):
        """Surface update."""
        self.circle = pygame.draw.circle(self.screen, self.color, self.center, self.radius)
        self.screen.blit(self.screen, self.circle)


if __name__ == '__main__':
    game = MainGame()
    game.start()

"""

Versions *Python 3.9.1 *pygame 2.0.1

Upvotes: 1

Views: 197

Answers (1)

Rabbid76
Rabbid76

Reputation: 210968

pygame.draw.circel draws a circle on the screen. This function doesn't generate something like a circle object. See how to draw shapes.
self.screen.blit(self.screen, self.circle) doesn't do what you'd expect, however. It copies the entire screen onto itself in the place of the circle.

Remove this instruction:

class CursorCircle(Sprite):
    # [...]

    def blitme(self):
        """Surface update."""
        pygame.draw.circle(self.screen, self.color, self.center, self.radius)

        #self.screen.blit(self.screen, self.circle) <--- DELETE

Alternatively you can draw the circle on a pygame.Surface object with a per pixel alpha format (pygame.SRCALPHA) and blit the Surface on the screen:

class CursorCircle(Sprite):
    """Circle that follows the cursor."""

    def __init__(self, main_game):
        """Initialize class assets."""
        super().__init__()
        self.screen = main_game.screen
        
        self.color = (0, 102, 204)
        self.radius = 100

        self.circle = pygame.Surface((radius*2, radius*2), pygame.SRCALPHA)
        pygame.draw.circle(self.circle, self.color, (radius, radius), radius)
        self.rect = self.circle.get_rect()

    def update(self):
        """Loop update."""
        self.rect.center = pygame.mouse.get_pos()

    def blitme(self):
        """Surface update."""
        self.screen.blit(self.circle, self.rect)

Upvotes: 2

Related Questions