2

So I started a new project where I am tying to imitate this. It is a pseudo 3d world where a ray in cast in a 2d world and the farther away the ray travels, the smaller the walls, giving the illusion of 3d. I stared by writing rays. The way I thought of doing this is:

  1. Initialize a whole load of rays going from player's angle - player's FOV/2 to player's angle + player's FOV/2. This is basically the player field of view.
  2. Shoot the rays in the specified direction. However, my problem is that that rays are too slow and I cant think of anything to make it go faster.

Here is the code:

import pygame
from math import sin, cos, tan, radians
pygame.init()

SW = 1200
SH = 600
WIN = pygame.display
D = WIN.set_mode((SW, SH))

class Ray:
    def __init__(self, x, y, theta):
        self.x = x
        self.y = y
        self.theta = theta

    # draw only for debugging 
    def draw(self):
        pygame.draw.circle(D, (255, 255, 0), (int(self.x), int(self.y)), 1)

class Player:
    def __init__(self, x, y):
        self.fov = 1.5
        self.x = x
        self.y = y
        self.theta = 0

player = Player(600, 300)

class Sim:
    def __init__(self):
        self.rays = []
        #lower end is 
        lowerEnd = player.theta - player.fov/2
        previous = 0
        # fov is 1.5 rads(90 degrees), so
        # i want there to be 900 rays
        # in 90 degrees so that every pixel
        # is covered (90/0.1) becasue rays
        # seem to diverge as they get farther
        # away
        while len(self.rays) < 900:
            previous += radians(0.1)# 0.1 in radians
            self.rays.append(Ray(player.x, player.y, lowerEnd + previous))

sim = Sim()
            
while True:
    #D.fill(0)
    keys = pygame.key.get_pressed()
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            pygame.quit()

    #print(self.rays[0].x, self.rays[0].y)
    for ray in sim.rays:
        ray.x += cos(ray.theta) 
        ray.y -= sin(ray.theta) 
        ray.draw()
        
    pygame.display.flip()

Running the code, you will be able to see that that all the pixels are covered, but it almost takes 2 seconds for the rays to get from the centre to the edge. I could force the rays to travel faster by skipping pixels, but that may bring unwanted problems later.

for ray in self.rays:
    ray.x += cos(ray.theta) * 20
    ray.y -= sin(ray.theta) * 20

I suspect that it might be faster when the rays are not drawn, but I cant think of a way to test this because printing the position values would also make it slow. Thanks for any help.

  • 1
    I have to tell you again that you need to switch to an implementation that uses the GPU. – Rabbid76 Sep 27 '20 at 21:22
  • 1
    if you think that drawing a ray slows your application, try printing some of debug info into a memory buffer. Flush the buffer at the end. Still, IMO, the textual debug information does not fit with graphical simulation. – user14063792468 Sep 27 '20 at 21:25
  • 1
    The application is slown down, because of drawing the 900 circles in each frame. However, these circles are only drawn for debugging purposes (`# draw only for debugging`). – Rabbid76 Sep 27 '20 at 21:29
  • @Rabbid Yea, I am looking into doing what yvw suggested for seeing how fast the program will run when rays are not being drawn. I am not familiar with memory buffer so I am having some problems. If I test it and it doesn't make much difference, then looks like I have no choice but to switch. It will be a whole new learning all over again –  Sep 27 '20 at 21:35
  • Raytracing doesn't seem like a good fit for an interpreted language like Python — and this is an example of the reason why… – martineau Sep 27 '20 at 22:15
  • I’m not sure what actual optimizations are or were in practical use for this type of game, but I am confident that solving for intersections with equations is faster than tracing them out frame by frame until they hit something. Deconstruct grid squares along the path into lines and find the first one that intersects. – Ry- Sep 27 '20 at 23:40
  • @hippozhipos If you remove the drawing, then you still do the event handling and a buffer swap (`pygame.display.flip()`) between frames. – Rabbid76 Sep 28 '20 at 15:06
  • @Rabbid76 I don't know what that means –  Sep 28 '20 at 15:10
  • 1
    @hippozhipos in a productive scenario you would have 2 nested loops, without any event handling or `pygame.display.flip()` in between, isn't it? – Rabbid76 Sep 28 '20 at 15:13
  • `pygame.display.flip()` yes but wouldn't the window crash without event handling?, unless there is a thread for handeling events –  Sep 28 '20 at 15:15

0 Answers0