Theo
Theo

Reputation: 31

Mask collision in Pygame

Im making a endless runner type game where the player flies a spaceship while trying to avoid obstacles. Im using mask collision detection between the ship and the obstacles. The player can also activate a laser which SHOULD destroy asteroids in its path. I am attempting to use the same method of collision detection for this but its not working. That may be because I have an animation for the laser which changes its image. How can I fix this issue?

######################
#
# Pls help
#
# Written by: Theo
# Date: December 2020 - March 2021
#
# Version 1: an endless runner game where the player
# controls a ship traveling through space, dodging obstacles


import pygame
import math
import time
import random
import RiftObstacles



global ship
global rot_ship
global shipX
global moonMask

pygame.init()
FPS = 100
clock = pygame.time.Clock()
size = pygame.display.Info()
WIDTH = 1000
HEIGHT = 1000
pygame.display.set_caption('Parallax Nebula')
screen = pygame.display.set_mode((WIDTH, HEIGHT))


########## Rift Variables
ship = pygame.image.load("Images/Rift/Ship.PNG")
ship = pygame.transform.scale(ship, (85, 125))
rot_ship = ship
shipX = 450
shipY = 700


A1 = pygame.image.load("Images/Rift/Asteroids/1.PNG")
A1 = pygame.transform.scale(A1, (100, 100))
A1 = pygame.Surface.convert_alpha(A1)

A2 = pygame.image.load("Images/Rift/Asteroids/2.PNG")
A2 = pygame.transform.scale(A2, (100, 100))
A2 = pygame.Surface.convert_alpha(A2)

A3 = pygame.image.load("Images/Rift/Asteroids/3.PNG")
A3 = pygame.transform.scale(A3, (100, 100))
A3 = pygame.Surface.convert_alpha(A3)

A4 = pygame.image.load("Images/Rift/Asteroids/4.PNG")
A4 = pygame.transform.scale(A4, (100, 100))
A4 = pygame.Surface.convert_alpha(A4)

A5 = pygame.image.load("Images/Rift/Asteroids/5.PNG")
A5 = pygame.transform.scale(A5, (100, 100))
A5 = pygame.Surface.convert_alpha(A5)

A6 = pygame.image.load("Images/Rift/Asteroids/6.PNG")
A6 = pygame.transform.scale(A6, (100, 100))
A6 = pygame.Surface.convert_alpha(A6)

A7 = pygame.image.load("Images/Rift/Asteroids/7.PNG")
A7 = pygame.transform.scale(A7, (100, 100))
A7 = pygame.Surface.convert_alpha(A7)

A8 = pygame.image.load("Images/Rift/Asteroids/8.PNG")
A8 = pygame.transform.scale(A8, (100, 100))
A8 = pygame.Surface.convert_alpha(A8)

A9 = pygame.image.load("Images/Rift/Asteroids/9.PNG")
A9 = pygame.transform.scale(A9, (100, 100))
A9 = pygame.Surface.convert_alpha(A9)

A10 = pygame.image.load("Images/Rift/Asteroids/10.PNG")
A10 = pygame.transform.scale(A10, (100, 100))
A10 = pygame.Surface.convert_alpha(A10)

A11 = pygame.image.load("Images/Rift/Asteroids/11.PNG")
A11 = pygame.transform.scale(A11, (100, 100))
A11 = pygame.Surface.convert_alpha(A11)

A12 = pygame.image.load("Images/Rift/Asteroids/12.PNG")
A12 = pygame.transform.scale(A12, (60, 100))
A12 = pygame.Surface.convert_alpha(A12)

F1 = pygame.image.load("Images/Rift/Fuel/1.PNG")
F1 = pygame.transform.scale(F1, (150, 150))

F2 = pygame.image.load("Images/Rift/Fuel/2.PNG")
F2 = pygame.transform.scale(F2, (150, 150))

F3 = pygame.image.load("Images/Rift/Fuel/3.PNG")
F3 = pygame.transform.scale(F3, (150, 150))

F4 = pygame.image.load("Images/Rift/Fuel/4.PNG")
F4 = pygame.transform.scale(F4, (150, 150))

F5 = pygame.image.load("Images/Rift/Fuel/5.PNG")
F5 = pygame.transform.scale(F5, (150, 150))

F6 = pygame.image.load("Images/Rift/Fuel/6.PNG")
F6 = pygame.transform.scale(F6, (150, 150))

comet = pygame.image.load("Images/Rift/Comet.PNG")
comet = pygame.transform.scale(comet, (80, 250))


moon = pygame.image.load("Images/Rift/Moon - WIP.PNG")
moon = pygame.transform.smoothscale(moon, (1200, 1200))
moon = pygame.Surface.convert_alpha(moon)
moonMask = pygame.mask.from_surface(moon)

angle = 0
lasers = []
stretch = []
for i in range(60):
    laserImg = pygame.image.load("Images/Rift/Laser/"+str(i+1)+".PNG")
    laserImg = pygame.Surface.convert_alpha(laserImg)
    stretchTemp = int(15*math.cos(math.pi*i/30))
    stretch.append(50 - stretchTemp)
    laserImg = pygame.transform.scale(laserImg, (50-stretchTemp, 1050))
    lasers.append(laserImg)



angle = 0

RiftBG = pygame.image.load("Images/Rift/Background/BG.PNG")
RiftBG = pygame.Surface.convert_alpha(RiftBG)
RiftBG = pygame.transform.smoothscale(RiftBG, (1000, 1000))





#Transparent comet warning
def warning(surface, color, rect):
    shape_surf = pygame.Surface(pygame.Rect(rect).size, pygame.SRCALPHA)
    pygame.draw.rect(shape_surf, color, shape_surf.get_rect())
    surface.blit(shape_surf, rect)
    
#Moving the ship 
def moveShip(shipX, X, angle):
    if (X >= 0 and shipX > 100) or (X <= 0 and shipX < 800):
        shipX -= X    
    else:
        shipX -= X
        angle -= (X*speed)/11
        
                
    rot_ship = pygame.transform.rotate(ship, angle)
    return(shipX, rot_ship, angle)

#Pulling ship towards the middle of the screen
def riftpull(shipX, angle, rot_ship):
    if shipX < 440:
        if angle > -25:
            angle -= 0.1 * speed
        else:
            angle += 0.1 * speed
        shipX, rot_ship, angle = moveShip(shipX, (angle/2.5), angle)
                
    elif shipX > 460:
        if angle < 25:
            angle += 0.1 * speed
        else:
            angle -= 0.1 * speed
        shipX, rot_ship, angle = moveShip(shipX, (angle/2.5), angle)
    
    elif 440 < shipX < 460:
        if abs(angle) > 1:
            angle = angle/1.2
        else:
            angle = 0
        shipX, rot_ship, angle = moveShip(shipX, (angle/2.5), angle)
    return shipX, angle, rot_ship  


#Ship Travel through the rift    
def rift():
    global ship
    global rot_ship
    global shipX
    global speed
    global moon
    global moonOutline
    global moonMask
    global distance
    speed = 5
    angle = 0
    laserOn = 2*FPS
    laserAnimation = int(FPS/30)
    laserFrame = 0
    distanceTimer = 0
    delay = FPS
    laserMode = False
    crash = False
    key = pygame.key.get_pressed()
    wallX = 470
    wallY = [-1000, -3000]
    
    
    rocks = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12]
    fuel = [F1, F2, F3, F4, F5, F6]
    numRock, rockX, rockY, numComet, cometX, cometY, numMoon, moonX, moonY, moonScale, numFuel, fuelX, fuelY = RiftObstacles.path()
    rockType = []
    rockAngle = []  
    rockAngle2 = []
    rockRot = []
    for i in range(numRock):
        rockType.append(random.choice(rocks))
        rockAngle.append(random.uniform(-0.2,0.2))
        rockAngle2.append(rockAngle[i])

    fuelType = []
    fuelAngle = []
    fuelAngle2 = []
    fuelRot = []
    for i in range(numFuel):
        fuelType.append(random.choice(fuel))
        fuelAngle.append(random.uniform(-0.2,0.2))
        fuelAngle2.append(fuelAngle[i])
        
    warningWidth = []
    for i in range(numComet):
        warningWidth.append(10)

    endRift = False
    while endRift == False:
        screen.blit(RiftBG, (0,0))
           
        #Displaying obstacles       
        for i in range(numRock):
            rockRot.append(pygame.transform.rotate(rockType[i], rockAngle2[i]))
            screen.blit(rockRot[i], (rockX[i], rockY[i]))

        for i in range(numComet):
            screen.blit(comet, (cometX[i], cometY[i]))
            if -2000 < cometY[i] < HEIGHT:
                warningHEIGHT = HEIGHT - cometY[i]
                cometWarning = pygame.Rect(cometX[i]+40 - warningWidth[i]/2, cometY[i]+100, warningWidth[i], warningHEIGHT)
                if warningWidth[i] < 78:
                    warningWidth[i] += 1.5
                warning(screen, (250, 200, 3, 127), (cometWarning))

        for i in range(numFuel):
            fuelRot.append(pygame.transform.rotate(fuelType[i], fuelAngle2[i]))
            screen.blit(fuelRot[i], (fuelX[i], fuelY[i]))
        

        if laserMode == True:
            laserOn -= 1
            if laserOn <= 0:
                laserMode = False

            rot_laser = pygame.transform.rotate(lasers[laserFrame], angle)
            laserAnimation -= 1
            if -1 < laserAnimation < 1:
                laserAnimation = FPS/30
                if laserFrame < 59:
                    laserFrame += 1
            laserRect = rot_laser.get_rect()
            if angle == 0:
                correction = 50*math.sin((angle*math.pi)/180)
                laserRect.midbottom = (shipX + 42 - correction, shipY+100)
                
            elif angle <= 1:
                correction = angle*9
                laserRect.midbottom = (shipX + 50 - correction, shipY+100)
            
            else:
                correction = angle*7
                laserRect.midbottom = (shipX + 30 - correction, shipY+100)
                
            screen.blit(rot_laser, laserRect)
                
        if laserMode == False:
            laserOn = 180
            laserFrame = 0
            
        for i in range(numMoon):
            moon = pygame.transform.smoothscale(moon, (int(100*moonScale[i]), int(100*moonScale[i])))
            screen.blit(moon, (moonX[i], moonY[i]))
            
    



        
        #Ship Movement                
        screen.blit(rot_ship, (int(shipX), shipY))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    key = 'left'
                elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    key = 'right'

                #Activating the laser
                elif event.key == pygame.K_SPACE:
                    if laserMode == False:
                        laserMode = True
                    
                else:
                    key = 'up'
                    
            if event.type == pygame.KEYUP:
                key = 'up'

        if key == 'left':
            if angle < 35 and shipX > 50:
                angle += 0.2*speed
                
            shipX, rot_ship, angle = moveShip(shipX, (angle/2.5), angle)

        elif key == 'right':
            if angle > -35 and shipX < 850:
                angle -= 0.2*speed
                
            shipX, rot_ship, angle = moveShip(shipX, (angle/2.5), angle)
            
        elif key == 'up':
            shipX, angle, rot_ship = riftpull(shipX, angle, rot_ship)


        #Collision detection
        shipRect = pygame.Rect(shipX, shipY, 85, 125)
        shipMask = pygame.mask.from_surface(rot_ship)
        if laserMode == True:
            rot_laser = pygame.transform.rotate(lasers[laserFrame], angle)
            laserRect = rot_laser.get_rect()
            laserMask = pygame.mask.from_surface(rot_laser)
            
        remove = []
        for i in range(numRock):
            rockRect = pygame.Rect(rockX[i], rockY[i], 100, 100)
            if laserMode == True:
                if rockRect.colliderect(laserRect):
                    #print('laser')
                    #print('o')
                    rockMask = pygame.mask.from_surface(rockRot[i])
                    offset_x = laserRect.x - rockRect.x
                    offset_y = laserRect.y - rockRect.y
                    laserHit = rockMask.overlap(laserMask, (offset_x, offset_y))
                    if laserHit:
                        remove.append(i)


            
            if rockRect.colliderect(shipRect):
                rockMask = pygame.mask.from_surface(rockRot[i])
                offset_x = shipRect.x - rockRect.x
                offset_y = shipRect.y - rockRect.y
                crash = rockMask.overlap(shipMask, (offset_x, offset_y))
                
        for i in range(len(remove)):
                numRock -= 1
                rockX.pop(remove[i])
                rockY.pop(remove[i])
                rockType.pop(remove[i])
                rockAngle.pop(remove[i])
                rockAngle2.pop(remove[i])
                rockRot.pop(remove[i])
                
        remove = []                    
        for i in range(numComet):
            cometRect = pygame.Rect(cometX[i], cometY[i], 80, 250)
            if cometRect.colliderect(shipRect):
                cometMask = pygame.mask.from_surface(comet)
                offset_x = shipRect.x - cometRect.x
                offset_y = shipRect.y - cometRect.y
                crash = cometMask.overlap(shipMask, (offset_x, offset_y))

        remove = []        
        for i in range(numMoon):
            moonRect = pygame.Rect(moonX[i], moonY[i], int(100*moonScale[i]), int(100*moonScale[i]))
            if int(50*moonScale[i]+60)**2 >= ((shipX+40 - moonX[i]-int(50*moonScale[i]))**2 + (shipY+100 - moonY[i]-int(50*moonScale[i]))**2) and not crash:
                moonMask = pygame.mask.from_surface(moon)
                offset_x = shipRect.x - moonRect.x
                offset_y = shipRect.y - moonRect.y
                crash = moonMask.overlap(shipMask, (offset_x, offset_y))

        remove = []
        for i in range(numFuel):
            fuelRect = pygame.Rect(fuelX[i], fuelY[i], 100, 100)
            if fuelRect.colliderect(shipRect):
                fuelMask = pygame.mask.from_surface(fuelRot[i])
                offset_x = shipRect.x - fuelRect.x
                offset_y = shipRect.y - fuelRect.y
                fuelHit = fuelMask.overlap(shipMask, (offset_x, offset_y))
                if fuelHit:
                    remove.append(i)
                
        for i in range(len(remove)):
            numFuel -= 1
            fuelX.pop(remove[i])
            fuelY.pop(remove[i])
            fuelType.pop(remove[i])
            fuelAngle.pop(remove[i])
            fuelAngle2.pop(remove[i])
            fuelRot.pop(remove[i])
            
        remove = []           
        #Moves objects down screen
        rockRot = []
        fuelRot = []
        for i in range(numRock):
            rockY[i] += speed
            rockAngle2[i] += rockAngle[i]

        for i in range(numMoon):
            moonY[i] += speed
                
        for i in range(numComet):
            cometY[i] += speed * 5
            if cometY[i] > HEIGHT:
                 warningWidth[i] = 10

        for i in range(numFuel):
            fuelY[i] += speed
            fuelAngle2[i] += fuelAngle[i]


        
                
        #Choosing a new path      
        rockPassed = HEIGHT+1
        cometPassed = HEIGHT+1
        moonPassed = HEIGHT+1
        if len(rockY) != 0:
            rockPassed = rockY[-1]
        if len(cometY) != 0:
            cometPassed = cometY[-1]
        if len(moonY) != 0:
            moonPassed = moonY[-1]
        if rockPassed > HEIGHT and cometPassed > HEIGHT and moonPassed > HEIGHT:
            numRock, rockX, rockY, numComet, cometX, cometY, numMoon, moonX, moonY, moonScale, numFuel, fuelX, fuelY = RiftObstacles.path()
            rockType = []
            rockAngle = []
            rockAngle2 = []
            rockRot = []
            fuelType = []
            fuelAngle = []
            fuelAngle2 = []
            fuelRot = []
            for i in range(numRock):
                rockType.append(random.choice(rocks))
                rockAngle.append(random.uniform(-0.2,0.2))
                rockAngle2.append(rockAngle[i])

            for i in range(numFuel):
                fuelType.append(random.choice(fuel))
                fuelAngle.append(random.uniform(-0.2,0.2))
                fuelAngle2.append(fuelAngle[i])

                
            warningWidth = []
            for i in range(numComet):
                warningWidth.append(10)

        #Shows that collision between the ship works with obstacles
        if crash:
            #print('crash')
            crash = False
            
        pygame.display.update()
        clock.tick(FPS)


distance = 0
active = True
while active:
    rift()

pygame.quit()

Upvotes: 1

Views: 187

Answers (1)

Rabbid76
Rabbid76

Reputation: 210968

pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the center of the rectangle can be specified with the keyword argument center. These keyword argument are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a full list of the keyword arguments).

Ensure that the position of laserRect is always set. The position is set here:

laserRect = rot_laser.get_rect()
if angle == 0:
   correction = 50*math.sin((angle*math.pi)/180)
   laserRect.midbottom = (shipX + 42 - correction, shipY+100)

But it is not set here:

if laserMode == True:
   rot_laser = pygame.transform.rotate(lasers[laserFrame], angle)
   laserRect = rot_laser.get_rect()
   laserMask = pygame.mask.from_surface(rot_laser)

Since laserRect is correctly set before, you don't need to get is later

if laserMode == True:
    rot_laser = pygame.transform.rotate(lasers[laserFrame], angle)
    
    # delete
    #laserRect = rot_laser.get_rect()
    
    laserMask = pygame.mask.from_surface(rot_laser)

Upvotes: 1

Related Questions