import pygame as pg import math from settings import * class RayCasting(): def __init__(self, game): self.game = game self.ray_casting_result = [] self.objects_to_render = [] self.textures = self.game.object_renderer.wall_textures def get_objects_to_render(self): self.objects_to_render = [] for ray, values in enumerate(self.ray_casting_result): depth, proj_height, texture, offset = values if proj_height < HEIGHT: wall_column = self.textures[texture].subsurface( offset * (TEXTURE_SIZE - SCALE), 0, SCALE, TEXTURE_SIZE ) wall_column = pg.transform.scale(wall_column, (SCALE, proj_height)) wall_pos = (ray * SCALE, HALF_HEIGHT - proj_height // 2) else: texture_height = TEXTURE_SIZE * HEIGHT / proj_height wall_column = self.textures[texture].subsurface( offset * (TEXTURE_SIZE - SCALE), HALF_TEXTURE_SIZE - texture_height // 2, SCALE, texture_height ) wall_column = pg.transform.scale(wall_column, (SCALE, HEIGHT)) wall_pos = (ray * SCALE, 0) self.objects_to_render.append((depth, wall_column, wall_pos)) def ray_cast(self): self.ray_casting_result = [] ox, oy = self.game.player.pos x_map, y_map = self.game.player.map_pos texture_vert, texture_horz = 1, 1 ray_angle = self.game.player.angle - HALF_FOV + 0.0001 # 0.0001 to avoid division by zero for ray in range(NUM_RAYS): sin_a = math.sin(ray_angle) cos_a = math.cos(ray_angle) # horizontal y_horz, dy = (y_map + 1, 1) if sin_a > 0 else (y_map - 1e-6, -1) depth_horz = (y_horz - oy) / sin_a x_horz = ox + depth_horz * cos_a delta_depth = dy / sin_a dx = delta_depth * cos_a for i in range(MAX_DEPTH): tile_horz = int(x_horz), int(y_horz) if tile_horz in self.game.map.world_map: texture_horz = self.game.map.world_map[tile_horz] break x_horz += dx y_horz += dy depth_horz += delta_depth # verticals x_vert, dx = (x_map + 1, 1) if cos_a > 0 else (x_map - 1e-6, -1) depth_vert = (x_vert - ox) / cos_a y_vert = oy + depth_vert * sin_a delta_depth = dx / cos_a dy = delta_depth * sin_a for i in range(MAX_DEPTH): tile_vert = int(x_vert), int(y_vert) if tile_vert in self.game.map.world_map: texture_vert = self.game.map.world_map[tile_vert] break x_vert += dx y_vert += dy depth_vert += delta_depth # Select the shortest distance to the wall, and the corresponding texture if depth_vert < depth_horz: depth, texture = depth_vert, texture_vert y_vert %= 1 offset = y_vert if cos_a > 0 else (1 - y_vert) else: depth, texture = depth_horz, texture_horz x_horz %= 1 offset = (1 - x_horz) if sin_a > 0 else x_horz # Draw the ray for debugging if DEBUG_MODE: pg.draw.line(self.game.screen, 'yellow', (ox * 100, oy * 100), (100 * ox + 100 * depth * cos_a, 100 * oy + 100 * depth * sin_a), 1) # Remove fish-eye effect if FISH_EYE_FIX: depth *= math.cos(self.game.player.angle - ray_angle) # Projection proj_height = SCREEN_DIST / (depth + 0.0001) # 0.0001 to avoid division by zero # Draw walls #colour = [255 / (1 + depth ** 5 * 0.0002)] * 3 #pg.draw.rect(self.game.screen, colour, # (ray * SCALE, HALF_HEIGHT - proj_height // 2, SCALE, proj_height)) # Ray casting result self.ray_casting_result.append((depth, proj_height, texture, offset)) ray_angle += DELTA_ANGLE def update(self): self.ray_cast() self.get_objects_to_render()