Python Forum
How to put my game loop into a function?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to put my game loop into a function?
#1
I am not sure if this is too much code to post, but this is the most code I've had in a single program so now it's overwhelming trying to solve this. I didn't think of adding a game_loop function until after I created my classes and other functions and now I get errors no matter what I try. I am currently trying to press and hold down a key (such as f) and then it will run the game loop, otherwise the game does not start. If I get that to work then I'll be able to replace that key if statement with my separate main menu program and integrate it with my game here.

When I put the entire code within the "while Running:" loop into the "def game_loop():" function, there were a bunch of errors to do with all of my variables such as them not existing or being setup properly, so I made most/all of them global variables and referenced them outside of the function but that would cause more problems.

import pygame
import random
import math

pygame.init()

# Screen dimensions
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Collision!')

# Colours
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)

# Have either 5 or 10 total AI objects spawned into the game
spawned_at_least_5 = False
spawned_at_least_10 = False

# Powerup variables - before the main game loop
powerup = None
powerup_spawned = False
powerup_destroy_time = 0

# Player brace mechanic
brace_charges = 0
charge_timer = 0  # Timer to track elapsed time for adding charges
charge_interval = 30  # Time in seconds to add a charge

# Score related variables, set all to 0 prior to the game starting
score = 0
last_score_increase = 0
last_score_double = 0

# Timer variables
def display_time(time_ms):
    """Return time in milliseconds as string mm:ss."""
    minutes, seconds = divmod(int(time_ms / 1000), 60)
    return f"{minutes: 2}:{seconds:02}"
    
timer_event = 10000  # This is 10 seconds in milliseconds
last_spawn_time = pygame.time.get_ticks()
last_time = pygame.time.get_ticks()
start_timer = pygame.time.get_ticks()
clock = pygame.time.Clock()
elapsed_time = pygame.time.get_ticks()
start_time = pygame.time.get_ticks()

class Player:
    def __init__(self):
        self.x = WIDTH // 2
        self.y = HEIGHT // 2
        self.speed = 2
        self.width = 30
        self.height = 30
        self.update_rect()

    def update_rect(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        
    def move(self, dx, dy):
        self.x += dx * self.speed
        self.y += dy * self.speed
        self.x = max(0, min(self.x, WIDTH - self.width))
        self.y = max(0, min(self.y, HEIGHT - self.height))
        self.update_rect()

    def grow(self):
        self.width += self.width * 0.1
        self.height += self.height * 0.1
        self.width = min(self.width, WIDTH)
        self.height = min(self.height, HEIGHT)
        self.update_rect()

    def reduce(self):
        self.width -= self.width * 0.15
        self.height -= self.height * 0.15
        self.update_rect()

    def draw(self):
        if pygame.key.get_pressed()[pygame.K_c] and brace_charges > 0:
            self.color = (0, 0, 255)  # Blue for active brace state
        else:
            self.color = RED
        pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
        
class AIObject:
    def __init__(self):
        self.x, self.y = random.randint(50, WIDTH-50), random.randint(50, HEIGHT-50)
        self.target_x, self.target_y = self.x, self.y
        self.speed = 2.5
        self.movement_phase = 0
        self.timer = 0

    def set_new_target(self):
        self.target_x = random.randint(50, WIDTH-50)
        self.target_y = random.randint(50, HEIGHT-50)

    def move(self):
        dx = self.target_x - self.x
        dy = self.target_y - self.y
        distance = math.sqrt(dx**2 + dy**2)
        if distance > self.speed:
            self.x += (dx / distance) * self.speed
            self.y += (dy / distance) * self.speed
        else:
            self.x, self.y = self.target_x, self.target_y
            self.timer += 1
            if self.timer >= random.randint(200, 300):
                if self.movement_phase == 0:
                    self.set_new_target()
                    self.movement_phase = 1
                elif self.movement_phase == 1:
                    self.set_new_target()
                    self.movement_phase = 2
                elif self.movement_phase == 2:
                    self.movement_phase = 0
                    self.timer = 0
                    return "delete"
                self.timer = 0

    def check_overlap_with_player(self, player):
        potential_rect = pygame.Rect(self.x - 15, self.y - 15, 30, 30)
        return player.rect.colliderect(potential_rect)
    
    @staticmethod
    def spawn_multiple():
        global spawned_at_least_5, spawned_at_least_10
        if len(ai_objects) >= 5 and not spawned_at_least_5:
            for _ in range(3):
                ai_objects.append(AIObject())
            spawned_at_least_5 = True
        if len(ai_objects) >= 10 and not spawned_at_least_10:
            for _ in range(3):
                ai_objects.append(AIObject())
            spawned_at_least_10 = True
            
    def draw(self):
        pygame.draw.circle(screen, WHITE, (int(self.x), int(self.y)), 15)

class Powerup:
    def __init__(self):
        self.x, self.y = random.randint(50, WIDTH-50), random.randint(50, HEIGHT-50)
        self.width = 20
        self.height = 20
        self.color = (200, 100, 0)
        self.update_rect()

    def update_rect(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        
    def draw(self, surface):
        pygame.draw.rect(surface, self.color, (self.x, self.y, self.width, self.height))

# Game setup
ai_objects = []
player = Player()
game_over = False
running = True

def game_loop():
    global score, last_score_increase, last_score_double, brace_charges
    global powerup, powerup_spawned, powerup_destroy_time, elapsed_time
    global last_spawn_time, last_time, start_timer, start_time, game_over

    # Reset game state variables
    ai_objects.clear()
    player.__init__()
    score = 0
    last_score_increase = 0
    last_score_double = 0
    brace_charges = 0
    powerup = None
    powerup_spawned = False
    powerup_destroy_time = 0
    elapsed_time = 0
    last_spawn_time = pygame.time.get_ticks()
    last_time = pygame.time.get_ticks()
    start_timer = pygame.time.get_ticks()
    start_time = pygame.time.get_ticks()

    while True:
        current_time = pygame.time.get_ticks()
        charge_timer = current_time - start_timer
        
        # Update the timer
        if not game_over:
            elapsed_time = current_time - start_time
            if elapsed_time - last_score_increase >= 5000:
                score += 1
                last_score_increase += 5000

            if elapsed_time - last_score_double >= 30000:
                score *= 2
                last_score_double += 30000

            if charge_timer >= 30000:
                brace_charges += 1
                start_timer = pygame.time.get_ticks()

        if game_over:
            screen.fill(BLACK)
            font = pygame.font.Font(None, 36)

            game_over_text = font.render("Game Over! Press SPACE to restart or ESC to quit.", True, WHITE)
            game_over_rect = game_over_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 100))

            time_text = font.render(f"Time: {display_time(elapsed_time)}", True, WHITE)
            screen.blit(time_text, (WIDTH // 2 - time_text.get_width() // 2, HEIGHT // 2 - time_text.get_height() // 2))

            score_text = font.render(f"Score: {score}", True, WHITE)
            screen.blit(score_text, (WIDTH // 2 - time_text.get_width() // 2, HEIGHT // 2 - score_text.get_height() // 2 + 25))

            screen.blit(game_over_text, game_over_rect)
            pygame.display.flip()
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
                    return  # Exit the game loop
                if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                    game_over = False
                    continue
            continue  # Skip the rest of the loop

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            
        # Player movement
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            player.move(0, -1)
        if keys[pygame.K_DOWN]:
            player.move(0, 1)
        if keys[pygame.K_LEFT]:
            player.move(-1, 0)
        if keys[pygame.K_RIGHT]:
            player.move(1, 0)
        
        if current_time - last_spawn_time > random.randint(2000, 4000):
            new_obj = AIObject()
            attempts = 0
            max_attempts = 100
            while new_obj.check_overlap_with_player(player) and attempts < max_attempts:
                new_obj.x, new_obj.y = random.randint(50, WIDTH-50), random.randint(50, HEIGHT-50)
                attempts += 1
            if attempts < max_attempts:
                ai_objects.append(new_obj)
            last_spawn_time = current_time

        if current_time - last_time >= timer_event:
            player.grow()
            last_time = current_time

        AIObject.spawn_multiple()

        # Powerup logic
        if powerup is None and not powerup_spawned and current_time - powerup_destroy_time >= 15000:
            powerup = Powerup()
            powerup_spawned = True

        if powerup:
            player.update_rect()
            if player.rect.colliderect(powerup.rect):
                player.reduce()
                powerup = None
                powerup_spawned = False
                powerup_destroy_time = current_time

        to_remove = []
        
        # Update AI objects and check for collisions
        for obj in ai_objects[:]:
            action = obj.move()
            if action == "delete":
                ai_objects.remove(obj)

        brace_key = pygame.key.get_pressed()
        if brace_key[pygame.K_c] and brace_charges > 0:
            for obj in ai_objects[:]:
                if player.rect.colliderect(pygame.Rect(obj.x - 15, obj.y - 15, 30, 30)):
                    to_remove.append(obj)
                    brace_charges -= 1
                    score += 5
                    if brace_charges < 0:
                        brace_charges = 0
        else:
            player.update_rect()
            for obj in ai_objects:
                if player.rect.colliderect(pygame.Rect(obj.x - 15, obj.y - 15, 30, 30)):
                    game_over = True
                    to_remove.append(obj)
                    break

        # Remove all objects that are to be deleted
        for obj in to_remove:
            if obj in ai_objects:
                ai_objects.remove(obj)

        if game_over:
            continue  # Skip the drawing and updating display part if game over

        screen.fill(BLACK)

        for obj in ai_objects[:]:
            action = obj.move()
            if action != "delete":
                obj.draw()
        
        player.draw()

        if powerup:
            powerup.draw(screen)

        keyp = pygame.key.get_pressed()
        if keyp[pygame.K_b]:
            font = pygame.font.Font(None, 36)
            score_text = font.render(f"Score: {score}", True, WHITE)
            screen.blit(score_text, (10, 10))
            time_text = font.render(f"Time elapsed: {display_time(elapsed_time)}", True, WHITE)
            screen.blit(time_text, (565, 10))
            brace_text = font.render(f"Charges: {brace_charges}", True, WHITE)
            screen.blit(brace_text, (10, 35))
            
        pygame.display.flip()
        clock.tick(50)  # 50 FPS for smooth movement

# Main loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_f:
                game_loop()

pygame.quit()
Edit: Now there's no errors, but when pressing "space" to restart the game, the game does not reset but it's just a continuation of the previous game. The time and score also don't reset, and the AI circle objects start to flicker.
Reply


Messages In This Thread
How to put my game loop into a function? - by temlotresid6 - Oct-26-2024, 11:45 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  I want to add a bullet function to my game. how would i go about it? Iyjja 5 2,725 Jan-09-2024, 05:42 PM
Last Post: Iyjja
  If 2nd input in the game is correct, replace with 1st input and loop the game tylerdurdane 11 6,417 Jul-17-2022, 04:55 AM
Last Post: deanhystad
  [PyGame] Problem With Entering Game Loop ElevenDecember 3 3,983 Jan-19-2020, 08:25 AM
Last Post: michael1789
  [Tkinter] Loop help in game Kgranulo1 1 3,996 Feb-28-2017, 08:02 AM
Last Post: wavic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020