-
-
Save paulsonkoly/02b527e4a511965f2301751b13a3f734 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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