Skip to content

Instantly share code, notes, and snippets.

@paulsonkoly
Created July 9, 2020 06:55
Show Gist options
  • Save paulsonkoly/02b527e4a511965f2301751b13a3f734 to your computer and use it in GitHub Desktop.
Save paulsonkoly/02b527e4a511965f2301751b13a3f734 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'gosu'
def linear_map(value, mina, maxa, minb, maxb)
(maxb - minb) * (value - mina) / (maxa - mina) + minb
end
RESOLUTON = 64
module HasPosition
attr_reader :position
def move_to(position)
@position = position
end
end
module HasDirection
attr_reader :direction
def turn_by(rad)
@direction += rad
end
end
module Arrow
include HasPosition
include HasDirection
def point_ahead
Vector2D.new(Math.cos(direction) * 2 + position.x,
Math.sin(direction) * 2 + position.y)
end
def point_behind
Vector2D.new(- Math.cos(direction) * 2 + position.x,
- Math.sin(direction) * 2 + position.y)
end
end
class Vector2D
def initialize(x, y)
@x = x
@y = y
end
attr_reader :x, :y
def distance_from(other)
Math::sqrt((other.x - x) ** 2 + (other.y - y) ** 2)
end
end
class Wall
def initialize
@x0, @y0, @x1, @y1 = 4.times.map { linear_map rand, 0, 1, 0, 400 }
end
attr_reader :x0, :y0, :x1, :y1
def draw
Gosu.draw_line(@x0, @y0, Gosu::Color::WHITE, @x1, @y1, Gosu::Color::WHITE, 0)
end
end
class Ray
include Arrow
def initialize(position, direction)
@position = position
@direction = direction
end
def draw(walls, camera, ix)
hits = walls.map { |wall| cast(wall) }.compact
closest = hits.min_by { |point| point.distance_from(position) }
if closest
Gosu.draw_line(position.x, position.y, Gosu::Color::WHITE,
closest.x, closest.y, Gosu::Color::WHITE)
distance = closest.distance_from(position)
distance *= Math.cos(direction - camera)
width = 400 / RESOLUTON
height = linear_map distance, 0, 400, 400, 50
y_offset = (400 - height) / 2
shade = linear_map(distance, 0, 400, 255, 0)
Gosu.draw_rect(400 + ix * width,
y_offset,
width,
height,
Gosu::Color.rgba(shade, shade, shade, 255))
end
end
private
attr_reader :wall
def cast(wall)
@wall = wall
denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom
u = ((y1 - y2) * (x1 - x3) - (x1 - x2) * (y1 - y3)) / denom
if 0 <= u && t <= 0 && u <= 1.0 then
Vector2D.new(x1 + t * (x2 - x1), y1 + t * (y2 - y1))
end
end
def x1; position.x; end
def y1; position.y; end
def x2; point_behind.x; end
def y2; point_behind.y; end
def x3; wall.x0; end
def y3; wall.y0; end
def x4; wall.x1; end
def y4; wall.y1; end
end
class Camera
include Arrow
def initialize(position, direction, number_of_rays: RESOLUTON)
@position = position
@direction = direction
@rays = number_of_rays.times.map do |ix|
direction = linear_map ix, 0, number_of_rays - 1, - Math::PI / 4, Math::PI / 4
Ray.new(position, direction)
end
end
attr_reader :direction
def move_to(position)
super
@rays.each { |ray| ray.move_to position }
end
def turn_by(rad)
super
@rays.each { |ray| ray.turn_by rad }
end
def draw(walls)
@rays.each_with_index { |ray, ix| ray.draw(walls, direction, ix) }
end
end
class X < Gosu::Window
def initialize
super 800, 400
self.caption = 'whops'
@walls = 8.times.map { Wall.new }
@camera = Camera.new(Vector2D.new(200, 200), 0)
@pressed = []
end
def update
@pressed.each do |key|
case key
when Gosu::KB_W
@camera.move_to(@camera.point_ahead)
when Gosu::KB_S
@camera.move_to(@camera.point_behind)
when Gosu::KB_A
@camera.turn_by(- Math::PI / 32)
when Gosu::KB_D
@camera.turn_by(Math::PI / 32)
end
end
end
def draw
@walls.each(&:draw)
@camera.draw(@walls)
end
def button_down(id)
if [Gosu::KB_A, Gosu::KB_S, Gosu::KB_D, Gosu::KB_W].include? id
@pressed = @pressed | [id]
end
end
def button_up(id)
@pressed = @pressed - [id]
end
end
X.new.show
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment