Jump to content

Collision Detection - Pygame (Python)

So, for one of my projects in my class we have to make a 2d game with a custom collision detection. I am having some issues with this and was wondering if anyone can help me
 
 
While my code currently works fine if you run into a platform on the right, I am having issues
Code for collision, each # in the level string represents a 50x50 square, 0 is empty, 1 is platform

 

My main issue isn't the collision itself, it is the fact that when I go to the right and hit a platform I stop, but if I go to the left I can go through the first 50x50 platform but I stop when I hit the second:

 

For example even though I have:

    111

It acts like I have:

    110
 

lvl_1 = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100000000000000111''' lvl = lvl_1 def collision(): #collision detection    global playerx, playery, lvl, yVel, xVel    clvl = lvl.replace('\n', '') #remove the new lines to count the characters in a string    def numRound(x, roundNum = 50): #round to nearest 50 function        return int(roundNum * round(float(x)/roundNum))    if playerx <= 0: playerx = 0     elif playerx+50 >= 1000: playerx = 950    if playery <= 0:                                        #<== This section stops player from going off the screen        playery = 0        yVel = -1    elif playery+50 >= 600: playery = 550     playerPos = numRound(playerx+24)/50 + (numRound(playery)/50*20) #the position of the player's nearest 50x50 box    if clvl[playerPos] == '1':        print xVel        if xVel > 0:            xVel = -4        elif xVel < 0:            xVel = 4 

import pygame, sys, time, randomfrom pygame.locals import * pygame.mixer.pre_init(44100, -16, 2, 2048) #sound buffer pygame.init() scrn = pygame.display.set_mode((1000, 600)) basicFont = pygame.font.SysFont(None, 22)songChoice = 0 #used to select songs muteImg = pygame.image.load('mute.png')  #Muted speaker imagebBg = pygame.image.load('bBackground.png').convert() #blue backgroundrBg = pygame.image.load('rBackground.png').convert() #red blackgroundgBg = pygame.image.load('gBackground.png').convert() #green backgroundplayerImg = pygame.image.load('player.png').convert() #persongoalPlat = pygame.image.load('goal.png').convert() #level goal#platformsplatPic = pygame.image.load('plat.png').convert()#Clear platforms (to be displayed when they are not on the specific color)clRPlat = pygame.image.load('clRPlat.png').convert_alpha()clGPlat = pygame.image.load('clGPlat.png').convert_alpha()clBPlat = pygame.image.load('clBPlat.png').convert_alpha() bGround = 'red' #checks background colormute = False #Check to see if they mute the sound plvl = int(open('save.txt', 'r').read()) #the current level (read the save #player position infoplayerx = 400playery = 400yVel = 0 #velocityxVel = 0 #x velocitygravity = .25 #gravity pullfriction = 1 class Sprite(pygame.sprite.Sprite):#sprite class for the player    def __init__(self,(x,y), scrn):        self.x = x        self.y = y        self.image = playerImg        self.rect = self.image.get_rect()#size of the player image        self.pos = (self.x,self.y)    def display(self,(x,y)):        scrn.blit(self.image,(x,y))        def makeSound():    global songChoice    if mute == False:        songs = ['celestial_fantasy', 'laser_nights', 'parago', 'invast'] #list of songs        song = songs[songChoice] + '.ogg' #choose a song        songChoice += 1 #After getting song, set it to go to next one        if songChoice > 3: #If songChoice index is too high lower it CHANGE THIS IF YOU ADD SONGS            songChoice -= 4        pygame.mixer.music.set_volume(.25)        pygame.mixer.music.load(song)        pygame.mixer.music.play()    if mute == True:        pygame.mixer.music.stop()        def commands(): #display commands    #commands each are a dif variable so that I can move them freely later on    cJump = basicFont.render("W/Space - Jump", False, (255, 255, 255))    cLeft = basicFont.render('A - Left', False, (255, 255, 255))    cRight = basicFont.render('D - Right', False, (255, 255, 255))    cRed = basicFont.render('J - Red', False, (255, 255, 255))    cGreen = basicFont.render('K - Green', False, (255, 255, 255))    cBlue = basicFont.render('L - Blue', False, (255, 255, 255))    cMute = basicFont.render('M - Mute', False, (255, 255, 255))    cSave = basicFont.render('I - Save', False, (255, 255, 255))    #display them    scrn.blit(cJump, (0,0))    scrn.blit(cLeft, (150,0))    scrn.blit(cRight, (250,0))    scrn.blit(cRed, (375,0))    scrn.blit(cGreen, (500,0))    scrn.blit(cBlue, (625,0))    scrn.blit(cMute, (750,0))    scrn.blit(cSave, (875, 0)) # 1 is red, 2 is green, 3 is blue, 0 is nothing, 4 is the goal, 5 is where you start # 18 #'s up# 24 #'s wide #start game w/ red background#Display text to explain each level as we go blank_lvl = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000''' lvl_1 = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100000000000000111''' lvl_12 = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000111005000000000011100000111100011110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000''' lvl_2 = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000040111022222211111111100000000000000000000''' lvl_3 = '''050000000000000000001111111111111111111122222222222222222222000000000000000000001111111111111111111100000000000000000000222222222222222222220000000000000000000033333333333333333303000000000000000000000000000004000000000011111111111111111111''' lvl_4 = '''000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000003333333333300000222222222222222050000000000000000001111111111111111111100000000000000000000''' lvl_5 = '''000000000000000000000000000000000000040000000000000000111111000033333330000000002200000000000000000000000000000000000000111110000000000000000000002222220000000000000000000000000000000000000000333330001111022222220000000000000000000000000000''' if plvl == 1: lvl = lvl_1elif plvl == 2: lvl = lvl_2elif plvl == 3: lvl = lvl_3 #Level if statmentselif plvl == 4: lvl = lvl_4elif plvl == 5: lvl = lvl_5 def lvlBuild(): #make the lvl    global lvl #Random boolean which is changed later    blvl = lvl.replace('\n', '') #remove the extra lines    pos = 0    for p in blvl:        yPos = int(pos/20)*50        xPos = int(pos%20)*50        if p == '1':            if bGround == 'red':                scrn.blit(platPic, (xPos, yPos))            else:                scrn.blit(clRPlat, (xPos, yPos))        if p == '2':            if bGround == 'green':                scrn.blit(platPic, (xPos, yPos))            else:                scrn.blit(clGPlat, (xPos, yPos))        if p == '3':            if bGround == 'blue':                scrn.blit(platPic, (xPos, yPos))            else:                scrn.blit(clBPlat, (xPos, yPos))        if p == '4':            scrn.blit(goalPlat, (xPos, yPos))        pos += 1 def collision(): #collision detection    global playerx, playery, lvl, yVel, xVel    clvl = lvl.replace('\n', '')    def numRound(x, roundNum = 50):        return int(roundNum * round(float(x)/roundNum))    if playerx <= 0: playerx = 0    elif playerx+50 >= 1000: playerx = 950    if playery <= 0:        playery = 0        yVel = -1    elif playery+50 >= 600: playery = 550     playerPos = numRound(playerx+24)/50 + (numRound(playery)/50*20) #the position of the player's nearest 50x50, number used to look through the level string    if clvl[playerPos] == '1':        print xVel        if xVel >= 0:            xVel = -4        elif xVel < 0:            xVel = 4def main():    global mute, bGround, playerx, playery, yVel, gravity, xVel    pygame.display.set_caption('Color-Hue by Quincy Els')    scrn.blit(rBg, (0,0))    keepGoing = True    makeSound()    player = Sprite((playerx,playery),scrn) #Player's sprite    while keepGoing:        collision() #check for collision         #--DISPLAY SECTION--        if bGround == 'red': scrn.blit(rBg, (0,0))        elif bGround == 'green': scrn.blit(gBg, (0,0))        elif bGround == 'blue': scrn.blit(bBg, (0,0))           if mute == True: scrn.blit(muteImg, (975, 0))        lvlBuild()        commands()        player.display((playerx, playery)) #display player         #--GRAVITY AND FRICTION SECTION--        yVel -= gravity #gravity pulls the player        if xVel > 0:  xVel -= friction #friction stops the player        elif xVel < 0: xVel += friction        if yVel < -10: yVel = -10        elif yVel > 30: yVel = 30 #This is the limits for velocity        if xVel < -10:  xVel = -10        elif xVel > 10: xVel = 10        playery -= yVel #y velocity edits the players location        playerx += xVel         #--KEY CHECK SECTION--        keys = pygame.key.get_pressed()         if keys[pygame.K_s]: yVel -= 5        if keys[pygame.K_d]: xVel += 3        elif keys[pygame.K_a]: xVel -= 3        for event in pygame.event.get():            if event.type == QUIT:                keepGoing = False            if event.type == KEYDOWN:                if event.key == K_w or event.key == K_SPACE: #Jump (put down here because they can only single jump                    if mute == False:                        jumpSound = pygame.mixer.Sound('jump.ogg')                        jumpSound.play() #if they aren't muted play sound                    yVel += 20.5                if event.key == 27: #27 is the escape key, you can also do K_ESC                    keepGoing = False                if event.key == K_j: #Red                    scrn.blit(rBg, (0,0))                    bGround = 'red'                if event.key == K_k: #Green                    scrn.blit(gBg, (0,0))                    bGround = 'green'                if event.key == K_l: #Blue                    scrn.blit(bBg, (0,0))                    bGround = 'blue'                if event.key == K_m: #Mute                    if mute == False: mute = True                    elif mute == True: mute = False                    makeSound()                if event.key == K_i:                    open('filename.txt', 'w').write(plvl)            if mute == True: scrn.blit(muteImg, (975, 0))            pygame.display.update()                    if pygame.mixer.music.get_busy == False and mute == False: #If music is not playing but is should be            makeSound() #get a new song to play        pygame.display.update()     collision()    pygame.quit() if __name__ == '__main__':    main()    #2d platformer, different color platforms, you must change the#background color to use a platform        

Intel 3570K - MSI GTX 660Ti 3GB OC Edition - 16GB Corsair LP RAM - ASRock Extreme4 Motherboard - Corsair HX850 - Adata Premier Pro SP900 120GB SSD with Windows 7 - Seagate Barracuda 1TD HDD - Seagate Barracuda 500GB HDD - Thermaltake Frio CPU Cooler - CM Storm Enforcer Case - Macbook Pro Early 2011 Laptop

Link to comment
https://linustechtips.com/topic/100095-collision-detection-pygame-python/
Share on other sites

Link to post
Share on other sites

Thank you for properly formatting...it made it a lot easier to read :D

 

Anyways, I have a bit of concern on this line

playerPos = numRound(playerx+24)/50 + (numRound(playery)/50*20)

There are actually a few things about this line that I want to address.

The first is adding 24, I assume you did this and it made colliding with a wall on the right work...this creates a nasty bug though, being at point 999 you will loop yourself "down" to the next row since you have 1023 as an x value now :P

The second thing, and I am not sure if was intended, but you are rounding to the nearest 50, so if you are at posx 25 you will be classified in slot 1 of the map (not sure if this was intended)

The third thing is imo numRound is a fairly useless function as it stands.  I would recommend making a function like coordToWorldCoord (and I would suggest not rounding...although if your code does require rounding then add it in)

    def coordToWorldCoord(x, y, roundNum = 50):        return int(float(x)/roundNum) + int(float(y)/roundNum)

The final thing is, this means you can collide with an object and stop...but you will then get stuck.  For this I would recommend simulating a move before checking for a collision...given your collision function is forcing the velocity you need to do it in that function (fyi I would usually suggest separating collision and velocity code...but you could ignore if you want).  Actually you should really stop both x and y velocity at this point :P

 

So given that one line I would suggest this instead

playerPos = coordToWorldCoord(playerx+velx, playery+vely)

This will make sure that the player doesn't get moved into a problem area...also you do need to set velx and vely to 0 if the level has a 1 (or your method of reversing the velocity...so add in the lines for vely)

0b10111010 10101101 11110000 00001101

Link to post
Share on other sites

Thank you for properly formatting...it made it a lot easier to read :D

 

Anyways, I have a bit of concern on this line

playerPos = numRound(playerx+24)/50 + (numRound(playery)/50*20)

There are actually a few things about this line that I want to address.

The first is adding 24, I assume you did this and it made colliding with a wall on the right work...this creates a nasty bug though, being at point 999 you will loop yourself "down" to the next row since you have 1023 as an x value now :P

The second thing, and I am not sure if was intended, but you are rounding to the nearest 50, so if you are at posx 25 you will be classified in slot 1 of the map (not sure if this was intended)

The third thing is imo numRound is a fairly useless function as it stands.  I would recommend making a function like coordToWorldCoord (and I would suggest not rounding...although if your code does require rounding then add it in)

    def coordToWorldCoord(x, y, roundNum = 50):        return int(float(x)/roundNum) + int(float(y)/roundNum)

The final thing is, this means you can collide with an object and stop...but you will then get stuck.  For this I would recommend simulating a move before checking for a collision...given your collision function is forcing the velocity you need to do it in that function (fyi I would usually suggest separating collision and velocity code...but you could ignore if you want).  Actually you should really stop both x and y velocity at this point :P

 

So given that one line I would suggest this instead

playerPos = coordToWorldCoord(playerx+velx, playery+vely)

This will make sure that the player doesn't get moved into a problem area...also you do need to set velx and vely to 0 if the level has a 1 (or your method of reversing the velocity...so add in the lines for vely)

That you for your help, I am implementing this in now :)

Intel 3570K - MSI GTX 660Ti 3GB OC Edition - 16GB Corsair LP RAM - ASRock Extreme4 Motherboard - Corsair HX850 - Adata Premier Pro SP900 120GB SSD with Windows 7 - Seagate Barracuda 1TD HDD - Seagate Barracuda 500GB HDD - Thermaltake Frio CPU Cooler - CM Storm Enforcer Case - Macbook Pro Early 2011 Laptop

Link to post
Share on other sites

Thank you for properly formatting...it made it a lot easier to read :D

 

Anyways, I have a bit of concern on this line

playerPos = numRound(playerx+24)/50 + (numRound(playery)/50*20)

There are actually a few things about this line that I want to address.

The first is adding 24, I assume you did this and it made colliding with a wall on the right work...this creates a nasty bug though, being at point 999 you will loop yourself "down" to the next row since you have 1023 as an x value now :P

The second thing, and I am not sure if was intended, but you are rounding to the nearest 50, so if you are at posx 25 you will be classified in slot 1 of the map (not sure if this was intended)

The third thing is imo numRound is a fairly useless function as it stands.  I would recommend making a function like coordToWorldCoord (and I would suggest not rounding...although if your code does require rounding then add it in)

    def coordToWorldCoord(x, y, roundNum = 50):        return int(float(x)/roundNum) + int(float(y)/roundNum)

The final thing is, this means you can collide with an object and stop...but you will then get stuck.  For this I would recommend simulating a move before checking for a collision...given your collision function is forcing the velocity you need to do it in that function (fyi I would usually suggest separating collision and velocity code...but you could ignore if you want).  Actually you should really stop both x and y velocity at this point :P

 

So given that one line I would suggest this instead

playerPos = coordToWorldCoord(playerx+velx, playery+vely)

This will make sure that the player doesn't get moved into a problem area...also you do need to set velx and vely to 0 if the level has a 1 (or your method of reversing the velocity...so add in the lines for vely)

The only issue I am having with this is that it adds the two together resulting in repeats:

 

for example if I am here (I am the #):

0#0

000

 

I returns a 1, however it will also return a 1 if I am here (because of the y value):

000

#00

 

Sorry about the graphics with 0's and #'s, but I wasn't sure how best to describe it :P

 

So I am not sure how I would check if there is a block where I am if my player's location is 1 even if they are on the second line

Intel 3570K - MSI GTX 660Ti 3GB OC Edition - 16GB Corsair LP RAM - ASRock Extreme4 Motherboard - Corsair HX850 - Adata Premier Pro SP900 120GB SSD with Windows 7 - Seagate Barracuda 1TD HDD - Seagate Barracuda 500GB HDD - Thermaltake Frio CPU Cooler - CM Storm Enforcer Case - Macbook Pro Early 2011 Laptop

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×