\n
Complete example:
\n\nimport pygame\n\n#sets up the screen\npygame.init()\nscreen = pygame.display.set_mode((800, 600))\npygame.display.set_caption("collision test")\n\n#defines the size and coords for the player\nwall_height, wall_width = 200, 20\nwall_x, wall_y = 400, 300\n\nlast_key = 0\nx = 400\ny = 240\nwidth = 40\nheight = 60\nvel = 5\n\n#where the main code is run\nrun = True\nwhile run:\n pygame.time.delay(50)\n\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n run = False\n #movement code\n keys = pygame.key.get_pressed() \n if keys[pygame.K_LEFT] and x > vel:\n last_key = "left"\n x -= vel\n if keys[pygame.K_RIGHT] and x < 800 - width:\n last_key = "right"\n x += vel\n if keys[pygame.K_UP] and y > vel:\n last_key = "up"\n y -= vel\n if keys[pygame.K_DOWN] and y < 600 - height - vel:\n last_key = "down"\n y += vel\n\n wall = pygame.Rect(wall_x, wall_y, wall_width, wall_height)\n player = pygame.Rect(x, y, width, height)\n\n #collision code\n if player.colliderect(wall) and last_key == "left":\n x += vel\n x = wall.right\n elif player.colliderect(wall) and last_key == "right":\n x -= vel\n x = wall.left - width\n elif player.colliderect(wall) and last_key == "up":\n y += vel\n y = wall.bottom\n elif player.colliderect(wall) and last_key == "down":\n y -= vel\n y = wall.top - height\n\n #draws the player\n screen.fill((0,0,0))\n pygame.draw.rect(screen, (244, 247, 30), (wall_x, wall_y, wall_width, wall_height))\n pygame.draw.rect(screen, (255, 255, 255), (x, y, width, height)) \n pygame.display.update()\n\npygame.quit()\n
\n","author":{"@type":"Person","name":"Rabbid76"},"upvoteCount":1}}}Reputation: 25
I am trying to make a top down game and I need functional walls for it to work, i have tried it multiple times but when i hit two directions at once the player phases through the wall. the system i have now is supposed to cancel out any velocity the player has by subtracting/adding it to the current (x, y) coordinates but it doesn't work, i have also tried making it so there is supposed to be no reaction when you press the button but that breaks the code too.
import pygame
import random
#sets up the screen
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("collision test")
#defines the size and coords for the player
wall_height, wall_width = 200, 20
wall_x, wall_y = 400, 300
last_key = 0
x = 400
y = 300
width = 40
height = 60
vel = 5
#where the main code is run
run = True
while run:
pygame.time.delay(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#movement code
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel:
last_key = "left"
x -= vel
if keys[pygame.K_RIGHT] and x < 800 - width:
last_key = "right"
x += vel
if keys[pygame.K_UP] and y > vel:
last_key = "up"
y -= vel
if keys[pygame.K_DOWN] and y < 600 - height - vel:
last_key = "down"
y += vel
#draws the player
screen.fill((0,0,0))
wall = pygame.draw.rect(screen, (244, 247, 30), (wall_x, wall_y, wall_width, wall_height))
player = pygame.draw.rect(screen, (255, 255, 255), (x, y, width, height))
#collision code
if player.colliderect(wall) and last_key == "left":
x += vel
elif player.colliderect(wall) and last_key == "right":
x -= vel
elif player.colliderect(wall) and last_key == "up":
y += vel
elif player.colliderect(wall) and last_key == "down":
y -= vel
pygame.display.update()
pygame.quit()
Upvotes: 1
Views: 203
Reputation: 211258
I'll give you an complete answer with minimal changes to your code.
Change the order. Do the collision detection before the drawing. Create pygame.Rect
objects:
wall = pygame.Rect(wall_x, wall_y, wall_width, wall_height)
player = pygame.Rect(x, y, width, height)
Restrict the position of the player to the boundaries of the wall:
if player.colliderect(wall) and last_key == "left":
x += vel
x = wall.right
elif player.colliderect(wall) and last_key == "right":
x -= vel
x = wall.left - width
elif player.colliderect(wall) and last_key == "up":
y += vel
y = wall.bottom
elif player.colliderect(wall) and last_key == "down":
y -= vel
y = wall.top - height
Complete example:
import pygame
#sets up the screen
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("collision test")
#defines the size and coords for the player
wall_height, wall_width = 200, 20
wall_x, wall_y = 400, 300
last_key = 0
x = 400
y = 240
width = 40
height = 60
vel = 5
#where the main code is run
run = True
while run:
pygame.time.delay(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#movement code
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel:
last_key = "left"
x -= vel
if keys[pygame.K_RIGHT] and x < 800 - width:
last_key = "right"
x += vel
if keys[pygame.K_UP] and y > vel:
last_key = "up"
y -= vel
if keys[pygame.K_DOWN] and y < 600 - height - vel:
last_key = "down"
y += vel
wall = pygame.Rect(wall_x, wall_y, wall_width, wall_height)
player = pygame.Rect(x, y, width, height)
#collision code
if player.colliderect(wall) and last_key == "left":
x += vel
x = wall.right
elif player.colliderect(wall) and last_key == "right":
x -= vel
x = wall.left - width
elif player.colliderect(wall) and last_key == "up":
y += vel
y = wall.bottom
elif player.colliderect(wall) and last_key == "down":
y -= vel
y = wall.top - height
#draws the player
screen.fill((0,0,0))
pygame.draw.rect(screen, (244, 247, 30), (wall_x, wall_y, wall_width, wall_height))
pygame.draw.rect(screen, (255, 255, 255), (x, y, width, height))
pygame.display.update()
pygame.quit()
Upvotes: 1
Reputation: 6166
Here is a solution, it uses built in collision detection methods and it is easier to "create/build" the world, just change sth in the matrix (explanation is in code comments (without comments this is around only 80 lines of code)):
# some useless comments too in the sense that they explain what code does
# import pygame
import pygame
# initialise pygame, set display and clock
pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
# constant for tile size, this size is because screen is 600x600 pixels,
# there are 12 tiles per column per row so each tile gets displayed
TILE = 50
# world matrix, this is where you create the world layout of tiles,
# if you want to have thinner walls, increase the row and grid count
# and decrease the tile size
world = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
# tile list which will contain all the valid tiles (rectangles)
# you could append also the number and then change the number
# in the world matrix to apply some different textures
tile_list = []
for row_num, row in enumerate(world):
for col_num, col in enumerate(row):
# if the current tile is 1 in the matrix then calculate
# its position and append the rectangular area to the tile_list
if col == 1:
x, y = col_num * TILE, row_num * TILE
rect = pygame.Rect(x, y, TILE, TILE)
tile_list.append(rect)
# player stuff, like player's rectangle to allow for
# easier collision detection
player_rect = pygame.Rect(60, 60, 30, 30)
vel = 3
fps = 60
while True:
clock.tick(fps)
screen.fill((0, 0, 0))
# get pressed keys
keys = pygame.key.get_pressed()
# delta x and delta y values that allow to move
# player in either direction, they are like
# a helper variable since
# they don't directly affect player movement thus
# allowing for additional checks like collision detection
dx, dy = 0, 0
# the basic key stuff, the reason to reduce instead of set the
# value is in case both keys are pressed so that the player stays in place
# basically makes both keys equivalent
if keys[pygame.K_UP]:
dy -= vel
if keys[pygame.K_DOWN]:
dy += vel
if keys[pygame.K_RIGHT]:
dx += vel
if keys[pygame.K_LEFT]:
dx -= vel
# this is where collision detection starts
# first I create two projections of where the player might be
# depending on what keys they pressed, `.move()` returns a new
# rectangle allowing to easily apply `.colliderect() method
x_projection = player_rect.move(dx, 0)
y_projection = player_rect.move(0, dy)
# now for each placed tile check against collision
for tile in tile_list:
# also draw the rectangle here too
pygame.draw.rect(screen, (255, 255, 0), tile)
# can add the border
# pygame.draw.rect(screen, (255, 255, 255), tile, width=1)
# `pygame.Rect` has a built-in method for checking the collision
if x_projection.colliderect(tile):
# depending on player movement calculate delta x
# so that the player can touch the wall and there
# wouldn't be any gaps between player and wall
# the same logic for checking
# y collision
if dx > 0:
dx = tile.left - player_rect.right
elif dx < 0:
dx = tile.right - player_rect.left
elif y_projection.colliderect(tile):
if dy > 0:
dy = tile.top - player_rect.bottom
elif dy < 0:
dy = tile.bottom - player_rect.top
# move the player's rectangle in place meaning
# it moves the actual rectangle instead of returning
# an new one as `.move` would do
player_rect.move_ip(dx, dy)
# draw the player cube on screen
pygame.draw.rect(screen, (255, 0, 0), player_rect)
# can also draw border
# pygame.draw.rect(screen, (0, 0, 255), player_rect, width=1)
# event checking
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
# remember to update the display
pygame.display.update()
Sources:
If you have any questions, be sure to ask them!
Upvotes: 0
Reputation: 31
so you are basically throwing velocity in the opposite direction if the player collides with the wall which isnt a bad thought
I would recommend either putting a rect (rectangle) around the wall and if you collide with that rect then your velocity is == 0
alternatively you can import math and through a math equation for finding distance between two object like, then when you are within a certain range you set vel ==0
distance = math.sqrt((math.pow(x - wall_x, 2)) + (math.pow(y - wally, 2)))
if distance < 20:
vel = 0
naturally the distance of 20 was just a guess that could be tweaked
Upvotes: 0