ntech
Junior Member
Posts: 99
|
Post by ntech on Aug 23, 2019 16:26:45 GMT
I've always wanted to see a raycaster in JustBasic. This is a challenge to see who can make the best raycaster by September 23 (exactly 1 month from now). Here are some articles to help you out: en.wikipedia.org/wiki/Ray_castinglodev.org/cgtutor/raycasting.htmlIn the meantime, I'll be making my own raycaster, and may the best one win!
|
|
ntech
Junior Member
Posts: 99
|
Post by ntech on Aug 23, 2019 17:37:10 GMT
Ok, I've come up with a preliminary version of a raycaster. Funny enough, it works pretty well, but throws errors often: "System primitive failed", and just a blank screen.
To use the code, run it. Currently, rotation ("a" and "d" keys) works (a little), but give it time to render before you jam the key again (wait till it's done!)
global map$ map$ = "1 2 3 1 2 3 1" + chr$(13) + _ 'The map "3 - - - - - 2" + chr$(13) + _ "2 - - - - - 3" + chr$(13) + _ "1 - - - - - 2" + chr$(13) + _ "2 - - - - - 3" + chr$(13) + _ "1 2 3 1 2 3 1"
global posX, posY, dirX, dirY, planeX, planeY posX = 4 '-- | posY = 4 '-- | The position vector of the player, set to the start position dirX = -1 '-| dirY = 0 '-| The direction of the player planeX = 0 '-----| planeY = 0.66 '--|The camera plane of the player
global w, h w = 640 'The width h = 480 'The height
WindowWidth = 640 WindowHeight = 480
open "Raycaster" for graphics_nsb_nf as #canvas
print #canvas, "fill black; flush seg" print #canvas, "when characterInput chrInput$ handle$, key$"
call draw$
wait
sub chrInput$ handle$, key$
rotspeed = .4 nrotspeed = -.4
select case lower$(key$)
case "w" 'forwards if worldMap(posX+dirX,posY) = 0 then posX = posX + dirX * 1 if worldMap(posX,posY+dirY) = 0 then posY = posY + dirY * 1
case "s" 'backwards if worldMap(posX-dirX,posY) = 0 then posX = posX - dirX * 1 if worldMap(posX,posY-dirY) = 0 then posY = posY - dirY * 1
case "a" 'rotate left oldDirX = dirX dirX = dirX * cos(rotspeed) - dirY * sin(rotspeed) dirY = oldDirX * sin(rotspeed) + dirY * cos(rotspeed)
oldPlaneX = planeX planeX = planeX * cos(rotspeed) - planeY * sin(rotspeed) planeY = oldPlaneX * sin(rotspeed) + planeY * cos(rotspeed)
case "d" 'rotate right oldDirX = dirX dirX = dirX * cos(nrotspeed) - dirY * sin(nrotspeed) dirY = oldDirX * sin(nrotspeed) + dirY * cos(nrotspeed)
oldPlaneX = planeX planeX = planeX * cos(nrotspeed) - planeY * sin(nrotspeed) planeY = oldPlaneX * sin(nrotspeed) + planeY * cos(nrotspeed)
end select
call draw$
end sub
sub draw$
print #canvas, "when characterInput" print #canvas, "delsegment seg; fill black"
for x = 0 to w
scan
'calculate ray position and direction
cameraX = 2 * x / w-1 'x-coordinate in camera space rayDirX = dirX + planeX * cameraX rayDirY = dirY + planeY * cameraX + .0000001
'get the xy coordinates of the box on the map that we're in mapX = int(posX) mapY = int(posY)
'length of ray from current position to next x- or y-side sideDistX = 0 sideDistY = 0
'length of ray from one x- or y-side to the next x- or y-side deltaDistX = abs(1 / rayDirX) deltaDistY = abs(1 / rayDirY) perpWallDist = 0
'what direction to step in x- or y-direction (either +1 or -1) stepX = 0 stepY = 0
hit = 0 'if 1, a wall was hit side = 0 'determines whether the wall hit is North-South, or East-West
'calculate step and initial sideDist
'If the ray direction has a negative x-component, sideDistX is the distance from the ray 'starting position to the first side to the left, if the ray direciton has a positive x-component 'the first side to the right is used instead. 'The same goes for the y-component, but now with the first side above or below the position. 'For these values, the integer value mapX is used and the real position subtracted from it, and 1.0 'is added in some of the cases depending if the side to the left or right, of the top or the bottom 'is used. Then you get the perpendicular distance to this side, so multiply it with deltaDistX or 'deltaDistY to get the real Euclidean distance.
if rayDirX < 0 then stepX = -1 sideDistX = (posX - mapX) * deltaDistX else stepX = 1 sideDistX = (mapX + 1 - posX) * deltaDistX end if
if rayDirY < 0 then stepY = -1 sideDistY = (posY-mapY) * deltaDistY else stepY = 1 sideDistY = (mapY + 1 - posY) * deltaDistY end if
'start DDA
hit = 0 while hit = 0
scan
if sideDistX < sideDistY then
sideDistX = sideDistX + deltaDistX mapX = mapX + stepX side = 0
else
sideDistY = sideDistY + deltaDistY mapY = mapY + stepY side = 1
end if
if worldMap(mapX, mapY) <> 0 then hit = 1
wend
'calculate the distance of the ray to the wall, so that we can calculate how high the wall has 'to be drawn.
if side = 0 then perpWallDist = (mapX - posX + (1 - stepX) / 2) / rayDirX else perpWallDist = (mapY - posY + (1 - stepY) / 2) / rayDirY end if
if perpWallDist = 0 then perpWallDist = .0000001
'calculate the length of the line to draw lineHeight = h / perpWallDist
'calculate lowest and highest pixel coords to draw with line drawStart = (0-abs(lineHeight)) / 2 + h / 2 if drawStart < 0 then drawStart = 0 drawEnd = lineHeight / 2 + h / 2 if drawEnd >= h then drawEnd = h-1
select case worldMap$(mapX, mapY)
case "1" if side = 1 then color$ = "darkred" if side = 0 then color$ = "red"
case "2" if side = 1 then color$ = "darkblue" if side = 0 then color$ = "blue"
case "3" if side = 1 then color$ = "darkgreen" if side = 0 then color$ = "green"
case "-" print "Hmmm, we are trying to draw air?"
case else print "What in the world is " + worldMap$(mapX, mapY) + "?"
end select
call drawLine$ x, drawStart, drawEnd, color$
next x
print #canvas, "flush seg" print #canvas, "when characterInput chrInput$ handle$, key$"
end sub
sub drawLine$ x, drawStart, drawEnd, color$
print #canvas, "color ";color$ print #canvas, "down; line ";x;" ";drawStart;" ";x;" ";drawEnd print #canvas, "up"
end sub
function worldMap(mx, my)
yLine$ = word$(map$, my, chr$(13))
if mx > wc(yLine$) or mx < 1 then worldMap = 0 exit function end if
tile$ = word$(yLine$, mx)
if tile$ <> "-" then worldMap = 1 end if
end function
function worldMap$(mx, my)
yLine$ = word$(map$, my, chr$(13))
if mx > wc(yLine$) or mx < 1 then worldMap$ = "" exit function end if
tile$ = word$(yLine$, mx)
worldMap$ = tile$
end function
function wc(s$)
'Word-count, based on spaces
wc = 0
for i = 1 to len(s$)
nc$ = mid$(s$, i, 1)
if nc$ = " " then wc = wc + 1
next i
end function
|
|
|
Post by Rod on Aug 24, 2019 16:00:16 GMT
Have not seen any errors yet but put a setfocus command at the foot of your draw sub. That way you don't have to click in the windows to make A and D work.
print #canvas, "flush seg" print #canvas, "when characterInput chrInput$ handle$, key$" print #canvas, "setfocus"
|
|
ntech
Junior Member
Posts: 99
|
Post by ntech on Aug 26, 2019 15:34:35 GMT
Ok, here's my entry edited, with all errors gone To run: run code. Controls: (a = rotate left) (d = rotate right) (w = forward) (s = backward) global map$ map$ = "1 2 3 1 2 3 1" + chr$(13) + _ 'The map "3 - - - - - 2" + chr$(13) + _ "2 - - - - - 3" + chr$(13) + _ "1 - - - - - 2" + chr$(13) + _ "2 - - - - - 3" + chr$(13) + _ "1 2 3 1 2 3 1"
global posX, posY, dirX, dirY, planeX, planeY posX = 4 '-- | posY = 4 '-- | The position vector of the player, set to the start position dirX = -1 '-| dirY = 0 '-| The direction of the player planeX = 0 '-----| planeY = 0.66 '--|The camera plane of the player
global w, h w = 640 'The width h = 480 'The height
WindowWidth = 640 WindowHeight = 480
open "Raycaster" for graphics_nsb_nf as #canvas
print #canvas, "fill black; flush seg" print #canvas, "when characterInput chrInput$ handle$, key$" print #canvas, "setfocus"
call draw$
wait
sub chrInput$ handle$, key$
rotspeed = .4 nrotspeed = -.4
select case lower$(key$)
case "w" 'forwards if worldMap(posX+dirX,posY) = 0 then posX = posX + dirX * 1 if worldMap(posX,posY+dirY) = 0 then posY = posY + dirY * 1
case "s" 'backwards if worldMap(posX-dirX,posY) = 0 then posX = posX - dirX * 1 if worldMap(posX,posY-dirY) = 0 then posY = posY - dirY * 1
case "a" 'rotate left oldDirX = dirX dirX = dirX * cos(rotspeed) - dirY * sin(rotspeed) dirY = oldDirX * sin(rotspeed) + dirY * cos(rotspeed)
oldPlaneX = planeX planeX = planeX * cos(rotspeed) - planeY * sin(rotspeed) planeY = oldPlaneX * sin(rotspeed) + planeY * cos(rotspeed)
case "d" 'rotate right oldDirX = dirX dirX = dirX * cos(nrotspeed) - dirY * sin(nrotspeed) dirY = oldDirX * sin(nrotspeed) + dirY * cos(nrotspeed)
oldPlaneX = planeX planeX = planeX * cos(nrotspeed) - planeY * sin(nrotspeed) planeY = oldPlaneX * sin(nrotspeed) + planeY * cos(nrotspeed)
end select
call draw$
end sub
sub draw$
print #canvas, "when characterInput" print #canvas, "delsegment seg; fill black"
for x = 0 to w
scan
'calculate ray position and direction
cameraX = 2 * x / w-1 'x-coordinate in camera space rayDirX = dirX + planeX * cameraX rayDirY = dirY + planeY * cameraX + .01 '.0000001
'get the xy coordinates of the box on the map that we're in mapX = int(posX) mapY = int(posY)
'length of ray from current position to next x- or y-side sideDistX = 0 sideDistY = 0
'length of ray from one x- or y-side to the next x- or y-side deltaDistX = abs(1 / rayDirX) deltaDistY = abs(1 / rayDirY) perpWallDist = 0
'what direction to step in x- or y-direction (either +1 or -1) stepX = 0 stepY = 0
hit = 0 'if 1, a wall was hit side = 0 'determines whether the wall hit is North-South, or East-West
'calculate step and initial sideDist
'If the ray direction has a negative x-component, sideDistX is the distance from the ray 'starting position to the first side to the left, if the ray direciton has a positive x-component 'the first side to the right is used instead. 'The same goes for the y-component, but now with the first side above or below the position. 'For these values, the integer value mapX is used and the real position subtracted from it, and 1.0 'is added in some of the cases depending if the side to the left or right, of the top or the bottom 'is used. Then you get the perpendicular distance to this side, so multiply it with deltaDistX or 'deltaDistY to get the real Euclidean distance.
if rayDirX < 0 then stepX = -1 sideDistX = (posX - mapX) * deltaDistX else stepX = 1 sideDistX = (mapX + 1 - posX) * deltaDistX end if
if rayDirY < 0 then stepY = -1 sideDistY = (posY-mapY) * deltaDistY else stepY = 1 sideDistY = (mapY + 1 - posY) * deltaDistY end if
'start DDA
hit = 0 while hit = 0
scan
if sideDistX < sideDistY then
sideDistX = sideDistX + deltaDistX mapX = mapX + stepX side = 0
else
sideDistY = sideDistY + deltaDistY mapY = mapY + stepY side = 1
end if
if worldMap(mapX, mapY) <> 0 then hit = 1
wend
'calculate the distance of the ray to the wall, so that we can calculate how high the wall has 'to be drawn.
if side = 0 then perpWallDist = (mapX - posX + (1 - stepX) / 2) / rayDirX else perpWallDist = (mapY - posY + (1 - stepY) / 2) / rayDirY end if
if perpWallDist = 0 then perpWallDist = .01'.0000001
'calculate the length of the line to draw lineHeight = h / perpWallDist
'calculate lowest and highest pixel coords to draw with line drawStart = (0-abs(lineHeight)) / 2 + h / 2 if drawStart < 0 then drawStart = 0 drawEnd = lineHeight / 2 + h / 2 if drawEnd >= h then drawEnd = h-1
select case worldMap$(mapX, mapY)
case "1" if side = 1 then color$ = "darkred" if side = 0 then color$ = "red"
case "2" if side = 1 then color$ = "darkblue" if side = 0 then color$ = "blue"
case "3" if side = 1 then color$ = "darkgreen" if side = 0 then color$ = "green"
case "-" print "Hmmm, we are trying to draw air?"
case else print "What in the world is " + worldMap$(mapX, mapY) + "?"
end select
call drawLine$ x, drawStart, drawEnd, color$
next x
print #canvas, "flush seg" print #canvas, "when characterInput chrInput$ handle$, key$"
end sub
sub drawLine$ x, drawStart, drawEnd, color$
print #canvas, "color ";color$ print #canvas, "down; line ";x;" ";drawStart;" ";x;" ";drawEnd print #canvas, "up"
end sub
function worldMap(mx, my)
mx = int(mx) my = int(my)
yLine$ = word$(map$, my, chr$(13))
if mx > wc(yLine$) or mx < 1 then worldMap = 0 exit function end if
tile$ = word$(yLine$, mx)
if tile$ <> "-" then worldMap = 1 end if
exit function
end function
function worldMap$(mx, my)
mx = int(mx) my = int(my)
yLine$ = word$(map$, my, chr$(13))
if mx > wc(yLine$) or mx < 1 then worldMap$ = "" exit function end if
tile$ = word$(yLine$, mx)
worldMap$ = tile$
end function
function wc(s$)
'Word-count, based on spaces
wc = 0
for i = 1 to len(s$)
nc$ = mid$(s$, i, 1)
if nc$ = " " then wc = wc + 1
next i
end function
|
|
carlg
New Member
Posts: 14
|
Post by carlg on Aug 27, 2019 2:19:58 GMT
Cool, but for me it hangs if I turn right several times.
|
|
ntech
Junior Member
Posts: 99
|
Post by ntech on Aug 29, 2019 23:01:26 GMT
Same with me, I'll have to figure out why. Most probably it's doing its job, but the coordinates of the camera are wacked.
|
|
atomose
Member in Training
Posts: 32
|
Post by atomose on Sept 1, 2019 11:50:48 GMT
hi ! if you want to test a raycaster i found this one ! FOUND HEREits a simple but nice raycaster. A little hard to understand ^^
|
|
ntech
Junior Member
Posts: 99
|
Post by ntech on Sept 3, 2019 13:57:29 GMT
hi ! if you want to test a raycaster i found this one ! FOUND HEREits a simple but nice raycaster. A little hard to understand ^^ Awesome! I had no idea such a FPS existed, and it's awesome (especially its ability to draw textures).
|
|