raycasting.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import pygame as pg
  2. import math
  3. from settings import *
  4. class RayCasting:
  5. def __init__(self, game):
  6. self.game = game
  7. self.ray_casting_result = []
  8. self.objects_to_render = []
  9. self.textures = self.game.object_renderer.wall_textures
  10. def get_objects_to_render(self):
  11. self.objects_to_render = []
  12. for ray, values in enumerate(self.ray_casting_result):
  13. depth, proj_height, texture, offset = values
  14. if proj_height < HEIGHT:
  15. wall_column = self.textures[texture].subsurface(
  16. offset * (TEXTURE_SIZE - SCALE), 0, SCALE, TEXTURE_SIZE
  17. )
  18. wall_column = pg.transform.scale(wall_column, (SCALE, int(proj_height)))
  19. wall_pos = (ray * SCALE, HALF_HEIGHT - proj_height // 2)
  20. else:
  21. texture_height = TEXTURE_SIZE * HEIGHT / proj_height
  22. wall_column = self.textures[texture].subsurface(
  23. offset * (TEXTURE_SIZE - SCALE), HALF_TEXTURE_SIZE - texture_height // 2,
  24. SCALE, texture_height
  25. )
  26. wall_column = pg.transform.scale(wall_column, (SCALE, HEIGHT))
  27. wall_pos = (ray * SCALE, 0)
  28. self.objects_to_render.append((depth, wall_column, wall_pos))
  29. def ray_cast(self):
  30. self.ray_casting_result = []
  31. texture_vert, texture_hor = 1, 1
  32. ox, oy = self.game.player.pos
  33. x_map, y_map = self.game.player.map_pos
  34. ray_angle = self.game.player.angle - HALF_FOV + 0.0001
  35. for ray in range(NUM_RAYS):
  36. sin_a = math.sin(ray_angle)
  37. cos_a = math.cos(ray_angle)
  38. # horizontals
  39. y_hor, dy = (y_map + 1, 1) if sin_a > 0 else (y_map - 1e-6, -1)
  40. depth_hor = (y_hor - oy) / sin_a
  41. x_hor = ox + depth_hor * cos_a
  42. delta_depth = dy / sin_a
  43. dx = delta_depth * cos_a
  44. for i in range(MAX_DEPTH):
  45. tile_hor = int(x_hor), int(y_hor)
  46. if tile_hor in self.game.map.world_map:
  47. texture_hor = self.game.map.world_map[tile_hor]
  48. break
  49. x_hor += dx
  50. y_hor += dy
  51. depth_hor += delta_depth
  52. # verticals
  53. x_vert, dx = (x_map + 1, 1) if cos_a > 0 else (x_map - 1e-6, -1)
  54. depth_vert = (x_vert - ox) / cos_a
  55. y_vert = oy + depth_vert * sin_a
  56. delta_depth = dx / cos_a
  57. dy = delta_depth * sin_a
  58. for i in range(MAX_DEPTH):
  59. tile_vert = int(x_vert), int(y_vert)
  60. if tile_vert in self.game.map.world_map:
  61. texture_vert = self.game.map.world_map[tile_vert]
  62. break
  63. x_vert += dx
  64. y_vert += dy
  65. depth_vert += delta_depth
  66. # depth, texture offset
  67. if depth_vert < depth_hor:
  68. depth, texture = depth_vert, texture_vert
  69. y_vert %= 1
  70. offset = y_vert if cos_a > 0 else (1 - y_vert)
  71. else:
  72. depth, texture = depth_hor, texture_hor
  73. x_hor %= 1
  74. offset = (1 - x_hor) if sin_a > 0 else x_hor
  75. # remove fishbowl effect
  76. depth *= math.cos(self.game.player.angle - ray_angle)
  77. # projection
  78. proj_height = SCREEN_DIST / (depth + 0.0001)
  79. # ray casting result
  80. self.ray_casting_result.append((depth, proj_height, texture, offset))
  81. ray_angle += DELTA_ANGLE
  82. def update(self):
  83. self.ray_cast()
  84. self.get_objects_to_render()