Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save EncodeTheCode/5ffde254891795cad150a1fbe79c7543 to your computer and use it in GitHub Desktop.
Save EncodeTheCode/5ffde254891795cad150a1fbe79c7543 to your computer and use it in GitHub Desktop.
import pygame
import numpy as np
import sys
import math
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("3D Line of Sight with Mouse Look")
clock = pygame.time.Clock()
pygame.event.set_grab(True)
pygame.mouse.set_visible(False)
# Constants
WIDTH, HEIGHT = screen.get_size()
FOV = math.radians(45)
MOVE_SPEED = 3.0
MOUSE_SENSITIVITY = 0.001
max_danger = 100.0
danger_rate = 20.0
danger_cooldown = 80.0
BAR_ALPHA = 150
font = pygame.font.SysFont(None, 24)
# Colors
WHITE = (255, 255, 255)
GRAY = (180, 180, 180)
DARK_GRAY = (80, 80, 80)
ORANGE = (255, 165, 0)
BLACK = (0, 0, 0)
flashing_color = [(133,40,32), (136,41,31), (138,38,33), (130,39,31)]
def get_flashing_color():
return flashing_color[(pygame.time.get_ticks() // 500) % len(flashing_color)]
# State
camera_pos = np.array([0, 1, -5], dtype=np.float32)
yaw = 0.0
pitch = 0.0
danger = 0.0
blink = False
blink_timer = 1.5
enemy_pos = np.array([0, 1, 8], dtype=np.float32)
wall_pos = np.array([0, 1.5, 4], dtype=np.float32)
wall_size = np.array([4, 3, 0.5], dtype=np.float32)
def normalize(v):
norm = np.linalg.norm(v)
return v / norm if norm > 0 else v
def project_point(point, cam_pos, yaw, pitch):
rel = point - cam_pos
cos_y, sin_y = np.cos(yaw), np.sin(yaw)
cos_p, sin_p = np.cos(pitch), np.sin(pitch)
x = rel[0] * cos_y - rel[2] * sin_y
z = rel[0] * sin_y + rel[2] * cos_y
y = rel[1]
y2 = y * cos_p - z * sin_p
z2 = y * sin_p + z * cos_p
z2 = max(z2, 0.01)
f = HEIGHT / (2 * math.tan(FOV / 2))
x_proj = (x * f) / z2 + WIDTH / 2
y_proj = (-y2 * f) / z2 + HEIGHT / 2
return int(x_proj), int(y_proj), z2
def draw_box(center, size, color, cam_pos, yaw, pitch, force_draw=False):
x, y, z = center
w, h, d = size
offsets = np.array([
[-w/2, -h/2, -d/2], [ w/2, -h/2, -d/2], [ w/2, h/2, -d/2], [-w/2, h/2, -d/2],
[-w/2, -h/2, d/2], [ w/2, -h/2, d/2], [ w/2, h/2, d/2], [-w/2, h/2, d/2],
])
corners = offsets + center
projected = []
for c in corners:
try:
projected.append(project_point(c, cam_pos, yaw, pitch))
except:
projected.append((0, 0, -1))
screen_pts = [(x, y) for x, y, z in projected if z > 0.1]
if len(screen_pts) < 3 and not force_draw:
return
faces = [[0,1,2,3], [4,5,6,7], [0,1,5,4], [2,3,7,6], [1,2,6,5], [0,3,7,4]]
for face in faces:
pts = [projected[i] for i in face]
pts2d = [(p[0], p[1]) for p in pts]
pygame.draw.polygon(screen, color, pts2d)
def draw_floor(y, spacing, cam_pos, yaw, pitch):
cam_x, _, cam_z = cam_pos
radius = 19
min_x = int(cam_x) - radius
max_x = int(cam_x) + radius
min_z = int(cam_z) - radius
max_z = int(cam_z) + radius
for x in range(min_x, max_x + 1):
for z in range(min_z, max_z + 1):
p1 = np.array([x, y, z], dtype=np.float32)
p2 = np.array([x + spacing, y, z], dtype=np.float32)
p3 = np.array([x, y, z + spacing], dtype=np.float32)
try:
p1p = project_point(p1, cam_pos, yaw, pitch)
p2p = project_point(p2, cam_pos, yaw, pitch)
p3p = project_point(p3, cam_pos, yaw, pitch)
pygame.draw.line(screen, GRAY, p1p[:2], p2p[:2])
pygame.draw.line(screen, GRAY, p1p[:2], p3p[:2])
except:
continue
def line_intersects_box(start, end, box_pos, box_size):
dir = end - start
t_min, t_max = -np.inf, np.inf
for i in range(3):
box_min = box_pos[i] - box_size[i] / 2
box_max = box_pos[i] + box_size[i] / 2
if abs(dir[i]) < 1e-6:
if start[i] < box_min or start[i] > box_max:
return False
else:
t1 = (box_min - start[i]) / dir[i]
t2 = (box_max - start[i]) / dir[i]
t1, t2 = min(t1, t2), max(t1, t2)
t_min = max(t_min, t1)
t_max = min(t_max, t2)
if t_max < t_min:
return False
return True
# Main loop
running = True
while running:
dt = clock.tick(60) / 1000.0
SEL_COLOR = get_flashing_color()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
mx, my = pygame.mouse.get_rel()
yaw += mx * MOUSE_SENSITIVITY
pitch += my * MOUSE_SENSITIVITY
pitch = np.clip(pitch, -np.pi/2 + 0.01, np.pi/2 - 0.01)
keys = pygame.key.get_pressed()
move = np.zeros(3, dtype=np.float32)
forward = np.array([np.sin(yaw), 0, np.cos(yaw)], dtype=np.float32)
right = np.array([np.cos(yaw), 0, -np.sin(yaw)], dtype=np.float32)
if keys[pygame.K_w]: move += forward
if keys[pygame.K_s]: move -= forward
if keys[pygame.K_a]: move -= right
if keys[pygame.K_d]: move += right
if keys[pygame.K_q]: move[1] -= 1
if keys[pygame.K_e]: move[1] += 1
if np.linalg.norm(move) > 0:
move = normalize(move) * MOVE_SPEED * dt
camera_pos += move
enemy_forward = normalize(np.array([0, 0, -1], dtype=np.float32))
to_player = normalize(camera_pos - enemy_pos)
dot = np.dot(enemy_forward, to_player)
enemy_fov_check = dot > -0.5
enemy_has_los = not line_intersects_box(enemy_pos, camera_pos, wall_pos, wall_size)
if enemy_fov_check and enemy_has_los:
if danger == 0.0:
danger = 11.67
elif danger == 11.67:
danger = danger
danger = min(max_danger, danger + danger_rate * dt)
else:
danger = max(0, danger - danger_cooldown * dt)
if danger >= max_danger:
blink_timer += dt
if blink_timer >= 0.5:
blink_timer = 0
blink = not blink
else:
blink = False
blink_timer = 0
screen.fill(BLACK)
draw_floor(0, 1, camera_pos, yaw, pitch)
# Always draw wall and enemy if within 15-tile radius
if np.linalg.norm(camera_pos - wall_pos) <= 75:
draw_box(wall_pos, wall_size, DARK_GRAY, camera_pos, yaw, pitch, force_draw=True)
if np.linalg.norm(camera_pos - enemy_pos) <= 75:
draw_box(enemy_pos, (1, 2, 1), ORANGE, camera_pos, yaw, pitch, force_draw=True)
bar_x, bar_y = 10, 40
bar_w, bar_h = 200, 16
fill_w = int(bar_w * (danger / max_danger))
if danger > 0 and (danger < max_danger or blink):
bar_rect = pygame.Rect(bar_x, bar_y, fill_w, bar_h)
pygame.draw.rect(screen, SEL_COLOR, bar_rect)
screen.blit(font.render("Danger", True, WHITE), (bar_x, bar_y - 20))
pygame.display.flip()
pygame.quit()
sys.exit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment