[Python] How do I fix wall warping in my raycaster?

Discussão em 'Python' iniciado por Stack, Fevereiro 15, 2025.

  1. Stack

    Stack Membro Participativo

    So I have made a raycaster in Python using pygame. I cast my rays little bit by bit and once they've hit a wall, I calculate the distance and use that base the height of my walls. The problem is that my walls are curved.

    I had thought I had fixed this problem by adding this

    angle = math.radians(self.view - degree)
    dist *= math.cos(angle)


    But with that the walls to the left or right of me would still be curved.

    Here is the full code or you can play it here!

    #Import modules
    import pygame
    import math

    #Set up window
    pygame.init()

    winWidth, winHeight = (1024, 512)
    window = pygame.display.set_mode((winWidth, winHeight))

    #Make player object
    class Player:
    def __init__(self, x, y, tm, fov, view):
    self.x = x
    self.y = y
    self.tm = tm #tilemap
    self.fov = fov #field of view
    self.view = view #players angle
    self.movements = {'w': False, 'a':False, 's':False, 'd':False}
    self.distances = []
    self.img = pygame.image.load("tile_test.png").convert_alpha()
    self.img = pygame.transform.scale(self.img, (64, 64))

    def update(self): #Player movements
    radAngle = math.radians(self.view)
    if self.movements['w'] == True and self.tm[int(self.y+(math.sin(radAngle)*0.05))][int(self.x+(math.cos(radAngle)*0.05))] == 0:
    self.x += math.cos(radAngle)*0.05
    self.y += math.sin(radAngle)*0.05
    if self.movements['a'] == True:
    self.view -= 3
    if self.movements['d'] == True:
    self.view += 3

    def draw(self, window):
    #---TopDown View---
    #Map
    for y, row in enumerate(self.tm):
    for x, tile in enumerate(row):
    if tile == 1:
    pygame.draw.rect(window, (255,255,255), (x*64, y*64, 64, 64))
    pygame.draw.rect(window, (0,0,0), (x*64, y*64, 64, 64), 1)
    else:
    pygame.draw.rect(window, (0,0,0), (x*64, y*64, 64, 64))
    pygame.draw.rect(window, (255,255,255), (x*64, y*64, 64, 64), 1)
    #player
    pygame.draw.circle(window, (255,255,0), (self.x*64, self.y*64), 8)
    #Rays :D
    self.distances = []
    for degree in range(int(self.view-(self.fov/2)), int(self.view+(self.fov/2))):
    radAngle = math.radians(degree)
    rayx = self.x
    rayy = self.y

    stop = False
    while self.tm[int(rayy)][int(rayx)] == 0 and stop == False:
    rayx += math.cos(radAngle)*0.01
    rayy += math.sin(radAngle)*0.01

    #Calculate ray distance
    dist = math.sqrt(((rayx-self.x)*(rayx-self.x)+(rayy-self.y)*(rayy-self.y)))
    #Draw the ray
    pygame.draw.line(window, (0,255,0), (self.x*64, self.y*64), (rayx*64, rayy*64))


    #Decide if colides horizontally or vertically (To help with drawing tiles)
    rx = round(rayx - int(rayx), 5)
    ry = round(rayy - int(rayy), 5)
    h_col = False
    if rx > .5:
    if ry > .5 - (rx - .5) and ry < .5 + (rx - .5):
    h_col = True
    else:
    h_col = False
    elif rx <= .5:
    if ry > .5 - (.5 - rx) and ry < .5 + (.5 - rx):
    h_col = True
    else:
    h_col = False

    if h_col == True:
    num = ry
    else:
    num = rx

    #Attempt at fixing curved walls. Works somewhat but not really
    angle = math.radians(self.view - degree)
    dist *= math.cos(angle)

    self.distances.append((dist, num))
    #draw player view ray
    pygame.draw.line(window, (255,0,0), (self.x*64, self.y*64), ((self.x+math.cos(math.radians(self.view)))*64, (self.y+math.sin(math.radians(self.view)))*64))
    #---3D View---
    for x, line in enumerate(self.distances):
    height = 256 - round(line[0], 1)*42
    if height <= .5:
    height = .5
    w, h = self.img.get_width(), self.img.get_height()

    img_x = int(line[1]*w)
    if img_x > 63:
    img_x = 63
    elif img_x < 0:
    img_x = 0
    img = self.img.subsurface(img_x, 0, 1, h)
    img = pygame.transform.scale(img, (8, height*2))

    window.blit(img, (512+(x*8), 256-height))


    class Control:
    def __init__(self):
    self.run = True
    self.clock = pygame.time.Clock()

    def update(self):
    for event in pygame.event.get():
    if event.type == pygame.QUIT:
    self.run = False
    if event.type == pygame.KEYDOWN:
    if event.key == pygame.K_w:
    player.movements["w"] = True
    if event.key == pygame.K_a:
    player.movements["a"] = True
    if event.key == pygame.K_s:
    player.movements["s"] = True
    if event.key == pygame.K_d:
    player.movements["d"] = True
    if event.key == pygame.K_ESCAPE:
    self.run = False
    elif event.type == pygame.KEYUP:
    if event.key == pygame.K_w:
    player.movements["w"] = False
    if event.key == pygame.K_a:
    player.movements["a"] = False
    if event.key == pygame.K_s:
    player.movements["s"] = False
    if event.key == pygame.K_d:
    player.movements["d"] = False

    game = Control()
    tm = [
    [1,1,1,1,1,1,1,1],
    [1,0,1,0,0,0,0,1],
    [1,0,1,1,0,1,0,1],
    [1,0,0,0,0,1,0,1],
    [1,1,1,1,0,1,0,1],
    [1,0,1,1,0,0,0,1],
    [1,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1]
    ]

    player = Player(3.5, 3.5, tm, 64, 0)

    while game.run:
    window.fill((50,50,50))
    player.draw(window)

    pygame.display.update()
    game.update()
    player.update()

    game.clock.tick(30)

    pygame.quit()

    Continue reading...

Compartilhe esta Página