-
-
Save amirrajan/9dcc5d3d2e62cfe4b301ee4b1d874c36 to your computer and use it in GitHub Desktop.
DragonRuby ray casting.
This file contains 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
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