-1

I recently started a new python file so I can test how much stuff I know. I have written a small script for a small game (I don't want help about making it better, no thanks) and whenever I begin playing at random times the window just closes. Is there an error in my code? Does it just happen by it's own? I don't know! Here is the script!

import pygame
import sys
import random
import pickle
from pygame.locals import *

pygame.init()

width = 1000
height = 600

black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
yellow = (255, 255, 0)
blue = (0, 0, 255)

player_pos = [450, 500]
player_size = 50

speed = 50

coin_pos = [random.randint(100, 900), random.randint(100, 500)]
coin_size = 20

mine_pos1 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos2 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos3 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos4 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos5 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos6 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos7 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos8 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos9 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos10 = [random.randint(100, 900), random.randint(100, 500)]
mine_pos11 = [random.randint(100, 900), random.randint(100, 500)]
mine_size = 20
big_mine_size = 40
large_mine_size = 60

coins_count = 0

title_font = pygame.font.SysFont('unisansheavyitaliccaps.ttf', 35, bold=20, italic=20)
goal_font = pygame.font.SysFont('unisansheavyitaliccaps.ttf', 37, bold=20, italic=20)

clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Collect And Avoid")

click = False


def draw_text(text, font, color, surface, x, y):
    text_obj = font.render(text, 5, color)
    text_rect = text_obj.get_rect()
    text_rect = (x, y)
    surface.blit(text_obj, text_rect)


def main_menu(player_pos):
    global click
    while True:
        yellow_cubes_text = goal_font.render("yellow cubes", True, yellow)
        red_cubes_text = goal_font.render("red cubes", True, red)
        screen.fill(black)
        draw_text('Collect And Avoid', title_font, white, screen, 375, 20)
        draw_text('Your goal is to collect as much coins as possible, the                     .', goal_font, white,
                  screen, 25, 300)
        draw_text('Careful not to step on the                , they are deadly mines that will', goal_font, white,
                  screen, 20, 350)
        draw_text('end your game if you step on them so try to avoid them!', goal_font, white, screen, 20, 400)
        draw_text('Click To Start -->', goal_font, white, screen, 150, 130)

        # coins_count = pickle.load(open("coins.dat", "rb"))
        draw_text('Total Coins: ' + str(coins_count), goal_font, white, screen, 400, 550)

        x, y = pygame.mouse.get_pos()
        screen.blit(yellow_cubes_text, (783, 300))
        screen.blit(red_cubes_text, (388, 350))

        button_1 = pygame.Rect(400, 120, 200, 50)
        if button_1.collidepoint((x, y)):
            if click:
                game(player_pos, coin_pos, coins_count)
        pygame.draw.rect(screen, red, button_1)

        click = False
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    sys.exit()
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        pygame.display.update()
        clock.tick(60)


def detect_collision(player_pos, coin_pos):
    p_x = player_pos[0]
    p_y = player_pos[1]

    c_x = coin_pos[0]
    c_y = coin_pos[1]

    if (p_x <= c_x < (p_x + player_size)) or (c_x <= p_x < (c_x + coin_size)):
        if (p_y <= c_y < (p_y + player_size)) or (c_y <= p_y < (c_y + coin_size)):
            return True

    return False


def game(player_pos, coin_pos, coins_count):
    global mine_pos1, mine_pos2, mine_pos3, mine_pos4, mine_pos5, mine_pos6, mine_pos7, mine_pos8, mine_pos9, \
        mine_pos10, mine_pos11
    game_over = False
    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

            if event.type == pygame.KEYDOWN:

                x = player_pos[0]
                y = player_pos[1]

                if event.key == pygame.K_w:
                    y -= speed
                elif event.key == pygame.K_s:
                    y += speed
                elif event.key == pygame.K_a:
                    x -= speed
                elif event.key == pygame.K_d:
                    x += speed
                elif event.key == pygame.K_ESCAPE:
                    sys.exit()

                player_pos = [x, y]

        screen.fill(black)

        if detect_collision(player_pos, coin_pos):
            coin = coin_pos = [random.randint(100, 900), random.randint(100, 500)]
            coins_count += 5
            # pickle.dump(coins_count, open("coins.dat", "wb"))
            mine_pos1 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos2 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos3 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos4 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos5 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos6 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos7 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos8 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos9 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos10 = [random.randint(100, 900), random.randint(100, 500)]
            mine_pos11 = [random.randint(100, 900), random.randint(100, 500)]

        if detect_collision(player_pos, mine_pos1):
            sys.exit()
        elif detect_collision(player_pos, mine_pos2):
            sys.exit()
        elif detect_collision(player_pos, mine_pos3):
            sys.exit()
        elif detect_collision(player_pos, mine_pos4):
            sys.exit()
        elif detect_collision(player_pos, mine_pos5):
            sys.exit()
        elif detect_collision(player_pos, mine_pos6):
            sys.exit()
        elif detect_collision(player_pos, mine_pos7):
            sys.exit()
        elif detect_collision(player_pos, mine_pos8):
            sys.exit()
        elif detect_collision(player_pos, mine_pos9):
            sys.exit()
        elif detect_collision(player_pos, mine_pos10):
            sys.exit()
        elif detect_collision(player_pos, mine_pos11):
            sys.exit()

        draw_text('Coins: ' + str(coins_count), goal_font, white, screen, 450, 20)

        if coins_count >= 5:
            mine1 = pygame.draw.rect(screen, red, (mine_pos1[0], mine_pos1[1], mine_size, mine_size))
            if coins_count >= 10:
                mine2 = pygame.draw.rect(screen, red, (mine_pos2[0], mine_pos2[1], mine_size, mine_size))
                if coins_count >= 15:
                    mine3 = pygame.draw.rect(screen, red, (mine_pos3[0], mine_pos3[1], mine_size, mine_size))
                    if coins_count >= 20:
                        mine4 = pygame.draw.rect(screen, red, (mine_pos4[0], mine_pos4[1], mine_size, mine_size))
                        if coins_count >= 25:
                            mine5 = pygame.draw.rect(screen, red, (mine_pos5[0], mine_pos5[1], mine_size, mine_size))
                            if coins_count >= 30:
                                mine6 = pygame.draw.rect(screen, red, (mine_pos6[0], mine_pos6[1], big_mine_size,
                                                                       big_mine_size))
                                if coins_count >= 35:
                                    mine7 = pygame.draw.rect(screen, red, (mine_pos7[0], mine_pos7[1], big_mine_size,
                                                                           big_mine_size))
                                    if coins_count >= 40:
                                        mine8 = pygame.draw.rect(screen, red, (
                                        mine_pos8[0], mine_pos8[1], big_mine_size, big_mine_size))
                                        if coins_count >= 50:
                                            mine9 = pygame.draw.rect(screen, red, (mine_pos9[0], mine_pos9[1],
                                                                                   big_mine_size, big_mine_size))
                                            if coins_count >= 60:
                                                mine10 = pygame.draw.rect(screen, red, (mine_pos10[0], mine_pos10[1],
                                                                                       large_mine_size,
                                                                                       large_mine_size))
                                                if coins_count >= 70:
                                                    mine11 = pygame.draw.rect(screen, red, (mine_pos11[0], mine_pos11[1],
                                                                                           large_mine_size,
                                                                                           large_mine_size))

        player = pygame.draw.rect(screen, blue, (player_pos[0], player_pos[1], player_size, player_size))
        coin = pygame.draw.rect(screen, yellow, (coin_pos[0], coin_pos[1], coin_size, coin_size))

        pygame.display.update()
        clock.tick(60)


main_menu(player_pos)
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Read about [Pygame mouse clicking detection](https://stackoverflow.com/questions/10990137/pygame-mouse-clicking-detection/64533684#64533684) and [How do I detect collision in pygame?](https://stackoverflow.com/questions/29640685/how-do-i-detect-collision-in-pygame/65064907#65064907) – Rabbid76 Feb 28 '21 at 13:01
  • Your `detect_collision` function returns `True` when it shouldn't, which triggers one of the many `sys.exit()`s in your game loop. – Paul M. Feb 28 '21 at 13:04

1 Answers1

3

The problem is that you are doing collision detection against mines that are not being drawn on the screen and terminating the program if there is any collision which makes it look like the game is randomly closing.

There are also other improvement that can be made. First, use a list for mines instead of defining mines manually.

mines = [[random.randint(100, 900), random.randint(100, 500)] for i in range(10)]

Use pygame.Rect for collision detection.

def detect_collision(player_pos, coin_pos):
    player_rect = Rect(player_pos[0], player_pos[1], player_size, player_size)
    coin_rect = Rect(coin_pos[0], coin_pos[1], coin_size, coin_size)
    if player_rect.colliderect(coin_rect):
        return True
    return False

Use a variable called mines_showing. Thats the number of mines you want to draw on the screen and do collision_detection against.

for i in range(mines_showing):
     pygame.draw.rect(screen, red, (mines[i][0], mines[i][1], mine_size, mine_size))

for mine in mines[:mines_showing]:
     if detect_collision(player_pos, mine):
          sys.exit()

Use mod operator to increment mines_showing every 5 score. But we don't want to affect the original coins_count variable, so create a new initial variable outside the loop called count_count_iter just to do the calculation and then:

if count_count_iter != 0:
     if count_count_iter % 5 == 0:
          count_count_iter = 0
          mines_showing += 1

Lastly, if there is a collision between player and coin, we want to update the new count_count_iter variable and also create new mines, but using the list instead of manually creating them.

if detect_collision(player_pos, coin_pos):
      coin = coin_pos = [random.randint(100, 900), random.randint(100, 500)]
      coins_count += 5
      count_count_iter += 5
      # pickle.dump(coins_count, open("coins.dat", "wb"))
      mines = []
      mines = [[random.randint(100, 900), random.randint(100, 500)] for i in range(10)]

New code:

import pygame
import sys
import random
import pickle
from pygame.locals import *

pygame.init()

width = 1000
height = 600

black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
yellow = (255, 255, 0)
blue = (0, 0, 255)

player_pos = [450, 500]
player_size = 50

speed = 50

coin_pos = [random.randint(100, 900), random.randint(100, 500)]
coin_size = 20

mines = [[random.randint(100, 900), random.randint(100, 500)] for i in range(10)]
mine_size = 20
big_mine_size = 40
large_mine_size = 60

coins_count = 0

title_font = pygame.font.SysFont('unisansheavyitaliccaps.ttf', 35, bold=20, italic=20)
goal_font = pygame.font.SysFont('unisansheavyitaliccaps.ttf', 37, bold=20, italic=20)

clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Collect And Avoid")

click = False

mines_showing = 0
count_count_iter = 0

def draw_text(text, font, color, surface, x, y):
    text_obj = font.render(text, 5, color)
    text_rect = text_obj.get_rect()
    text_rect = (x, y)
    surface.blit(text_obj, text_rect)


def main_menu(player_pos):
    global click
    while True:
        yellow_cubes_text = goal_font.render("yellow cubes", True, yellow)
        red_cubes_text = goal_font.render("red cubes", True, red)
        screen.fill(black)
        draw_text('Collect And Avoid', title_font, white, screen, 375, 20)
        draw_text('Your goal is to collect as much coins as possible, the                     .', goal_font, white,
                  screen, 25, 300)
        draw_text('Careful not to step on the                , they are deadly mines that will', goal_font, white,
                  screen, 20, 350)
        draw_text('end your game if you step on them so try to avoid them!', goal_font, white, screen, 20, 400)
        draw_text('Click To Start -->', goal_font, white, screen, 150, 130)

        # coins_count = pickle.load(open("coins.dat", "rb"))
        draw_text('Total Coins: ' + str(coins_count), goal_font, white, screen, 400, 550)

        x, y = pygame.mouse.get_pos()
        screen.blit(yellow_cubes_text, (783, 300))
        screen.blit(red_cubes_text, (388, 350))

        button_1 = pygame.Rect(400, 120, 200, 50)
        if button_1.collidepoint((x, y)):
            if click:
                game(player_pos, coin_pos, coins_count)
        pygame.draw.rect(screen, red, button_1)

        click = False
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    sys.exit()
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        pygame.display.update()
        clock.tick(60)


def detect_collision(player_pos, coin_pos):
    player_rect = Rect(player_pos[0], player_pos[1], player_size, player_size)
    coin_rect = Rect(coin_pos[0], coin_pos[1], coin_size, coin_size)
    if player_rect.colliderect(coin_rect):
        return True
    return False


def game(player_pos, coin_pos, coins_count):
    global mines
    global mines_showing
    global can_update
    global count_count_iter
    game_over = False
    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

            if event.type == pygame.KEYDOWN:

                x = player_pos[0]
                y = player_pos[1]

                if event.key == pygame.K_w:
                    y -= speed
                elif event.key == pygame.K_s:
                    y += speed
                elif event.key == pygame.K_a:
                    x -= speed
                elif event.key == pygame.K_d:
                    x += speed
                elif event.key == pygame.K_ESCAPE:
                    sys.exit()

                player_pos = [x, y]

        screen.fill(black)

        if detect_collision(player_pos, coin_pos):
            coin = coin_pos = [random.randint(100, 900), random.randint(100, 500)]
            coins_count += 5
            count_count_iter += 5
            # pickle.dump(coins_count, open("coins.dat", "wb"))
            mines = []
            mines = [[random.randint(100, 900), random.randint(100, 500)] for i in range(10)]

        for mine in mines[:mines_showing]:
            if detect_collision(player_pos, mine):
                sys.exit()

        draw_text('Coins: ' + str(coins_count), goal_font, white, screen, 450, 20)

        if count_count_iter != 0:
            if count_count_iter % 5 == 0:
                count_count_iter = 0
                mines_showing += 1

        for i in range(mines_showing):
            pygame.draw.rect(screen, red, (mines[i][0], mines[i][1], mine_size, mine_size))

        player = pygame.draw.rect(screen, blue, (player_pos[0], player_pos[1], player_size, player_size))
        coin = pygame.draw.rect(screen, yellow, (coin_pos[0], coin_pos[1], coin_size, coin_size))

        pygame.display.update()
        clock.tick(60)


main_menu(player_pos)