Reputation: 59
Im working on a ray-caster as a project in pygame and i've stumbled upon an issue where the ray casts in the opposite direction from angles 90 to 270. Ive tried making the DDA algorithm subtract the x and y values instead of add them but it does nothing. The ray-cast code starts at line 73, here is the code:
import pygame
import sys
import math
# initialise pygame, set display and clock
WIDTH = 600
HEIGHT = 600
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
#map
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 = WIDTH/len(world)
tile_list = []
for row_num, row in enumerate(world):
for col_num, col in enumerate(row):
if col == 1:
x, y = col_num * TILE, row_num * TILE
rect = pygame.Rect(x, y, TILE, TILE)
tile_list.append(rect)
planeX = 0
planeY = 0.66
dirX = -1
dirY = 0
player_rect = pygame.Rect(60, 60, 30, 30)
vel = 0.5
fps = 60
#sets the origin for all vectors
startpoint = pygame.math.Vector2(player_rect[0] + player_rect[2]/2, player_rect[1] + player_rect[3]/2)
#sets the differnt endpoints for all vectors
endpoint = pygame.math.Vector2(15,0)
fov = 60
angle = 0
angle_rest = angle - fov//2
radian = 0.01745329
lineStartPoint = startpoint
lineEndPoint = endpoint
turnRate = 5
#DDA algorithm/raycasting
def castRay(rayAngle):
slope = math.tan(radian * rayAngle)
COLLIDE = False
x = startpoint[0]
y = startpoint[1]
#loop that adds 1 to x and the slope to y until it collides with a wall
while not COLLIDE and x < WIDTH and x > 0:
x += 1
y += slope
point = pygame.Rect(x,y, 1,1)
for tile in tile_list:
rect = pygame.Rect(tile)
if(point.colliderect(rect)):
COLLIDE = True
#draws line for this loop
pygame.draw.line(screen, (255,0,0), startpoint, (x, y))
x = startpoint[0]
y = startpoint[1]
#loop that adds 1/slope to x and 1 to y until it collides with a wall
while not COLLIDE and y < HEIGHT and y > 0:
x += 1/slope
y += 1
point = pygame.Rect(x,y, 1,1)
for tile in tile_list:
rect = pygame.Rect(tile)
if(point.colliderect(rect)):
COLLIDE = True
pygame.draw.line(screen, (0,0,255), startpoint, (x, y))
while True:
clock.tick(fps)
screen.fill((135, 53, 53))
# get pressed keys
keys = pygame.key.get_pressed()
# delta x and delta y values
dx, dy = 0, 0
startpoint = pygame.math.Vector2(player_rect[0] + player_rect[2]/2, player_rect[1] + player_rect[3]/2)
current_endpoint = startpoint + endpoint.rotate(angle)
lineStartPoint = current_endpoint + endpoint.rotate((angle+90) % 360)
lineEndPoint = current_endpoint - endpoint.rotate((angle+90) % 360)
angle_rest = (angle - fov//2) % 360
#movement
if keys[pygame.K_UP]:
dx -= (startpoint[0] - current_endpoint[0])*vel
dy -= (startpoint[1] - current_endpoint[1])*vel
if keys[pygame.K_DOWN]:
dx += (startpoint[0] - current_endpoint[0])*vel
dy += (startpoint[1] - current_endpoint[1])*vel
if keys[pygame.K_RIGHT]:
angle = (angle+turnRate) % 360
if keys[pygame.K_LEFT]:
angle = (angle-turnRate) % 360
# this is where collision detection starts
x_projection = player_rect.move(dx, 0)
y_projection = player_rect.move(0, dy)
for tile in tile_list:
pygame.draw.rect(screen, (99, 99, 99), tile)
if x_projection.colliderect(tile):
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
player_rect.move_ip(dx, dy)
pygame.draw.rect(screen, (38, 136, 126), player_rect)
pygame.draw.line(screen , (255,0,0), startpoint, current_endpoint)
pygame.draw.line(screen, 255, (lineStartPoint), (lineEndPoint))
castRay(angle)
print(angle)
# remember to update the display
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
Upvotes: 1
Views: 173
Reputation: 210889
If the angle is between 90° and 270°, you must reverse the direction:
def castRay(rayAngle):
slope = math.tan(math.radians(rayAngle))
dx = 1
if rayAngle > 90 and rayAngle < 270:
slope *= -1
dx *= -1
COLLIDE = False
x = startpoint[0]
y = startpoint[1]
#loop that adds 1 to x and the slope to y until it collides with a wall
while not COLLIDE and x < WIDTH and x > 0:
x += dx
y += slope
point = pygame.Rect(x,y, 1,1)
for tile in tile_list:
rect = pygame.Rect(tile)
if(point.colliderect(rect)):
COLLIDE = True
However, I suggest to simplify your code. Compute the direction vector:
dx, dy = math.cos(angleRad), math.sin(angleRad)
The components of the direction vector by the reciprocal maximum component so that the larger component becomes 1:
s = max(abs(dx), abs(dy))
dx /= s
dy /= s
function castRay
:
def castRay(rayAngle):
angleRad = math.radians(rayAngle)
dx, dy = math.cos(angleRad), math.sin(angleRad)
s = max(abs(dx), abs(dy))
dx /= s
dy /= s
COLLIDE = False
x, y = startpoint
while not COLLIDE and 0 <= y < WIDTH and 0 < y < HEIGHT:
x += dx
y += dy
point = pygame.Rect(x,y, 1,1)
for tile in tile_list:
rect = pygame.Rect(tile)
if(point.colliderect(rect)):
COLLIDE = True
pygame.draw.line(screen, (255,0,0), startpoint, (x, y))
Upvotes: 1