1

I have a map im creating in python using pygame with a player and walls:

import pygame


x = 25
y = 19

collision = [
    [0,0,0,37],#vertical
    [4,23,4,27],
    [12,18,12,25],
    [13,0,13,1],
    [13,4,13,7],
    [13,10,13,11],
    [15,13,15,18],
    [15,23,15,37],
    [19,0,19,13],
    [29,25,29,26],
    [29,29,29,37],
    [35,0,35,9],
    [35,12,35,17],
    [35,21,35,26],
    [35,29,35,37],
    [36,17,36,21],
    [44,0,44,6],
    [44,10,44,17],
    [54,0,54,17],
    [0,0,19,0],#horizontal
    [35,0,46,0],
    [52,0,54,0],
    [13,5,19,5],
    [19,6,24,6],
    [30,6,35,6],
    [0,13,10,13],
    [15,13,21,13],
    [25,13,44,13],
    [35,17,36,17],
    [44,17,54,17],
    [35,21,36,21],
    [4,23,12,23],
    [0,25,4,25],
    [19,25,36,25],
    [4,27,12,27],
    [0,37,35,37],
    [14,12,14,12],#dots
    [11,14,11,14]
]

white = (255,255,255)
black = (0,0,0)
red = (255,0,0)

pygame.init()

screen = pygame.display.set_mode((200,200))
pygame.display.set_caption('Collision')
screen.fill(white)

for list in collision:#draw map
    pygame.draw.line(screen,black,(list[0],list[1]),(list[2],list[3]),1)

pygame.draw.line(screen,red,(x,y),(x,y),1)#draw player

pygame.display.update()

while(True):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_w:
                for list in collision:
                    #---START OF EQUATION---#
                    if(list[0]<=list[2] and not(y-1==list[1] and x>=list[0] and x<=list[2])) or (list[0]>list[2] and not(y-1==list[1] and x>=list[2] and x<=list[0])):#updated
                    #---END OF EQUATION---#
                        pygame.draw.line(screen,white,(x,y),(x,y),1)
                        y-=1
                        pygame.draw.line(screen,red,(x,y),(x,y),1)
                        break
            elif event.key == pygame.K_s:
                for list in collision:
                    if(list[0]<=list[2] and not(y+1==list[1] and x>=list[0] and x<=list[2])) or (list[0]>list[2] and not(y+1==list[1] and x>=list[2] and x<=list[0])):#updated
                        pygame.draw.line(screen,white,(x,y),(x,y),1)
                        y+=1
                        pygame.draw.line(screen,red,(x,y),(x,y),1)
                        break
            elif event.key == pygame.K_a:
                for list in collision:
                    if(list[1]<=list[3] and not(x-1==list[0] and y>=list[1] and y<=list[3])) or (list[1]>list[3] and not(x-1==list[0] and y>=list[3] and y<=list[1])):#updated
                        pygame.draw.line(screen,white,(x,y),(x,y),1)
                        x-=1
                        pygame.draw.line(screen,red,(x,y),(x,y),1)
                        break
            elif event.key == pygame.K_d:
                for list in collision:
                    if(list[1]<=list[3] and not(x+1==list[0] and y>=list[1] and y<=list[3])) or (list[1]>list[3] and not(x+1==list[0] and y>=list[3] and y<=list[1])):#updated
                        pygame.draw.line(screen,white,(x,y),(x,y),1)
                        x+=1
                        pygame.draw.line(screen,red,(x,y),(x,y),1)
                        break
            pygame.display.update()

The equation, in an 'easier' to read format:

if (
    list[0]<=list[2]
    and not(

        y-1==list[1]
        and

        x>=list[0]
        and
        x<=list[2])

    )

    or

    (

    list[0]>list[2]
    and not(

        y-1==list[1]
        and

        x>=list[2]
        and
        x<=list[0]

        )

    )

Down where keypresses are being detected is where im having an issue. I have tested this method several times, but it always results in the wrong functionality.

I am having my code loop through a list of 'walls' every time the user presses w/a/s/d, and tests if they are capable of going through that space if no wall is present. Lets say that there is a wall like this here with the red dot being the player. How can I prevent the player from moving up or to the left with modification of the equation?

Equation thats being used to test for collision (is different for every direction, in this case is for the up direction):

if(list[0]<=list[2] and not(y-1==list[1] and x>=list[0] and x<=list[2])) or (list[0]>list[2] and not(y-1==list[1] and x>=list[2] and x<=list[0]))
crazicrafter1
  • 309
  • 5
  • 18
  • 2
    Please go read up on [PEP8](https://www.python.org/dev/peps/pep-0008/). Your code is hard to read, and that is unacceptable for debugging. – Daniel Jan 03 '18 at 18:18
  • PyGame has [pygame.Rect()](http://pygame.org/docs/ref/rect.html) which has functions to check collisions - ie. [colliderect()](http://pygame.org/docs/ref/rect.html#pygame.Rect.colliderect) – furas Jan 03 '18 at 18:53
  • Does colliderect() include collision for values entered within a list? – crazicrafter1 Jan 03 '18 at 19:08
  • why do you check `list[0]<=list[2]` and `list[0]>list[2]` ? Your data should always gives true for `list[0]<=list[2]` – furas Jan 03 '18 at 19:08
  • for future values of registering coordinates, ie. something like (19,5,14,5) wouldnt pass, although 14,5,19,5 is the same line. I wrote all the values left-right, though I just want to keep everything consistent to check everything, just incase my x1 becomes greater than my x2 – crazicrafter1 Jan 03 '18 at 19:13
  • you keep all walls as `Rect(x,y,width, height)` and player also as `Rect(x,y,width, height)` and then you can check `wall_rect.colliderect(player_rect). If you use `Sprite` for every wall and player, then you can keep walls in group `pygame.sprite.Group` and then you can use `pygame.sprite.spritecollideany()` to check collision between player and group. – furas Jan 03 '18 at 19:14
  • better use only `14,5,19,5` and it will make life/code easier. You can always create function which will check walls and replace `19,5,14,5` with `14,5,19,5` – furas Jan 03 '18 at 19:15
  • If I did switch each point value of (x1,y1) and (x2,y2) to satisfy the x1 to be less than x2, I just realized the same problem will be occuring for the y1 and y2, and that I wasn't checking whether the complimentary value of a test was within range either, gonna change that in a bit... – crazicrafter1 Jan 03 '18 at 19:18
  • main problem is that it checks first wall and it may not collidate and then it makes move - but it didn't check other walls which can collidate. You have to check all walls before you can move player. – furas Jan 04 '18 at 10:17

1 Answers1

2

Problem is because it checks first wall and it may not collidate and then it makes move - but it didn't check other walls which can collidate.

You have to check all walls before you can move player.

All moves are almost the same - you use only different x, y to check new position - so I create one function to check it.

def check_collisions(walls, player_new_x, player_new_y):

    for x1, y1, x2, y2 in walls:
        if x1 <= player_new_x <= x2 and y1 <= player_new_y <= y2:
            print("COLLIDE")
            return True

    return False

Full code

import pygame

# --- constants --- (UPPER_CASE_NAMES)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED   = (255, 0, 0)

FPS = 5

# --- functions --- (lower_case_names)

def check_collisions(walls, player_new_x, player_new_y):

    for x1, y1, x2, y2 in walls:
        if x1 <= player_new_x <= x2 and y1 <= player_new_y <= y2:
            print("COLLIDE")
            return True

    return False

# --- main ---

collision = [
    [0,0,0,37],#vertical
    [4,23,4,27],
    [12,18,12,25],
    [13,0,13,1],
    [13,4,13,7],
    [13,10,13,11],
    [15,13,15,18],
    [15,23,15,37],
    [19,0,19,13],
    [29,25,29,26],
    [29,29,29,37],
    [35,0,35,9],
    [35,12,35,17],
    [35,21,35,26],
    [35,29,35,37],
    [36,17,36,21],
    [44,0,44,6],
    [44,10,44,17],
    [54,0,54,17],
    [0,0,19,0],#horizontal
    [35,0,46,0],
    [52,0,54,0],
    [13,5,19,5],
    [19,6,24,6],
    [30,6,35,6],
    [0,13,10,13],
    [15,13,21,13],
    [25,13,44,13],
    [35,17,36,17],
    [44,17,54,17],
    [35,21,36,21],
    [4,23,12,23],
    [0,25,4,25],
    [19,25,36,25],
    [4,27,12,27],
    [0,37,35,37],
    [14,12,14,12],#dots
    [11,14,11,14]
]

x = 25
y = 19


# - init -

pygame.init()

screen = pygame.display.set_mode((200,200))
screen.fill(WHITE)

# - draws -

for item in collision:
    pygame.draw.line(screen, BLACK, item[:2], item[2:], 1)

pygame.draw.line(screen, RED, (x, y), (x, y), 1)

pygame.display.update()

# - mainloop -

clock = pygame.time.Clock()

while True:

    moved = False

    # - events -

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

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_w:
                new_x = x
                new_y = y - 1

                collide = check_collisions(collision, new_x, new_y)

                if not collide:
                    pygame.draw.line(screen, WHITE, (x, y), (x, y), 1)
                    x = new_x
                    y = new_y
                    pygame.draw.line(screen, RED, (x, y), (x, y), 1)

            elif event.key == pygame.K_s:
                new_x = x
                new_y = y + 1

                collide = check_collisions(collision, new_x, new_y)

                if not collide:
                    pygame.draw.line(screen, WHITE, (x, y), (x, y), 1)
                    x = new_x
                    y = new_y
                    pygame.draw.line(screen, RED, (x, y), (x, y), 1)

            elif event.key == pygame.K_a:
                new_x = x - 1
                new_y = y

                collide = check_collisions(collision, new_x, new_y)

                if not collide:
                    pygame.draw.line(screen, WHITE, (x, y), (x, y), 1)
                    x = new_x
                    y = new_y
                    pygame.draw.line(screen, RED, (x, y), (x, y), 1)

            elif event.key == pygame.K_d:
                new_x = x + 1
                new_y = y

                collide = check_collisions(collision, new_x, new_y)

                if not collide:
                    pygame.draw.line(screen, WHITE, (x, y), (x, y), 1)
                    x = new_x
                    y = new_y
                    pygame.draw.line(screen, RED, (x, y), (x, y), 1)

    pygame.display.update()
    clock.tick(FPS) # to display less frame and use less CPU

EDIT: version in which you can keep key pressed and it will move - you don't have to press many times

import pygame

# --- constants --- (UPPER_CASE_NAMES)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED   = (255, 0, 0)

FPS = 5

# --- functions --- (lower_case_names)

def check_collisions(walls, player_new_x, player_new_y):

    for x1, y1, x2, y2 in walls:
        if x1 <= player_new_x <= x2 and y1 <= player_new_y <= y2:
            print("COLLIDE")
            return True

    return False

# --- main ---

collision = [
    [0,0,0,37],#vertical
    [4,23,4,27],
    [12,18,12,25],
    [13,0,13,1],
    [13,4,13,7],
    [13,10,13,11],
    [15,13,15,18],
    [15,23,15,37],
    [19,0,19,13],
    [29,25,29,26],
    [29,29,29,37],
    [35,0,35,9],
    [35,12,35,17],
    [35,21,35,26],
    [35,29,35,37],
    [36,17,36,21],
    [44,0,44,6],
    [44,10,44,17],
    [54,0,54,17],
    [0,0,19,0],#horizontal
    [35,0,46,0],
    [52,0,54,0],
    [13,5,19,5],
    [19,6,24,6],
    [30,6,35,6],
    [0,13,10,13],
    [15,13,21,13],
    [25,13,44,13],
    [35,17,36,17],
    [44,17,54,17],
    [35,21,36,21],
    [4,23,12,23],
    [0,25,4,25],
    [19,25,36,25],
    [4,27,12,27],
    [0,37,35,37],
    [14,12,14,12],#dots
    [11,14,11,14]
]

x = 25
y = 19


# - init -

pygame.init()

screen = pygame.display.set_mode((200,200))
screen.fill(WHITE)

# - draws -

for item in collision:
    pygame.draw.line(screen, BLACK, item[:2], item[2:], 1)

pygame.draw.line(screen, RED, (x, y), (x, y), 1)

pygame.display.update()

# - mainloop -

clock = pygame.time.Clock()

move_x = 0
move_y = 0

while True:

    # - events -

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

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_w:
                move_y = -1

            elif event.key == pygame.K_s:
                move_y = 1

            elif event.key == pygame.K_a:
                move_x = -1

            elif event.key == pygame.K_d:
                move_x = 1

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_w:
                move_y = 0

            elif event.key == pygame.K_s:
                move_y = 0

            elif event.key == pygame.K_a:
                move_x = 0

            elif event.key == pygame.K_d:
                move_x = 0

    # - update -

    if move_x != 0 or move_y != 0:

        new_x = x + move_x
        new_y = y + move_y

        collide = check_collisions(collision, new_x, new_y)

        if not collide:
            pygame.draw.line(screen, WHITE, (x, y), (x, y), 1)
            x = new_x
            y = new_y
            pygame.draw.line(screen, RED, (x, y), (x, y), 1)


    pygame.display.update()
    clock.tick(FPS) # to display less frame and use less CPU
furas
  • 134,197
  • 12
  • 106
  • 148