HashDankHog
HashDankHog

Reputation: 59

cant get raycast to work from angles 90 to 270 pygame

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

Answers (1)

Rabbid76
Rabbid76

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

Related Questions