Search code examples
pythonimagepygamecollision-detection

These hit boxes are crossing over even though they are literally not with if statements


I am trying to get my pieces to collide and and when I jump up into the air the characters should not collide. Here is a video explaining the problem: https://drive.google.com/file/d/1a26Vmd6TF4C3dJ0DpnFLTR1d3a_lEfn4/view?usp=sharing

The collision is with the goblin. Furthermore, we can see that the goblin is colliding even though the boxes aren't touching and when on top of the hitbox.

I have tried to change the condition of the loop for example changed the ">" around to "<" and tried making the jump higher. Both of these did not resolve the problem and I am not an experienced coder.

Here is the main problem:

if (adventurer.hitbox_running[0] > attacker.hitbox[0] and adventurer.hitbox_running[0] < attacker.hitbox[0]+70) or (adventurer.hitbox_running[0]+70 > attacker.hitbox[0] and adventurer.hitbox_running[0]+90 < attacker.hitbox[0]+70):
            if  (adventurer.hitbox_running[1] > attacker.hitbox[1] and adventurer.hitbox_running[1] < attacker.hitbox[1]+70) or (adventurer.hitbox_running[1]+90 > attacker.hitbox[1] and adventurer.hitbox_running[1]+90 < attacker.hitbox[1]+70):
                window.fill((255,255,255))
                death()
                pygame.display.update()
                s(0.1)
                print("hit")

Here is the entire code with all of the classes and everything:

from time import sleep as s
import random
import pygame
import time

#Loads and plays the music
pygame.mixer.pre_init(44100,16,2,4096)
pygame.init()
pygame.mixer.music.load("Music.mp3")
pygame.mixer.music.set_volume(0.0)
pygame.mixer.music.play(-1)

score = 0

#sets the dimension of the window and loads in the background
window = pygame.display.set_mode((1000, 500))
pygame.display.set_caption("Practice Game")
background = pygame.image.load('pixel6.png')

#Transforms the background to desired dimensions
background = pygame.transform.scale(background, (1000, 500))

#Loads all the jump animation
jumpv1 = [pygame.image.load('adventurer-jump-00.png'),pygame.image.load('adventurer-jump-01.png'),pygame.image.load('adventurer-jump-02.png'),pygame.image.load('adventurer-jump-03.png'), pygame.image.load('adventurer-smrslt-00.png'),pygame.image.load('adventurer-smrslt-01.png'),pygame.image.load('adventurer-smrslt-02.png'),pygame.image.load('adventurer-smrslt-03.png')]
jumpv2 = [pygame.image.load('adventurer-smrslt-00.png'),pygame.image.load('adventurer-smrslt-01.png'),pygame.image.load('adventurer-smrslt-02.png'),pygame.image.load('adventurer-smrslt-03.png')]
jumpv3 = [pygame.image.load('adventurer-smrslt-00.png'),pygame.image.load('adventurer-smrslt-01.png'),pygame.image.load('adventurer-smrslt-02.png'),pygame.image.load('adventurer-smrslt-03.png')]
jumpv4 = [pygame.image.load('adventurer-smrslt-00.png'),pygame.image.load('adventurer-smrslt-01.png'),pygame.image.load('adventurer-smrslt-02.png'),pygame.image.load('adventurer-smrslt-03.png')]
jump = jumpv1+jumpv2+jumpv3+jumpv4

#running animation
run = [pygame.image.load('adventurer-run-00.png'), pygame.image.load('adventurer-run-01.png'),pygame.image.load('adventurer-run-02.png'),pygame.image.load('adventurer-run-03.png')]

#sliding animation
slide = [pygame.image.load('adventurer-slide-00.png'),pygame.image.load('adventurer-slide-01.png'),pygame.image.load('adventurer-stand-00.png'),pygame.image.load('adventurer-stand-01.png'),pygame.image.load('adventurer-stand-02.png')]

#attacking animation
firstattack = [pygame.image.load('adventurer-attack3-00.png'),pygame.image.load('adventurer-attack3-01.png'),pygame.image.load('adventurer-attack3-02.png'),pygame.image.load('adventurer-attack3-03.png'),pygame.image.load('adventurer-attack3-04.png'),pygame.image.load('adventurer-attack3-05.png')]
secondattack = [pygame.image.load('adventurer-attack2-00.png'),pygame.image.load('adventurer-attack2-01.png'),pygame.image.load('adventurer-attack2-02.png'),pygame.image.load('adventurer-attack2-03.png'),pygame.image.load('adventurer-attack2-04.png'),pygame.image.load('adventurer-attack2-05.png')]
thirdattack = [pygame.image.load('adventurer-attack1-00.png'),pygame.image  .load('adventurer-attack1-01.png'),pygame.image.load('adventurer-attack1-02.png'),pygame.image.load('adventurer-attack1-03.png'), pygame.image.load('adventurer-attack1-04.png')]
attack = firstattack+secondattack+ thirdattack

#falling animation
falling = [pygame.image.load('adventurer-fall-00.png'), pygame.image.load('adventurer-fall-01.png')]                               

score = 0
run_program = True

#resizes all the adventuruer images to specified dimensions
for i in range (20):
    jump[i] = pygame.transform.scale(jump[i],(90,90))
for i in range(3):
    run[i] = pygame.transform.scale(run[i],(90,90))
for i in range (4): 
    slide[i] = pygame.transform.scale(slide[i],(90,90))
for i in range (1):
    falling[i] = pygame.transform.scale(falling[i],(90,90))
for i in range(16):
    attack[i] = pygame.transform.scale(attack[i],(90,90))

background_x = 0
background_2 = background.get_width()
clock = pygame.time.Clock()

ghost_image = pygame.image.load('imgg.png')
def text_objects(text, font):
    textSurface = font.render(text, True, (0,0,0))
    return textSurface, textSurface.get_rect()

def message_display(text):
    largeText = pygame.font.Font('freesansbold.ttf',115)
    TextSurf, TextRect = text_objects(text, largeText)
    TextRect.center = ((1000/2),(500/2))
    window.blit(TextSurf, TextRect)
    pygame.display.update()

def death():
    message_display('You DIED')

class ghost(object):
    ghost_animation = [pygame.image.load('imgg.png'),pygame.image.load('imgg.png')]
    def __init__(self, x,y,height):
        self.x = x
        self.y =y
        self.height = height
        self.hitbox = (x,y,100,100)
        self.count = 0

    def draw(self, window):
        self.hitbox = (self.x, self.y, 45, 60)
        if self.count >=1:
            self.count = 0
        self.count +=1
        window.blit(pygame.transform.scale(self.ghost_animation[self.count], (45,60)), (self.x, self.y)) 
        pygame.draw.rect(window, (255,0,0), self.hitbox, 2)

    def hit(self):
            print ("hit")

class eye(object):
    eye_load = pygame.image.load('obstc2.png')
    def __init__(self, x,y,width2, height2):
        self.x = x
        self.y =y
        self.hitbox_enemy = (x,y,200,100)
        self.count1 = 0

    def draw(self, window):
        self.hitbox_enemy = (self.x, self.y, 65, 65)
        window.blit(pygame.transform.scale(self.eye_load, (65,65)), (self.x, self.y)) 
        pygame.draw.rect(window, (255,0,0), self.hitbox_enemy, 2)

class player(object):
    def __init__(self, x,y):
        self.vel = 0.5
        self.running = True
        self.jumping = True
        self.sliding = True
        self.attacking = True
        self.isJump = False
        self. jumps = True
        self.fall = True
        self.player_x = 40
        self.player_y = 378
        self.x = 40
        self.y = 378
        self.width = 378
        self.speed = 0.6
        self.jumpcount = 10
        self.jumpingcount = 0
        self.runcount = 0
        self.attackcount = 0
        self.slidecount = 0       
        self.fallspeed = 0.3
        self.fallingcount = 0
        self.hitbox_running = (self.player_x,self.player_y,90,90)
        self.hitbox_sliding = (45,self.player_y+47,65,45)
        self.hitbox_falling = (self.player_x+30,self.player_y,35,80)
        self.hitbox_jumping = (self.player_x+20,self.player_y+20,52,55)
        self.hitbox_attacking = (58,405,47,70)
        self.hitbox_sword = (108, 405, 20, 50)


    def movement(self, window):
        pygame.time.delay(20)
        if self.runcount >= 3:
            self.runcount = 0

        if self.running == True:
            window.blit(run[self.runcount],(int(self.player_x),int(self.player_y)))
            self.runcount +=1
            self.hitbox_running = (self.player_x+30,self.player_y+20,48,70)
            pygame.draw.rect(window,(255,0,0),self.hitbox_running, 2)

        if (keys[pygame.K_DOWN]) or ((keys[pygame.K_DOWN]) and keys[pygame.K_p]):
            if self.player_y == 378:
                self.running = False
                if self.slidecount >= 4:
                    self.slidecount = 0

                if self.sliding:  
                    window.blit(slide[self.slidecount],(int(self.player_x),int(self.player_y)))
                    self.slidecount +=1
                    pygame.draw.rect(window,(255,0,0),self.hitbox_sliding, 2)

        if event.type == pygame.KEYDOWN:
            if (event.key == pygame.K_DOWN )and self.player_y < self.width:

                self.running = False
                self.jumping = False
                self.fallspeed += 0.2

                if self.fallingcount >= 1:
                    self.fallingcount = 0
                if self.fall:
                    window.blit(falling[self.fallingcount], (int(self.player_x),int(self.player_y)))
                    self.hitbox_falling = (self.player_x+30,self.player_y,35,80)
                    pygame.draw.rect(window,(255,0,0),self.hitbox_falling, 2)                    
                    self.fallingcount +=1
        if keys[pygame.K_UP] and keys[pygame.K_p] :
            self.fallspeed = 0.3
            self.running = False
            self.jumping = False
            self.sliding = False
            if self.attackcount >= 16:
                self.attackcount = 0
            if self.attacking: 
                window.blit(attack[self.attackcount],(int(self.player_x),int(self.player_y)))
                self.attackcount += 1
                self.hitbox_attacking = (self.player_x+30,self.player_y+20,38,70)
                self.hitbox_sword = (self.player_x+72, self.player_y+20, 20, 50)
                pygame.draw.rect(window,(255,0,0),self.hitbox_attacking, 2)
                pygame.draw.rect(window,(255,0,0),self.hitbox_sword, 2)
            if self.jumpingcount >= 20:
                self.jumpingcount = 0
            if self.jumping and self.player_y < self.width:  
                window.blit(jump[self.jumpingcount],(int(self.player_x),int(self.player_y)))
                self.hitbox_jumping = (int(self.player_x+20),int(self.player_y+20),52,55)
                pygame.draw.rect(window,(255,0,0),self.hitbox_jumping, 2)
                self.jumpingcount +=1
                self.fallspeed = 0.3

        if keys[pygame.K_UP]:
            self.fallspeed = 0.3
            self.running = False
            if self.jumpingcount >= 20:
                self.jumpingcount = 0

            if self.jumping and self.player_y < self.width:  
                window.blit(jump[self.jumpingcount],(int(self.player_x),int(self.player_y)))
                self.hitbox_jumping = (int(self.player_x+20),int(self.player_y+20),52,55)
                pygame.draw.rect(window,(255,0,0),self.hitbox_jumping, 2)
                self.jumpingcount +=1
                self.fallspeed = 0.3

        if keys[pygame.K_p] and not keys[pygame.K_UP]:
            self.running = False
            self.jumping = False
            self.sliding = False
            if self.attackcount >= 16:
                self.attackcount = 0

            if self.attacking:
                self.hitbox_attacking = (self.player_x+30,self.player_y+20,38,70)
                self.hitbox_sword = (self.player_x+72, self.player_y+20, 20, 50)
                window.blit(attack[self.attackcount],(int(self.player_x),int(self.player_y)))
                self.attackcount += 1
                pygame.draw.rect(window,(255,0,0),self.hitbox_attacking, 2)
                pygame.draw.rect(window,(255,0,0),self.hitbox_sword, 2)
        if keys[pygame.K_DOWN] and keys[pygame.K_UP]:
            self.running = False

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                self.running = True
                self.jumping = True
                self.fallspeed = 0.3

            if event.key == pygame.K_UP:
                self.running=True
            if event.key == pygame.K_p:
                self.running = True
                self.jumping = True
                self.sliding = True

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')]
    for i in range (10):
        walkright[i] = pygame.transform.scale(walkright[i],(70,70))
    for i in range(10):
        walkleft[i] = pygame.transform.scale(walkleft[i],(70,70))

    def __init__(self,enemy_x,enemy_y, end):
        self.x = enemy_x
        self.y = enemy_y             
        self.end = end

        self.path = [self.x,self.end]
        self.walkcount = 0
        self.vel = 7
        self.hitbox = (self.x,self.y-15,90,90)

    def draw_enemy(self,window):
        self.move()
        if self.walkcount+1 >= 33:
            self.walkcount = 0
            self.vel+=0.2
        #walking right
        if self.vel > 0:
            window.blit(self.walkleft[self.walkcount//3], (self.x,self.y-15))
            self.walkcount += 1
        #walking left
        if self.vel < 0:
##            window.blit(self.walkleft[self.walkcount//3], (self.x,self.y))
            self.walkcount -= 1
        if self.x < -100:
            self.x = 1050
        self.hitbox = (self.x+25,self.y-15,41,70)
        pygame.draw.rect(window, (255,0,0),self.hitbox,2)

    def move(self):
        if self.vel > 0:
            #see if position + movement space is < the end, then it is able to move
            if self.x + self.vel < self.path[1]:
                self.x -= self.vel
            #past end point and turns 180
            else:
                self.vel = self.vel*-1
                self.walkcount = 0
        #see if position is smaller than starting position
        else:
            if self.x - self.vel > self.path[0]:
                #vel is already negative
                self.x += self.vel
            else:
               #Truns 180 agian
               self.vel = self.vel* -1
               self.walkcount = 0

class wizard(object):
    walkleft = [pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_05.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_05.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_05.gif'),pygame.image.load('wizard-fly-forward_04.gif'), pygame.image.load('wizard-fly-forward_04.gif'),pygame.image.load('wizard-fly-forward_05.gif')]

    for i in range(16):
        walkleft[i] = pygame.transform.scale(walkleft[i],(70,70))

    def __init__(self,enemy_x,enemy_y, end):
        self.x = enemy_x
        self.y = enemy_y             
        self.end = end

        self.path = [self.x,self.end]
        self.walkcount = 0
        self.vel = 7
        self.hitbox = (self.x,self.y-15,90,90)  

    def draw_enemy(self,window):
        self.move()
        if self.walkcount >= 16:
            self.walkcount = 0
        #walking left
        if self.vel > 0:
            window.blit(self.walkleft[self.walkcount], (self.x,self.y-15))
            self.walkcount += 1
            self.vel+=0.002

        if self.x < -120:
            self.x = 1100
        self.hitbox = (self.x+25,self.y-15,41,70)
        pygame.draw.rect(window, (255,0,0),self.hitbox,2)

    def move(self):
        if self.vel >   0:
            #see if position + movement space is < the end, then it is able to move
            if self.x + self.vel < self.path[1]:
                self.x -= self.vel
            #past end point and turns 180
            else:
                self.vel = self.vel*-1
                self.walkcount = 0
        #see if position is smaller than starting position
        else:
            if self.x - self.vel > self.path[0]:
                #vel is already negative
                self.x += self.vel
            else:
               #Truns 180 agian
               self.vel = self.vel* -1
               self.walkcount = 0

class dragon(object):
    walkleft = [pygame.image.load('dragon_1.gif'), pygame.image.load('dragon_2.gif'), pygame.image.load('dragon_3.gif'), pygame.image.load('dragon_4.gif'), pygame.image.load('dragon_5.gif'), pygame.image.load('dragon_6.gif')]

    for i in range (6):
        walkleft[i] = pygame.transform.scale(walkleft[i],(200,176))
    def __init__(self,enemy_x,enemy_y, end):
        self.x = enemy_x
        self.y = enemy_y             
        self.end = end

        self.path = [self.x,self.end]
        self.walkcount = 0
        self.vel = 7
        self.hitbox = (self.x,self.y-15,90,90)  

    def draw_enemy(self,window):
        self.move()
        if self.walkcount >= 6:
            self.walkcount = 0
        #walking left
        if self.vel > 0:
            window.blit(self.walkleft[self.walkcount], (self.x,300))
            self.walkcount += 1
            self.vel+=0.002

        if self.x < -120:
            self.x = 1100
        self.hitbox = (self.x+25,self.y-15,41,70)
        pygame.draw.rect(window, (255,0,0),self.hitbox,2)

    def move(self):
        if self.vel >   0:
            #see if position + movement space is < the end, then it is able to move
            if self.x + self.vel < self.path[1]:
                self.x -= self.vel
            #past end point and turns 180
            else:
                self.vel = self.vel*-1
                self.walkcount = 0
        #see if position is smaller than starting position
        else:
            if self.x - self.vel > self.path[0]:
                #vel is already negative
                self.x += self.vel
            else:
               #Truns 180 agian
               self.vel = self.vel* -1
               self.walkcount = 0

ghost_list = []
eye_list = []

def keepdrawing():
    window.blit(background, (background_x,0))
    window.blit(background, (background_2,0))

    for draw_ghost in ghost_list:
        draw_ghost.draw(window)
    for draw_eye in eye_list:
        draw_eye.draw(window)

    adventurer.movement(window)
    attacker.draw_enemy(window)
    wizard.draw_enemy(window)
    dragon.draw_enemy(window)

    score_text = score_font.render("score: " + str(score), 1, (0,0,0))
    window.blit(score_text,(470,10))
    pygame.display.update()

score_font = pygame.font.SysFont("Arial",30,True)
attacker = enemy(950,407,951)
adventurer = player(40,387)
wizard = wizard(905,407,951)
dragon = dragon(905,407, 951)
vel_background = 2
pygame.time.set_timer(pygame.USEREVENT+1,random.randint(1000, 2000))
pygame.time.set_timer(pygame.USEREVENT+2,random.randint(2000, 3000))
while run:
    while run_program:
        clock.tick(60)
        background_x -= vel_background
        background_2 -= vel_background
        if background_x < background.get_width() * -1:
            background_x = background.get_width()
        if background_2 < background.get_width() * -1:
            background_2 = background.get_width()
        keys = pygame.key.get_pressed()
        if (adventurer.hitbox_running[0] > attacker.hitbox[0] and adventurer.hitbox_running[0] < attacker.hitbox[0]+70) or (adventurer.hitbox_running[0]+70 > attacker.hitbox[0] and adventurer.hitbox_running[0]+90 < attacker.hitbox[0]+70):
            if  (adventurer.hitbox_running[1] > attacker.hitbox[1] and adventurer.hitbox_running[1] < attacker.hitbox[1]+70) or (adventurer.hitbox_running[1]+90 > attacker.hitbox[1] and adventurer.hitbox_running[1]+90 < attacker.hitbox[1]+70):
                window.fill((255,255,255))
                death()
                pygame.display.update()
                s(0.1)
                print("hit")

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                exit()
            if event.type == pygame.USEREVENT+1:
                vel_background+=0.2
            if event.type == pygame.USEREVENT+2:
                r = random.randint(0,1)
                if r==0:
                    new_eye = eye(1050, 390, 64,64)
                    eye_list.append(new_eye)
                if r==1:
                    new_ghost = ghost(1050,390,64)
                    ghost_list.append(new_ghost)

        if not(adventurer.isJump):

            if keys[pygame.K_UP]:
                adventurer.isJump =True

        else:

            if adventurer.jumpcount >= -10:

                neg=1
                if adventurer.player_y > adventurer.width:
                    adventurer.player_y = adventurer.width
                if adventurer.jumpcount <0:
                    neg = -1
    ##                s(0.01)
                adventurer.player_y -= (adventurer.jumpcount**2)*adventurer.fallspeed*neg*2
    ##            s(0.01)
                adventurer.jumpcount -=1

                if adventurer.player_y > adventurer.width:
                    adventurer.player_y=adventurer.width

            else:
                adventurer.isJump = False
                adventurer.jumpcount = 10
                if adventurer.player_y > adventurer.width:
                    player_y = adventurer.width
                    adventurer.fallspeed = 0.05
        keepdrawing()

The expected results should be that the output should not be shit if the hit boxes are not in contact.


Solution

  • The major issue is that you've different "hitboxis, which have different meanings at different states of the game, but you use adventurer.hitbox_running for the collison test event if adventurer is .running or .falling.

    The best solution would be the set one .hitbox attribute, which always contains the proper location of adventurer in the method player.movement.
    But it is also possible the choose the proper hit box:

    if adventurer.running:
        hitbox = adventurer.hitbox_running
    elif adventurer.jumping:
        hitbox = adventurer.hitbox_jumping
    elif adventurer.falling:
        hitbox = adventurer.hitbox_falling
    

    Further I recommend to use pygame.Rect objects and .colliderect() for the collision test:

    rect_adventurer = pygame.Rect(*hitbox)
    rect_attacker = pygame.Rect(*attacker.hitbox)
    if rect_adventurer.colliderect(rect_attacker):
    
            window.fill((255,255,255))
            death()
            pygame.display.update()
            s(0.1)
            print("hit")