Jump to content

Collision Detection - Pygame (Python)

aikoels

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
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 comment
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)

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 comment
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)

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 comment
Share on other sites

Link to post
Share on other sites

Oh, my bad...I missed the multiplier :P

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

The "* 20" was what I missed

0b10111010 10101101 11110000 00001101

Link to comment
Share on other sites

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

×