Nathan
Nathan

Reputation: 79

Pygame - collisions and lists

I'm trying to code my first game with Python and Pygame. I'm fairly new to Python and coding in general, so I'm sorry if my code is hard to read.

There are 4 circles, moving from the middle of the screen to the 4 edges. I'm dropping cubes (enemies) from the 4 edges to the middle. The goal is to stop those cubes from reaching the middle of the screen, by pressing keyboard arrows. I

Now I'm working on the logic of lifes. The player has 5 lifes. If an enemy reaches the middle of the screen, the player loses a life. I got this part.

The player also loses a life when he misscliks. I'm struggling with this part. I can see a few scenario. Let's say that the player clicks LEFT:

Any idea ?

Here's my code.

# ---------- Packages and Inits ----------

import pygame, random, math
pygame.init()

# ---------- Settings ----------

SCREEN_WIDTH  = 600
SCREEN_HEIGHT = 600
FPS = 60 
SPEED = 1
SPEED_ENEMIES = 0.5
CIRCLE_RADIUS = 50
ENEMY_SIZE = 40

red    = (255,000,000)
blue   = (000,000,255)
yellow = (255,255,000)
green  = (000,128,000)
pink   = (255,192,203)
black  = (000,000,000)

# ---------- Classes ----------

class Enemies:

    def __init__(self, x, y, size=ENEMY_SIZE, thick=5, color=blue, speed=SPEED_ENEMIES, position="top"):

        self.rect = pygame.Rect(0, 0, size, size)
        if ( x == 0 and y == 0 ):
            self.randomise()

        self.rect.centerx = x
        self.rect.centery = y
        self.size = size
        self.thick = thick
        self.color = color
        self.speed = speed
        self.calcDirection()
        self.position = position

    def calcDirection( self ):
        self.x_float = 1.0 * self.rect.centerx
        self.y_float = 1.0 * self.rect.centery

        # Determine direction vector from (x,y) to the centre of the screen
        self.position_vector = pygame.math.Vector2( self.x_float, self.y_float )
        self.velocity_vector = pygame.math.Vector2( SCREEN_WIDTH/2 - self.x_float, SCREEN_HEIGHT/2 - self.y_float )
        self.velocity_vector = self.velocity_vector.normalize()

    def update( self ):
        x_delta = self.speed * self.velocity_vector[0]
        y_delta = self.speed * self.velocity_vector[1]
        self.x_float += x_delta
        self.y_float += y_delta
        self.rect.centerx = int( self.x_float )
        self.rect.centery = int( self.y_float )

    def draw(self, screen):
        pygame.draw.rect(screen, self.color, self.rect )

    def reachedPoint( self, x, y ):
        return self.rect.collidepoint( x, y )

    def randomise( self ):
        self.rect.centerx = SCREEN_WIDTH//2
        self.rect.centery = SCREEN_HEIGHT//2
        side = random.randint( 0, 4 )
        if ( side == 0 ):
            self.rect.centery = SCREEN_HEIGHT
            self.color = green
            self.position= "bot"
        elif ( side == 1 ):
            self.rect.centery = 0
            self.color = yellow
            self.position= "top"
        elif ( side == 2 ):
            self.rect.centerx = 0
            self.color = blue
            self.position= "left"
        else:
            self.rect.centerx = SCREEN_WIDTH
            self.color = red
            self.position= "right"
        self.calcDirection()

class Circle:

    def __init__(self, x, y, radius=CIRCLE_RADIUS, thick=5, color=blue, speed=SPEED, position="top"):

        self.rect = pygame.Rect(0, 0, 2*radius, 2*radius)

        self.rect.centerx = x
        self.rect.centery = y
        self.radius = radius
        self.thick = thick
        self.color = color
        self.speed = speed
        self.position = position

        if speed >= 0:
            self.directionX = 'right'
            self.direction = 'up'
        else:
            self.directionX = 'left'
            self.direction = 'down'

    def draw(self, screen):
        pygame.draw.circle(screen, self.color, self.rect.center, self.radius, self.thick)

    def swing(self):
        if self.position == "top":
            self.rect.y -= self.speed

            if self.rect.top <= 0  and self.direction == 'up':
                self.direction = 'down'
                self.speed = -self.speed

            elif self.rect.bottom > int(SCREEN_HEIGHT/2) - self.radius  and self.direction == 'down':
                self.direction = 'up'
                self.speed = -self.speed

        if self.position == "bot":
            self.rect.y -= self.speed

            if self.rect.top < int(SCREEN_HEIGHT/2) + self.radius  and self.direction == 'up':
                self.direction = 'down'
                self.speed = -self.speed

            elif self.rect.bottom >= SCREEN_HEIGHT and self.direction == 'down':
                self.direction = 'up'
                self.speed = -self.speed

        if self.position == "left":
            self.rect.x -= self.speed

            if self.rect.right > int(SCREEN_WIDTH/2) - self.radius and self.directionX == 'left':
                self.directionX = 'right'
                self.speed = -self.speed

            elif self.rect.left <= 0 and self.directionX == 'right':
                self.directionX = 'left'
                self.speed = -self.speed

        if self.position == "right":
            self.rect.x -= self.speed

            if self.rect.left < int(SCREEN_WIDTH/2) + self.radius  and self.directionX == 'right':
                self.directionX = 'left'
                self.speed = -self.speed

            elif self.rect.right >= SCREEN_WIDTH and self.directionX == 'left':
                self.directionX = 'right'
                self.speed = -self.speed

    def isCollision(self, enemyX, enemyY, circleX, circleY):
        distance = math.sqrt((math.pow(enemyX-circleX,2))+(math.pow(enemyY-circleY,2)))
        if distance < 65:
            return True
        else:
            return False

# ---------- Main ----------

def main():

    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    screen_rect = screen.get_rect()
    clock = pygame.time.Clock()
    game_over = False
    lifes = 5
    score = 0
    myFont = pygame.font.SysFont("monospace", 25)

    # Start with 4 enemies
    all_enemies = [
        Enemies(int(SCREEN_WIDTH/2)    , 0                       , color = yellow, position = "top"),
        Enemies(int(SCREEN_WIDTH/2)    , SCREEN_HEIGHT-ENEMY_SIZE, color = green , position = "bot"),
        Enemies(0                      ,int(SCREEN_HEIGHT/2)     , color = blue  , position = "left"),
        Enemies(SCREEN_WIDTH-ENEMY_SIZE, int(SCREEN_HEIGHT/2)    , color = red   , position = "right")
    ]

    # Start with 4 circles
    all_circles = [
        Circle(screen_rect.centerx, screen_rect.centery - 2*CIRCLE_RADIUS, position="top"),
        Circle(screen_rect.centerx, screen_rect.centery + 2*CIRCLE_RADIUS, position="bot"),
        Circle(screen_rect.centerx + 2*CIRCLE_RADIUS, screen_rect.centery, position="right"),
        Circle(screen_rect.centerx - 2*CIRCLE_RADIUS, screen_rect.centery, position="left")
    ]



    while not game_over:
        screen.fill(black)

        # Reference enemy lists
        left_enemies = [x for x in all_enemies if x.position == "left"]
        right_enemies = [x for x in all_enemies if x.position == "right"]
        top_enemies = [x for x in all_enemies if x.position == "top"]
        bot_enemies = [x for x in all_enemies if x.position == "bot"]

        # Place and swing 4 circles (the player)
        for c in all_circles:
            c.draw(screen)
            c.swing()

        # The enemy reaches the middle
        for e in all_enemies:
            e.update()
            if ( e.reachedPoint( SCREEN_WIDTH//2, SCREEN_HEIGHT//2 ) ):
                lifes -=1
                e.randomise()
            e.draw( screen )


        # Score points with keyboard arrow
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

            for c in all_circles:

                if event.type == pygame.KEYDOWN:

                    # LEFT
                    if event.key == pygame.K_LEFT and c.position == "left":
                        if not left_enemies :
                            lifes -=1

                        for e in left_enemies:
                            collision = c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)
                            if e.position == "left" and collision == True :
                                score +=1
                                e.randomise()
                            else:
                                lifes -=1 

                    # RIGHT
                    if event.key == pygame.K_RIGHT and c.position == "right":
                        if not right_enemies :
                            lifes -=1

                        for e in right_enemies:
                            collision = c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)
                            if e.position == "right" and collision == True :
                                score +=1
                                e.randomise()
                            else:
                                lifes -=1 

                    # TOP
                    if event.key == pygame.K_UP and c.position == "top":
                        if not top_enemies :
                            lifes -=1

                        for e in top_enemies:
                            collision = c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)
                            if e.position == "top" and collision == True :
                                score +=1
                                e.randomise()
                            else:
                                lifes -=1 

                    # BOT
                    if event.key == pygame.K_DOWN and c.position == "bot":
                        if not bot_enemies :
                            lifes -=1

                        for e in bot_enemies:
                            collision = c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)
                            if e.position == "bot" and collision == True :
                                score +=1
                                e.randomise()
                            else:
                                lifes -=1 

        print_lifes = myFont.render("Lifes:" + str(lifes), 1, red)
        screen.blit(print_lifes, (10, SCREEN_HEIGHT-50))

        print_score = myFont.render("Score:" + str(score), 1, red)
        screen.blit(print_score, (10, 10))

        pygame.display.update()
        clock.tick(FPS)
main()
pygame.quit()

Upvotes: 2

Views: 187

Answers (1)

sloth
sloth

Reputation: 101162

Your rules can be broken down to one simply rule:

If the player presses a key, look if there's an enemy under the circle.

So on KEYDOWN, filter the enemy list for all enemies that collide with the circle and just check if the number is > 0.

Replace your checks/loops:

if not left_enemies:
    lifes -=1

for e in left_enemies:
    collision = c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)
    if e.position == "left" and collision == True :
        score +=1
        e.randomise()
    else:
        lifes -=1 

with something like this:

hits = [e for e in left_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery) and position == "left"]
if not hits:
    lifes -=1
for e in hits:
    score +=1
    e.randomise()

Upvotes: 3

Related Questions