Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Created January 7, 2020 19:47
Show Gist options
  • Save amirrajan/9dcc5d3d2e62cfe4b301ee4b1d874c36 to your computer and use it in GitHub Desktop.
Save amirrajan/9dcc5d3d2e62cfe4b301ee4b1d874c36 to your computer and use it in GitHub Desktop.
DragonRuby ray casting.
class Raycasting
# there is a class macro that automatically
# wires up your class to serve up inputs, outputs,
# state etc
attr_gtk
def tick
defaults
update
render
end
def defaults
state.lines = []
# these days, most ruby devs use snake_cased variable names
# as opposed to camelCased
state.screen_width ||= 160
state.screen_height ||= 90
state.scale_x ||= 1280.fdiv(state.screen_width)
state.scale_y ||= 720.fdiv(state.screen_height)
state.map_width ||= 24
state.map_height ||= 24
# make it pretty yo, ascii art yay!
state.world_map ||= parse_map <<-v
111111111111111111111111
1 1
1 1
1 1
1 22222 3 3 3 1
1 2 2 1
1 2 2 3 3 1
1 2 2 1
1 22 22 3 3 3 1
1 1
1 1
1 1
1 1
1 1
1 1
1 1
144444444 1
14 4 4 1
14 5 4 1
14 4 4 1
14 444444 1
14 1
144444444 1
111111111111111111111111
v
state.pos_x ||= 22
state.pos_y ||= 12
state.dir_x ||= -1
state.dir_y ||= 0
state.plane_x ||= 0
state.plane_y ||= 0.66
state.mov_speed ||= 0.14
state.rot_speed ||= 0.02
end
def parse_map s
s.each_line.map do |l|
l.strip.each_char.map do |c|
if c == ' '
0
else
c.to_i
end
end
end
end
def rotate_point x, y, magnitude
# this formula signifies "rotate point around origin"
[x * Math.cos(-magnitude) - y * Math.sin(-magnitude),
x * Math.sin(-magnitude) + y * Math.cos(-magnitude)]
end
def update
if inputs.keyboard.key_held.w
if state.world_map[(state.pos_x + state.dir_x * state.mov_speed).floor][state.pos_y.floor] == 0
state.pos_x += state.dir_x * state.mov_speed
end
if state.world_map[state.pos_x.floor][(state.pos_y + state.dir_y * state.mov_speed).floor] == 0
state.pos_y += state.dir_y * state.mov_speed
end
end
if inputs.keyboard.key_held.s
if state.world_map[(state.pos_x - state.dir_x * state.mov_speed).floor][state.pos_y.floor] == 0
state.pos_x -= state.dir_x * state.mov_speed
end
if state.world_map[state.pos_x.floor][(state.pos_y - state.dir_y * state.mov_speed).floor] == 0
state.pos_y -= state.dir_y * state.mov_speed
end
end
if inputs.keyboard.key_held.d
# ruby supports destructoring of arrays
# example: a, b, c = [:item_1, :item_2, :item_3]
# a will have the value :item_1, etc...
state.dir_x, state.dir_y = rotate_point state.dir_x, state.dir_y, state.rot_speed
state.plane_x, state.plane_y = rotate_point state.plane_x, state.plane_y, state.rot_speed
end
if inputs.keyboard.key_held.a
state.dir_x, state.dir_y = rotate_point state.dir_x, state.dir_y, -state.rot_speed
state.plane_x, state.plane_y = rotate_point state.plane_x, state.plane_y, -state.rot_speed
end
end
def render
# it's faster/more performant to used Numeric#map_with_index
(state.screen_width - 1).map_with_index do |x|
camera_x = 2 * x / state.screen_width - 1
ray_dir_x = state.dir_x + state.plane_x * camera_x
ray_dir_y = state.dir_y + state.plane_y * camera_x
map_x = state.pos_x.floor
map_y = state.pos_y.floor
side_dist_x = 0
side_dist_y = 0
delta_dist_x = (1 / ray_dir_x).abs
delta_dist_y = (1 / ray_dir_y).abs
perm_wall_dist = 0
step_x = 0
step_y = 0
hit = 0
side = 0
if ray_dir_x < 0
step_x = -1
side_dist_x = (state.pos_x - map_x) * delta_dist_x
else
step_x = 1
side_dist_x = (map_x + 1.0 - state.pos_x) * delta_dist_x
end
if ray_dir_y < 0
step_y = -1
side_dist_y = (state.pos_y - map_y) * delta_dist_y
else
step_y = 1
side_dist_y = (map_y + 1.0 - state.pos_y) * delta_dist_y
end
while hit == 0
if side_dist_x < side_dist_y
side_dist_x += delta_dist_x
map_x += step_x
side = 0
else
side_dist_y = delta_dist_y
map_y += step_y
side = 1
end
if state.world_map[map_x][map_y] > 0
hit = 1
end
end
if side == 0
perm_wall_dist = (map_x - state.pos_x + (1 - step_x) / 2) / ray_dir_x
else
perm_wall_dist = (map_y - state.pos_y + (1 - step_y) / 2) / ray_dir_y
end
line_height = (state.screen_height / perm_wall_dist).floor
draw_start = -line_height / 2 + state.screen_height / 2
if draw_start < 0
draw_start = 0
end
draw_end = line_height / 2 + state.screen_height / 2
if draw_end >= state.screen_height
draw_end = state.screen_height - 1
end
r, g, b = 0
case state.world_map[map_x][map_y]
when 1
r = side == 1 ? 128 : 255
when 2
g = side == 1 ? 128 : 255
when 3
b = side == 1 ? 128 : 255
when 4
r, g, b = side == 1 ? 128 : 255
else
r = side == 1 ? 128 : 255
g = side == 1 ? 128 : 255
end
draw_line(x, draw_start, draw_end, r, g, b)
end
end
def draw_line x, draw_start, draw_end, r, g, b
line1X = x
line1Y = draw_start
line2X = x
line2Y = draw_end
state.lines << [line1X, line1Y, line2X, line2Y, r, g, b]
end
end
$raycasting = Raycasting.new
def tick args
# if you use attr_gtk, you only have to set the args
# property
$raycasting.args = args
$raycasting.tick
args.render_target(:viewport).lines << args.state.lines
args.sprites << [0, 0, 1280 * args.state.scale_x, 720 * args.state.scale_y, :viewport]
end
$gtk.reset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment