TerraceMason
TerraceMason

Reputation: 93

One click activates multiple buttons if overlapping

Alright, so yes, I get that it is a lot of code I am about to display.

My problem: I am making a zombie shooter game where you are the main character on top of a building and you have to click on zombies as they come in waves. My current problem is whenever multiple zombies overlap on top of each other, I can kill both of them (or as many are overlapping) in one click because technically, all of their hitboxes are colliding.

If this is a bigger problem than sought out to be, I would like someone to just say that.

import pygame
import random
import time

pygame.init()
#Setting Variables
screenW = 1020
screenH = 630
x = 125
y = 164
width = 50
height = 50
velocity = 5
wave = 2
GOLD = (255, 215, 0)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 128, 0)
wallHealth = 0
zombieKilled = 0


class ZombieChars():
    def __init__(self):
        self.damage = 0
        self.vel = 5
        self.x_change = random.randrange(2,5)
        self.y_change = 1
        self.height = 120
        self.width = 149
        self.color = random.sample(range(250), 4)
        self.image = pygame.Surface([self.width, self.height], pygame.HWSURFACE, 32)
        self.rect = self.image.get_rect(topleft = (random.randrange(900, 1150), 330))
        #pygame.draw.rect(self.image, (self.color), (self.x, self.y, self.width, self.height))

    def draw(self):
        window.blit(ZombieWalking, self.rect.topleft)

    def update(self):
        if self.rect.x >= 364:
            self.rect.x -= self.x_change
        else:
            self.rect.x -= 0

    def wallHP(self):
        global wallHealth
        if self.rect.x < 365:
            self.damage += 1
        if self.damage == 30:
            self.damage = 0
            wallHealth += 1


    def death(self):
        global zombieKilled
        if event.type == pygame.MOUSEBUTTONDOWN:
            gunShot.play()
            mouse_pos = event.pos
            if self.rect.collidepoint(mouse_pos):
                self.rect.x = 5000
                self.rect.x -= self.x_change
                zombieHit.play()
                zombieKilled += 1
                print(zombieKilled)

    def waveCounter(self):
        global wave
        print(wave)
        if wave == zombieKilled / 2:
            wave = 2



#FPS
clock = pygame.time.Clock()
clock.tick(60)

#Screen
window = pygame.display.set_mode((screenW,screenH))
pygame.display.set_caption(("Zombie Shooter"))


#Image Loading
bg = pygame.image.load("bg.jpg")
mainmenu = pygame.image.load("mainmenu.jpg")
ZombieWalking = pygame.image.load("Sprites/AAIdle.png")

#Sound Loading
gunShot = pygame.mixer.Sound('sounds/gunShot.wav')
zombieHit = pygame.mixer.Sound('sounds/zombieHit.wav')
gameMusic = pygame.mixer.music.load('sounds/gameMusic.mp3')
menuMusic = pygame.mixer.music.load('sounds/menuMusic.mp3')



zombies = ZombieChars()

my_list = []
for zombs in range(wave):
    my_object = ZombieChars()
    my_list.append(my_object)
def text_objects(text, font):
    textSurface = font.render(text, True, BLACK)
    return textSurface, textSurface.get_rect()

smallText = pygame.font.Font('freesansbold.ttf', 30)
tinyText = pygame.font.Font('freesansbold.ttf', 20)

TextSurf3, TextRect3 = text_objects("Wave: " + str(wave), smallText)
TextRect3.center = ((1020 / 2), (50))


#Main Loop
run = True
mainMenu = True
pygame.mixer.music.play()
global event
while mainMenu == True:

    window.blit(mainmenu, (0,0))
    pygame.display.flip()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False   #if x is pressed dont run game
            mainMenu = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_a]:
        mainMenu = False  #if a is pressed run game


def wallHPBar():
    pygame.draw.rect(window, GREEN, (20, 20, 100, 10))
    if wallHealth == 0:
        pass
    if wallHealth == 1:
        pygame.draw.rect(window, RED, (20, 20, 25, 10))
    if wallHealth == 2:
        pygame.draw.rect(window, RED, (20, 20, 50, 10))
    if wallHealth == 3:
        pygame.draw.rect(window, RED, (20, 20, 75, 10))
    if wallHealth >= 4:
        pygame.draw.rect(window, RED, (20, 20, 100, 10))

def overlapKill():
    if zombieKilled == 1:
        print("oh my goodness we going")
    if zombieKilled == 2:
        print("we 2 ")
while run:
    pygame.mixer.music.stop()
    window.blit(bg, (0, 0))
    window.blit(TextSurf3, TextRect3)
    wallHPBar()
    pygame.time.delay(25)

    for zombie in my_list:
        zombie.draw()
        zombie.update()
        zombie.death()
        zombie.wallHP()
        zombie.waveCounter()

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

Thank you.

Upvotes: 3

Views: 174

Answers (2)

Kingsley
Kingsley

Reputation: 14906

A Zombie object should not be dealing with user-input. Handle the click outside of the zombie, then the outside code gets to decide if the click is "used up".

class ZombieChars():

[ ... ]

def death( self, mouse_position ):
    killed = False
    global zombieKilled
    if self.rect.collidepoint( mouse_position ):
        self.rect.x = 5000
        self.rect.x -= self.x_change
        zombieHit.play()
        zombieKilled += 1
        print(zombieKilled)
        killed = True
    return killed

Then in your main loop, stop processing hits once the first is found:

### Main Loop
while not exiting:

    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            exiting = True
        elif ( event.type == pygame.MOUSEBUTTONDOWN ):
            gunShot.play()
            mouse_pos = event.pos
            for zombie in my_list:
                if ( zombie.death( mouse_pos ) ):
                    break   # stop on first hit

Upvotes: 4

Rabbid76
Rabbid76

Reputation: 210889

Remove the event handling from the method death and return a boolean value, that indicates if a zombie was killed:

class ZombieChars():
    # [...]

    def death(self):
        global zombieKilled
        mouse_pos = pygame.mouse.get_pos()
        if self.rect.collidepoint(mouse_pos):
            self.rect.x = 5000
            self.rect.x -= self.x_change
            zombieHit.play()
            zombieKilled += 1
            print(zombieKilled)
            return True
        return False

Do the pygame.MOUSEBUTTONDOWN event handling in the event loop and evaluate if a zombie was killed in a loop. break the loop when a zombie is killed. Thus only one zombie can be get killed on one klick:

while run:
    # [...]

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

        if event.type == pygame.MOUSEBUTTONDOWN:
            gunShot.play()
            for zombie in (reversed):
                if zombie.death():
                    break


    for zombie in my_list:
        zombie.draw()
        zombie.update()
        # zombie.death() <--- DELETE
        zombie.wallHP()
        zombie.waveCounter()

Upvotes: 4

Related Questions