After the first goblin the second is not spawning randomly

Viewed 40

my code is not working to randomize the goblins spawn point, basically what its doing is randomly spawning a single goblin but not spawning more then one goblin randomly, even though I've set up a while loop that should be accessed more then once. (note i am a very new programmer, i'm sorry if i'm asking a question that should be obvious). im sorry i really can't figure out how to put my code on properly, so the indents are all messed up.

here's the code

import pygame

import random

pygame.init()

win = pygame.display.set_mode((500,480))

pygame.display.set_caption("First Game")

walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'), 
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'), 
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'), 
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'), 
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
bg = pygame.image.load('bg.jpg')
char = pygame.image.load('standing.png')

clock = pygame.time.Clock()

bulletSound = pygame.mixer.Sound('bullet.wav')
hitSound = pygame.mixer.Sound('hit.wav')
stage = 1
printStage = True
music = pygame.mixer.music.load('music.mp3')
pygame.mixer.music.play(-1)
goblinAlive = True
score = 0



class player(object):
def __init__(self,x,y,width,height):
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.vel = 5
    self.isJump = False
    self.left = False
    self.right = False
    self.walkCount = 0
    self.jumpCount = 10
    self.standing = True
    self.hitbox = (self.x + 17, self.y + 11, 29, 52)

def draw(self, win):
    if self.walkCount + 1 >= 27:
        self.walkCount = 0

    if not(self.standing):
        if self.left:
            win.blit(walkLeft[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        elif self.right:
            win.blit(walkRight[self.walkCount//3], (self.x,self.y))
            self.walkCount +=1
    else:
        if self.right:
            win.blit(walkRight[0], (self.x, self.y))
        else:
            win.blit(walkLeft[0], (self.x, self.y))
    self.hitbox = (self.x + 17, self.y + 11, 29, 52)
    #pygame.draw.rect(win, (255,0,0), self.hitbox,2)

def hit(self):
    self.x = 60
    self.y = 410
    self.walkCount = 0
    font1 = pygame.font.SysFont('comicsans', 100)
    text = font1.render('-5', 1, (255,0,0))
    win.blit(text, (250 - (text.get_width()/2),200))
    pygame.display.update()
    i = 0
    while i < 200:
        pygame.time.delay(10)
        i += 1
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                i = 201
                pygame.quit()



class projectile(object):
    def __init__(self,x,y,radius,color,facing):
    self.x = x
    self.y = y
    self.radius = radius
    self.color = color
    self.facing = facing
    self.vel = 8 * facing

def draw(self,win):
    pygame.draw.circle(win, self.color, (self.x,self.y), self.radius)


 class enemy(object):
    walkRight = [pygame.image.load('R1E.png'), pygame.image.load('R2E.png'), 
pygame.image.load('R3E.png'), pygame.image.load('R4E.png'), pygame.image.load('R5E.png'), 
pygame.image.load('R6E.png'), pygame.image.load('R7E.png'), pygame.image.load('R8E.png'), 
pygame.image.load('R9E.png'), pygame.image.load('R10E.png'), pygame.image.load('R11E.png')]
    walkLeft = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), 
pygame.image.load('L3E.png'), pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), 
pygame.image.load('L6E.png'), pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), 
pygame.image.load('L9E.png'), pygame.image.load('L10E.png'), pygame.image.load('L11E.png')]

def __init__(self, x, y, width, height, end):
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.end = end
    self.path = [self.x, self.end]
    self.walkCount = 0
    self.vel = 3
    self.hitbox = (self.x + 17, self.y + 2, 31, 57)
    self.health = 10
    self.visible = True

def draw(self,win):
    self.move()
    if self.visible:
        if self.walkCount + 1 >= 33:
            self.walkCount = 0

        if self.vel > 0:
            win.blit(self.walkRight[self.walkCount //3], (self.x, self.y))
            self.walkCount += 1
        else:
            win.blit(self.walkLeft[self.walkCount //3], (self.x, self.y))
            self.walkCount += 1

        pygame.draw.rect(win, (255,0,0), (self.hitbox[0], self.hitbox[1] - 20, 50, 10))
        pygame.draw.rect(win, (0,128,0), (self.hitbox[0], self.hitbox[1] - 20, 50 - (5 * (10 - self.health)), 10))
        self.hitbox = (self.x + 17, self.y + 2, 31, 57)
        #pygame.draw.rect(win, (255,0,0), self.hitbox,2)

def move(self):
    if self.vel > 0:
        if self.x + self.vel < self.path[1]:
            self.x += self.vel
        else:
            self.vel = self.vel * -1
            self.walkCount = 0
    else:
        if self.x - self.vel > self.path[0]:
            self.x += self.vel
        else:
            self.vel = self.vel * -1
            self.walkCount = 0

def hit(self):
    if self.health > 0:
        self.health -= 1
    else:
        self.visible = False
    print('hit')



def redrawGameWindow():
    win.blit(bg, (0,0))
    text = font.render('You Are On Stage: ' + str(stage), 1, (0,0,0))
    win.blit(text, (10, 10))
    text = font.render('Score: ' + str(score), 1, (0,0,0))
    win.blit(text, (370, 10))
    man.draw(win)
    goblin.draw(win)
    for bullet in bullets:
        bullet.draw(win)

    pygame.display.update()






#mainloop
    font = pygame.font.SysFont('comicsans', 30, True)
  printStageFont = pygame.font.SysFont('comicsans', 20, True)
man = player(30, 410, 64,64)
goblin = enemy(random.randint(0, 200), 410, 64, 64, random.randint(201, 490))
shootLoop = 0
numOfGoblins = 1
bullets = []
run = True
while run:
clock.tick(27)
if man.y > 410:
    man.y = 410

while numOfGoblins < stage:
    goblin = enemy(random.randint(0, 200), 410, 64, 64, random.randint(201, 490))
    numOfGoblins += 1


if goblin.visible:
    if man.hitbox[1] < goblin.hitbox[1] + goblin.hitbox[3] and man.hitbox[1] + man.hitbox[3] > goblin.hitbox[1]:
        if man.hitbox[0] + man.hitbox[2] > goblin.hitbox[0] and man.hitbox[0] < goblin.hitbox[0] + goblin.hitbox[2]:
            man.hit()
            score -= 5
vel = 5
if shootLoop > 0:
    shootLoop += 1
if shootLoop > 15:
    shootLoop = 0

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        run = False

for bullet in bullets:
    if goblin.visible == True:

        if bullet.y - bullet.radius < goblin.hitbox[1] + goblin.hitbox[3] and bullet.y + bullet.radius > goblin.hitbox[1]:
            if bullet.x + bullet.radius > goblin.hitbox[0] and bullet.x - bullet.radius < goblin.hitbox[0] + goblin.hitbox[2]:
                hitSound.play()
                goblin.hit()
                score += 1
                bullets.pop(bullets.index(bullet))

    if bullet.x < 500 and bullet.x > 0:
        bullet.x += bullet.vel
    else:
        bullets.pop(bullets.index(bullet))

keys = pygame.key.get_pressed()

if keys[pygame.K_z] and shootLoop == 0:
    bulletSound.play()
    if man.left:
        facing = -1
    else:
        facing = 1

    if len(bullets) < 5:
        bullets.append(projectile(round(man.x + man.width //2), round(man.y + man.height//2), 6, (0,0,0), facing))

    shootLoop = 1

if keys[pygame.K_LEFT] and man.x > man.vel:
    man.x -= man.vel
    man.left = True
    man.right = False
    man.standing = False
elif keys[pygame.K_RIGHT]:

    if man.x == 500 and goblin.visible == False:
        man.x = 30
        stage += 1
        numOfGoblins = 0
    if man.x > 500 - man.width - man.vel and goblin.visible == True:
        vel = 0
    else:

        man.x += man.vel
        man.right = True
        man.left = False
        man.standing = False
else:
    man.standing = True
    man.walkCount = 0

if not(man.isJump):
    if keys[pygame.K_SPACE]:
        man.isJump = True
        man.right = False
        man.left = False
        man.walkCount = 0
else:
    if man.jumpCount >= -10:
        neg = 1
        if man.jumpCount < 0:
            neg = -1
        man.y -= (man.jumpCount ** 2) * 0.5 * neg
        man.jumpCount -= 1
    else:
        man.isJump = False
        man.jumpCount = 10

redrawGameWindow()

pygame.quit()
1 Answers

The code is re-assigning a new enemy object into the goblin variable each time, this over-writes the existing value. So there is only ever one enemy:

while numOfGoblins < stage:
    goblin = enemy(random.randint(0, 200), 410, 64, 64, random.randint(201, 490))  # <-- HERE
    numOfGoblins += 1

Maybe use a list of enemy. A list - which is behaves much like an array in other programming languages is an ordered set of items. So whereas a plain variable stores only one object or value, a list can store many.

For example, we can create a list of 3 enemies:

goblins = []  # a new list, which is empty
goblins.append( enemy( 100, 410, 64, 64, 200 ) )  # Add 1st goblin to list
goblins.append( enemy( 150, 410, 64, 64, 220 ) )  # Add 2nd goblin
goblins.append( enemy( 200, 410, 64, 64, 240 ) )  # Add 3rd goblin

To access the items in the list goblins, the program uses a square-bracket notation, with the number of the item, e.g.: goblins[1]. There is an important point here though - the first item in the list is at [0]. Normally we think of the first item being item 1, but in most programming languages, this is number 0. So goblins[0] refers to the first enemy in the list.

Thus:

# Put the first goblin in the list onto the screen
goblins[0].draw( window )

But watch out for going past the end of the list:

goblins[3].draw( window )  # <-- ERROR! so-far only elements [0], [1], [2] have been added.

The really useful aspect of using lists is to be able to write simple loops that perform the same action with each item in the list:

# Paint 3 goblins
for i in range( 0, 3 ):
    goblins[i].draw( window )

# Paint every goblin in the list (no matter how many):
for i in range( len( goblins ) ):
    goblins[i].draw( window )

# Paint every goblin, looping without the numeric counter
# On each loop, the <gob> variable becomes equal to the next-item
for gob in goblins:
    gob.draw( window )

Keeping all your enemy objects in a list makes drawing, and checking for collisions simple because the code can just loop over the list, performing the same action for each one.

When it's time to remove an item from the list (say the goblin is retired), the code can use the Python del() function, or perhaps pop().

So merging this idea with your existing code:

goblins = []
while ( len( goblins ) < stage ):
    goblins.append( enemy(random.randint(0, 200), 410, 64, 64, random.randint(201, 490)) )

(which can also be done in a single line of code, but is less-readable)

And then when painting the goblins, iterate through the list, drawing each one:

for gobbo in goblins:
    gobbo.draw( win )

Obviously collisions need to be handled in much the same way - checking each goblin in the goblins list. The PyGame sprite class already has code for this sort of operation.

Related