Pygame Texture Ray Caster

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:

# 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 = {
}

# 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):