-1

Aim: Distance enemies from each other to provide player a chance to dodge.

Problem: Enemies spawn in clumps. Example of problem:

example of problem

Limitations:

  1. Enemies move in one direction
  2. background is an imported image so screen.blit() can't be used

Attempts (futile):

  1. increasing the range of randint
  2. using delay = random.random()
  3. tried increasing the value of milliseconds for obstacle_timer ==> unchanged.
import pygame 
from sys import exit
from random import randint, choice
import random

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        walk1 = pygame.image.load('').convert_alpha()
        walk2 = pygame.image.load('').convert_alpha()
        
        self.player_walk = [walk1, walk2]
        self.player_index = 0
        
        jump = pygame.image.load('').convert_alpha()
        fall1 = pygame.image.load('').convert_alpha()
        fall2 = pygame.image.load('').convert_alpha()
        fall3 = pygame.image.load('').convert_alpha()
        
        self.player_jump = [jump, fall1, fall2, fall3]
        
        self.image = self.player_walk[self.player_index]
        self.rect = self.image.get_rect(midbottom = (80, 300))
        self.gravity = 0
        self.jump_sound = pygame.mixer.Sound('')
        

    def player_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE] and self.rect.bottom >= 300:
            self.gravity = -20
            self.jump_sound.play()
    
    def apply_gravity(self):
        self.gravity += 1
        self.rect.y += self.gravity
        if self.rect.bottom >= 300: self.rect.bottom = 300 

    def animation_state(self):
        if self.rect.bottom < 300: 
            self.player_index += 0.07
            if self.player_index >= len(self.player_jump): self.player_index = 0
            self.image = self.player_jump[int(self.player_index)]
        else:
            self.player_index += 0.1
            if self.player_index >= len(self.player_walk): self.player_index = 0
            self.image = self.player_walk[int(self.player_index)]

    def update(self):
        self.player_input()
        self.apply_gravity()
        self.animation_state()

class Obstacle(pygame.sprite.Sprite):
    def __init__(self, type):
        super().__init__()

        if type == 'Obstacle1':
            obstacle1 = pygame.image.load('').convert_alpha()
            obstacle1a = pygame.image.load('').convert_alpha()
            obstacle1b = pygame.image.load('').convert_alpha()
            self.anim = [obstacle1, obstacle1a, obstacle1b]
            y_pos = 150

        else: 
            obstacle2 = pygame.image.load('').convert_alpha()
            obstacle2a = pygame.image.load('').convert_alpha()
            obstacle2b = pygame.image.load('').convert_alpha()
            self.anim = [obstacle2, obstacle2a, obstacle2b]
            y_pos = 300

        self.animation_index = 0
        self.image = self.anim[self.animation_index]
        self.rect = self.image.get_rect(midbottom = (randint(900, 4000), y_pos))
    
    def animation_state(self):
        self.animation_index += 0.1
        if self.animation_index >= len(self.anim): self.animation_index = 0
        self.image = self.anim[int(self.animation_index)]
     
    def destroy(self):
        if self.rect.x <= -100: self.kill()
    
    def update(self):
        self.animation_state()
        self.rect.x -= 6
        self.destroy()

def display_score():
    current_time = int(pygame.time.get_ticks()/1000) - start_time
    score_surf = font.render(f'Score: {current_time}', False, "white")
    score_rect = score_surf.get_rect(center = (400, 50))
    screen.blit(score_surf, score_rect)
    return current_time 

def collision():
    if pygame.sprite.spritecollide(player.sprite,obstacle_group,False):
        obstacle_group.empty()
        bg_rect.x = 0
        return False
    else: 
        return True



pygame.init()
screen = pygame.display.set_mode((800, 350)) #display keeps it up.
pygame.display.set_caption('Game by @xxRookiexx')
clock = pygame.time.Clock()
game_active = False
score = 0
start_time = 0
during = pygame.mixer.Sound('')
during.play(loops = -1)


#Groups
player = pygame.sprite.GroupSingle()
player.add(Player())

obstacle_group = pygame.sprite.Group()

#variables
font = pygame.font.Font('', 20) #fonttype,fontsize

#backgrounds
bg = pygame.image.load('').convert_alpha() #convert to animation
bg_rect = bg.get_rect(topleft = (0,0))
land = pygame.image.load('').convert_alpha()
shadow = pygame.image.load('').convert_alpha()

#Home screen
player_stand = pygame.image.load('')
player_stand_rect = player_stand.get_rect(center = (400, 225))
intro = pygame.image.load('')
intro_rect = intro.get_rect(topleft = (0,0))

#Timer 
obstacle_timer = pygame.USEREVENT + 1
pygame.time.set_timer(obstacle_timer,1500)

while True:
    
    #all elements
    for event in pygame.event.get(): #for loop. #region 
        
        if event.type == pygame.QUIT: #'x'button clicked to quit the game. 
            pygame.quit() #opposite of pygame.display.set_mode()
            exit() #true loop is gone  

    if game_active: #True
        if event.type == obstacle_timer:
            obstacle_group.add(Obstacle(choice(['Obstacle1', 'Obstacle2', 'Obstacle2'])))#more same object in an array increase possibility of getting chosen.
    else: #False
        if event.type == pygame.MOUSEBUTTONDOWN and player_stand_rect.collidepoint(pygame.mouse.get_pos()): 
            start_game = pygame.mixer.Sound('')
            start_game.play()
            game_active = True
            start_time = int(pygame.time.get_ticks()/1000)
   
            
    if game_active:
        
        bg_rect.x -= 0.5
        if bg_rect.x == -1000: bg_rect.x = 0
        screen.blit(bg,bg_rect)
        screen.blit(land, (0,300))
        score = display_score()
        player.draw(screen)
        player.update() 
        delay = random.random()
        obstacle_group.draw(screen)
        obstacle_group.update()
        
        game_active = collision()
    else:
        screen.blit(intro, intro_rect)
        screen.blit(player_stand,player_stand_rect)
        score_message = font.render(f'Your score: {score}',False,"white")
        score_message_rect = score_message.get_rect(center = (400,330))
        screen.blit(score_message,score_message_rect)
        game_active = False

    pygame.display.update()
    clock.tick(60)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174

1 Answers1

1

As I understood your enemies (Obstacles) are stored in an obstacle_group. To avoid enemies spawning close together, you could:

  • increase time between spawns (when randomizing time, implement a minimum time gap between two spawns)
  • make a condition on when to spawn new enemies (e.g. if the last spawned enemy is 100 pixel away from the spawn point)

Option 1 seems easiest. If your enemies move 6 to the left each time you call update(), and your enemies are 60 wide, you would need to wait at least 10 update-cycles to spawn the next one (assuming you spawn in the next location).

If you want to go a but further, you could iterate over each enemy in the obstacle_group to see if you can spawn in a new enemy (e.g. by comparing x-positions to check if they would overlap). There are many approaches to this, try out new things!

Quantum
  • 510
  • 1
  • 2
  • 19
  • 1
    Hey, thanks for the advise! I just realised I made a mistake in indentation, causing the spawn to take place outside the for loop and the obstacles to be spawned continuously. But still, I really appreciate that you answered my question. – xxRookiexx Jun 14 '23 at 04:21