0
\$\begingroup\$

Recently I have been attempting to develop a ray caster in python pygame. I managed to get it to work fairly efficiently when using basic colors, but when I altered the program to use textures instead, it led to many performance issues, even when using low resolutions. So I am curious to know how I can optimize my code so that it runs faster.

I would also like it if somebody could review the code normally so that I can improve it further in other ways.

My code:

import pygame
import math
import random


def main():
    world_map = []
    # Read automap data from file
    with open("automap") as f:
        world_map = f.read().split("\n")

   
    # Initialize Pygame
    pygame.init()

    # Set up the display
    width, height = 1800, 1000
    screen = pygame.display.set_mode((width, height))
    clock = pygame.time.Clock()



  
    # Textures
    textures = {
        '1': pygame.image.load('1.png').convert(),
        '2': pygame.image.load('2.png').convert()
    }

    

    # Player variables
    player_pos = [1.5, 1.5]
    player_dir = [1.0, 0.0]
    player_plane = [0.0, 0.66]

    # Mouse sensitivity
    mouse_sensitivity = 0.02

    # Movement speed
    move_speed = 0.05

    # Raycasting function
    def raycast(screen, player_pos, player_dir, player_plane, world_map, textures, width, height, column_width):
        for x in range(width):
            camera_x = 2 * x / width - 1

            #direction of ray
            ray_dir_x = player_dir[0] + player_plane[0] * camera_x
            ray_dir_y = player_dir[1] + player_plane[1] * camera_x


            #map pos
            map_x = int(player_pos[0])
            map_y = int(player_pos[1])

            #length of ray
            delta_dist_x = abs(1 / ray_dir_x)
            delta_dist_y = abs(1 / (ray_dir_y + 1e-10))  # adds a small epsilon value to avoid division by zero(for future reference you have no idea what that means  but google said it would fix your issue)

            #step direction and inital distance
            if ray_dir_x < 0:
                step_x = -1
                side_dist_x = (player_pos[0] - map_x) * delta_dist_x
            else:
                step_x = 1
                side_dist_x = (map_x + 1.0 - player_pos[0]) * delta_dist_x

            if ray_dir_y < 0:
                step_y = -1
                side_dist_y = (player_pos[1] - map_y) * delta_dist_y
            else:
                step_y = 1
                side_dist_y = (map_y + 1.0 - player_pos[1]) * delta_dist_y


            #dda algorithm??
            hit = False
            side = None

            while not hit:
                if side_dist_x < side_dist_y:
                    side_dist_x += delta_dist_x
                    map_x += step_x
                    side = 0
                else:
                    side_dist_y += delta_dist_y
                    map_y += step_y
                    side = 1

                if world_map[map_y][map_x] != '0':
                    hit = True


            # Calculate distance projected on camera direction 
            if side == 0:
                perp_wall_dist = (map_x - player_pos[0] + (1 - step_x) / 2) / ray_dir_x
            else:
                perp_wall_dist = (map_y - player_pos[1] + (1 - step_y) / 2) / ray_dir_y

            

            #calculate line height
            line_height = int(height / perp_wall_dist)

            
            
            draw_start = int(-line_height // 2 + height // 2)
            if draw_start < 0:
                draw_start = 0
            draw_end = int(line_height // 2 + height // 2) 
            if draw_end >= height:
                draw_end = height - 1

            wall_texture = textures[world_map[map_y][map_x]]

            #adjusts texture x coord based on wall side and ray direction
            if side == 0:
                wall_x = player_pos[1] + perp_wall_dist * ray_dir_y
            else:
                wall_x = player_pos[0] + perp_wall_dist * ray_dir_x
            wall_x -= math.floor(wall_x)

            tex_x = int(wall_x * wall_texture.get_width())

            if side == 0 and ray_dir_x > 0:
                tex_x = wall_texture.get_width() - tex_x - 1
            if side == 1 and ray_dir_y < 0:
                tex_x = wall_texture.get_width() - tex_x - 1

                
            #draws the texture column on screen
            tex_column = pygame.transform.scale(
                wall_texture.subsurface((tex_x, 0, 1, wall_texture.get_height())), (column_width, line_height))

            screen.blit(tex_column, (x, draw_start))

            

    # Game loop
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        # Get mouse movement
        mouse_movement = pygame.mouse.get_rel()
        mouse_x = mouse_movement[0] * mouse_sensitivity

        # Rotate player direction
        old_dir_x = player_dir[0]
        player_dir[0] = player_dir[0] * math.cos(mouse_x) - player_dir[1] * math.sin(mouse_x)
        player_dir[1] = old_dir_x * math.sin(mouse_x) + player_dir[1] * math.cos(mouse_x)
        old_plane_x = player_plane[0]
        player_plane[0] = player_plane[0] * math.cos(mouse_x) - player_plane[1] * math.sin(mouse_x)
        player_plane[1] = old_plane_x * math.sin(mouse_x) + player_plane[1] * math.cos(mouse_x)

        # Handle keyboard input for movement
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:
            new_pos_x = player_pos[0] + player_dir[0] * move_speed
            new_pos_y = player_pos[1] + player_dir[1] * move_speed
            if world_map[int(new_pos_y)][int(new_pos_x)] == '0':
                player_pos[0] = new_pos_x
                player_pos[1] = new_pos_y
        if keys[pygame.K_s]:
            new_pos_x = player_pos[0] - player_dir[0] * move_speed
            new_pos_y = player_pos[1] - player_dir[1] * move_speed
            if world_map[int(new_pos_y)][int(new_pos_x)] == '0':
                player_pos[0] = new_pos_x
                player_pos[1] = new_pos_y
        if keys[pygame.K_a]:
            new_pos_x = player_pos[0] - player_plane[0] * move_speed
            new_pos_y = player_pos[1] - player_plane[1] * move_speed
            if world_map[int(new_pos_y)][int(new_pos_x)] == '0':
                player_pos[0] = new_pos_x
                player_pos[1] = new_pos_y
        if keys[pygame.K_d]:
            new_pos_x = player_pos[0] + player_plane[0] * move_speed
            new_pos_y = player_pos[1] + player_plane[1] * move_speed
            if world_map[int(new_pos_y)][int(new_pos_x)] == '0':
                player_pos[0] = new_pos_x
                player_pos[1] = new_pos_y


        screen.fill((128,128,128))        #fills floor
        screen.fill((80,80,80), (0, 0, screen.get_width(), screen.get_height()//2))  #fills sky

        

        # Raycasting
        raycast(screen, player_pos, player_dir, player_plane, world_map, textures, width, height, 2)

        # Update the display
        pygame.display.flip()
        clock.tick(60)
        fps = clock.get_fps()
        fps_text = f"Ray Caster 1.7 - FPS: {int(fps)}"
        pygame.display.set_caption(fps_text)

    # Quit the game
    pygame.quit()
if __name__ == "__main__":
    main()

automap is text file containing:

111111111111
100000000001,
100000000201,
100000000001,
100201000001,
111111111111,

And these are the images (1.png, 2.png): 1.png

2.png

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.