0

I am trying to have a pixel perfect collision with my pong object and my rotated paddle object. However, I realized that the pong ball is still hitting the rectangle of the rotated paddle object. As you can see, the ball is hitting the rect of the paddle, instead of the actual paddle

Here is my pong class with defined collision detection. Please check the checkCollision() function because I use the pygame.sprite.colliderect() function.

class Pong(pygame.sprite.Sprite):

def __init__(self, screensize):

    pygame.sprite.Sprite.__init__(self)

    self.screensize = screensize

    self.centerx = screensize[0] // 2
    self.centery = screensize[1] // 2
    self.radius = 25
    self.rect = pygame.Rect(self.centerx-self.radius,
                            self.centery-self.radius,
                            self.radius*2, self.radius*2)

    self.image = pygame.image.load("pokeball.png").convert()
    self.colorkey = self.image.get_at((0,0))
    self.image = pygame.transform.scale(self.image, (self.radius, self.radius))

    #Create the mask
    self.mask = pygame.mask.from_surface(self.image)

    self.color = (100,100,255)
    self.direction = [-2,1]

    self.speedx = 2
    self.speedy = 3

    self.hit_edge_left = False
    self.hit_edge_right = False

def checkCollision(self, player_paddle, ai_paddle):

    col = pygame.sprite.collide_rect(self, player_paddle)

    return col 

def collisionFormula(self, player_paddle, ai_paddle):

    if self.checkCollision(self, player_paddle):
        relative_IntersectionY = player_paddle.centery - self.rect.centery
        normal_IntersectionY = relative_IntersectionY / (player_paddle.height /2)
        bounce_angle = normal_IntersectionY * (5*math.pi/ 12 )


        self.direction[0] = math.cos(bounce_angle)
        self.direction[1] = -1 * math.sin(bounce_angle)



def update_ball_position(self):
    #update the position of the ball 
    self.centerx += self.direction[0]*self.speedx
    self.centery += self.direction[1]*self.speedy
    self.rect.center = (self.centerx, self.centery)


def reset_ball(self):
    if self.rect.right >= self.screensize[0]-1:
        self.hit_edge_right = True
        self.__init__(self.screensize)
    elif self.rect.left <= 0:
        self.hit_edge_left = True
        self.__init__(self.screensize)

def collision_checks(self, player_paddle, ai_paddle):

    #if the ball hits the top or bottom of the screen, change the y direction
    if self.rect.top <= 0 or self.rect.bottom >= self.screensize[1] - 1:
        self.direction[1] *= -1

    #if the pong hits the paddles, change how the pong ball moves 
    if self.rect.colliderect(player_paddle.rect) or self.rect.colliderect(ai_paddle.rect):
        self.collisionFormula(player_paddle, ai_paddle)

def update(self, player_paddle, ai_paddle):

    self.update_ball_position()
    self.reset_ball()
    self.collision_checks(player_paddle, ai_paddle)


def render(self, screen):
    screen.blit(self.image, (self.centerx, self.centery))<!-- Paste the part of the code that shows the problem. 

Here is my PlayerPaddle class with defined rotate functions.

class PlayerPaddle(pygame.sprite.Sprite):
def __init__(self, screensize):

    pygame.sprite.Sprite.__init__(self)
    self.screensize = screensize

    self.centerx = 50
    self.centery = screensize[1]//2

    self.height = 100
    self.width = 20

    self.imageMaster = pygame.image.load("naruto.png").convert()
    self.colorkey = self.imageMaster.get_at((0,0))
    self.imageMaster = pygame.transform.scale(self.imageMaster, (100, 200))
    self.image = self.imageMaster

    #mask
    #self.maskMaster = pygame.mask.from_surface(self.image)
    #self.mask = self.maskMaster 

    self.rect = self.image.get_rect()
    self.rect.center = (screensize[0]//2, screensize[1]//2)
    self.dir = 0 


    self.color = (100,255,100)
    self.speed = 3
    self.direction = 0

    self.update()

def update(self):
    #Rotate functions
    oldCenter = self.rect.center
    self.image = pygame.transform.rotate(self.imageMaster, self.dir).convert()
    self.image.set_colorkey(self.colorkey)
    self.rect = self.image.get_rect()
    #self.rect.center = oldCenter
    self.mask = pygame.mask.from_surface(self.image)


    self.centery += self.direction*self.speed

    self.rect.center = (self.centerx, self.centery)
    if self.rect.top < 0:
        self.rect.top = 0

    if self.rect.bottom > self.screensize[1]-1:
        self.rect.bottom = self.screensize[1]-1

def render(self, screen):

    screen.blit(self.image, (self.rect.x, self.rect.y))


def turnLeft(self):
    self.dir += 45
    if self.dir > 360:
        self.dir = 45

def turnRight(self):
    self.dir -= 45
    if self.dir < 0:
        self.dir = 315

And I write this within the while loop of my main().

        if pygame.sprite.spritecollide(pong_sprite, paddleGroup, False, pygame.sprite.collide_mask):
        print("Collided")

I'm not sure why this is occurring but I'd like to know how to get a pixel perfect collision so I can avoid the issue from the image.

EDIT: I changed the update function of my PlayerPaddle class.

def update(self):
    #Rotate functions
    oldCenter = self.rect.center
    self.image = pygame.transform.rotate(self.imageMaster, self.dir)
    self.rect = self.image.get_rect()
    self.rect.center = oldCenter
    self.mask = pygame.mask.from_surface(self.image)



    self.centery += self.direction*self.speed

    self.rect.center = (self.centerx, self.centery)
    if self.rect.top < 0:
        self.rect.top = 0

    if self.rect.bottom > self.screensize[1]-1:
        self.rect.bottom = self.screensize[1]-1

And here is my main():

def main():
    pygame.init()

    screensize = (640,700)
    screen = pygame.display.set_mode(screensize)

    background = pygame.Surface(screen.get_size())
    background.fill((0, 255, 0))

    clock = pygame.time.Clock()

    pong_sprite = Pong(screensize)
    player_paddle = PlayerPaddle(screensize)
    ai_paddle = AIPaddle(screensize)


    paddleGroup = pygame.sprite.Group()
    paddleGroup.add(player_paddle)
    pongGroup = pygame.sprite.Group()
    pongGroup.add(pong_sprite)


    running = True

    keymap = {pygame.K_u: False}

    while running:
        #fps limiting/reporting phase
        clock.tick(64)
        screen.fill((100,100,100))
        #event handling phase
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False

            if event.type == KEYDOWN:
                if event.key in keymap:
                    keymap[event.key] = True
                if event.key == K_UP:
                    player_paddle.direction = -1
                elif event.key == K_DOWN:
                    player_paddle.direction = 1
                elif event.key == K_LEFT:
                    player_paddle.turnLeft()
                elif event.key == K_RIGHT:
                    player_paddle.turnRight()


            if event.type == KEYUP:

                if event.key in keymap:
                    keymap[event.key] = False
                if event.key == K_UP and player_paddle.direction == -1:
                    player_paddle.direction = 0
                elif event.key == K_DOWN and player_paddle.direction == 1:
                    player_paddle.direction = 0

        #object updating phase
        ai_paddle.update(pong_sprite, player_paddle)
        #player_paddle.update()
        #pong.update(player_paddle, ai_paddle)

        if pygame.sprite.spritecollide(pong_sprite, paddleGroup, False, pygame.sprite.collide_mask):
            print("Collided")


        if pong_sprite.hit_edge_left:
            print("You won")
            #running = False
        elif pong_sprite.hit_edge_right:
            print("You Lose")
            #running = False

        #rendering phase
        ai_paddle.render(screen)
        player_paddle.render(screen)
        #pong.render(screen)

        paddleGroup.clear(screen, background)
        paddleGroup.update()
        paddleGroup.draw(screen)

        pongGroup.clear(screen,background)
        pongGroup.update(player_paddle, ai_paddle)
        pongGroup.draw(screen)

        pygame.display.flip()

    pygame.quit()

Here is my imageenter image description here: This is the naruto.jpg image

And here is the pokeball.jpg enter image description here

turtlefish12
  • 241
  • 3
  • 12
  • after you rotate paddle you have to rotate mask too - it will not rotate mask automatically – furas Dec 04 '17 at 19:06
  • BTW: for testing you could display mask instead of image. – furas Dec 04 '17 at 19:08
  • How would I do that? in my keypressed, if I press the right key for example, I tried calling player_paddle.mask.turnLeft()? However, this isn't working for me. – turtlefish12 Dec 04 '17 at 21:30
  • you have to generate mask from current image after rotating. `self.mask = pygame.mask.from_surface(self.image)` or use `self.mask = pygame.transform.rotate(self.maskMaster)` if you have `self.maskMaster` – furas Dec 04 '17 at 21:37
  • However, I got this error. argument 1 must be pygame.Surface, not pygame.mask.Mask if I tried running self.mask = pygame.transform.rotate(self.maskMaster, self.dir) – turtlefish12 Dec 04 '17 at 21:46
  • then use `self.mask = pygame.mask.from_surface(self.image)` - it is more natural. If you use only few angles to rotate paddle then you could create all rotated images and masks at start - and keep in dictionary `self.all_masks[45] = ...` and later `self.mask = self.all_masks[self.dir]` – furas Dec 04 '17 at 21:50
  • I'm going to have many directions so I prefer not to do that. I made edits to my code for you to see. I'm still hitting the rect of the original rectangle instead of the rotated one. – turtlefish12 Dec 04 '17 at 21:57
  • I see only one explanation - image is not ideal and it has pixels on border after scaling or rotating and mask use this pixel to check collision. And probably this is why images in games are created manually and game doesn't have to scale and rotate them. – furas Dec 04 '17 at 23:25
  • Does this mean my code is correct? Also, do you suggest I manually make my image instead now? – turtlefish12 Dec 04 '17 at 23:47
  • I made [example with mask](https://github.com/furas/python-examples/tree/master/pygame/mask-collision) and it works even after rescaling and rotateting but I use surface filled with color, not external image. I use `olist` to draw mask border as black line so I see how it looks like. – furas Dec 05 '17 at 00:06
  • Your code seems OK. I would have to run it with your images to say something more. – furas Dec 05 '17 at 00:08
  • I edited my response to include the link to the image. Thank you for the example though! – turtlefish12 Dec 05 '17 at 01:17
  • I need your full code to see how it works with your image. – furas Dec 05 '17 at 01:38
  • I have added my main() which creates the player_paddle and pong sprites. – turtlefish12 Dec 05 '17 at 01:57
  • I create my example with your image and it works correctly if I use `convert()` instead of `convert_alpha()` and `set_colorkey()`. See: [example with image](https://github.com/furas/python-examples/tree/master/pygame/mask-collision) – furas Dec 05 '17 at 02:02
  • I edited my code and my pong object collides properly at the bottom somtimes but doesn't collide properly most of the times. I did the convert() and colorkey() methods. – turtlefish12 Dec 05 '17 at 02:28

0 Answers0