JIAN2587's RAYCASTING TUTORIAL  jian2587@hotmail.com
RAYCASTING TUTORIAL BY TZE JIAN CHEAR
INTRODUCTION
If U read this tut, means you don't understand raycast eventhough you read plenty of similiar
tuts, just as I once was. But now, I'll explain all the stuffs and thin' behind raycasting and
make sure you understand it!
Okay, I assume you have no prior XP nor knowledge regarding raycasting. Now, let'ta tut begin!
Lesson 1 ABOUT COSINE AND SINUSE AND WHAT ARE THEIR FUNCTIONS
Okay, I won't explain raycasting first. Instead somethin' about COS/SIN. Have you ever
wonder how QB draws a circle with just center X,Y and radius as its parameter? I mean, how QB
know the exact position of each pixel of the circle? Can U figure it out? Some of my lamer
QB friends said by incrementing both X and Y position and then the other way round to
produce a circle, now this is wrong!(If ur like him)Try this then you'll understand.
'Lamer's way
SCREEN 12
CenterX = 320: CenterY = 240: Radius = 50
X = CenterX
Y = CenterY  Radius
DO UNTIL X = CenterX + Radius
X = X + 1
Y = Y + 1
PSET (X, Y), 15
LOOP
DO UNTIL X = CenterX
X = X  1
Y = Y  1
PSET (X, Y), 14
LOOP
'Run it. Got what I meant?
Okay, that's impossible. Don't think of any other ways.(Though I am sure that there's
other way to draw a circle by using plus and subtract) Use Cosine and Sinuse. So try this source.
'Intermediate's way
SCREEN 12
CenterX = 320: CenterY = 240: Radius = 50
DO UNTIL DEGREE = 360
DEGREE = DEGREE + 1
RADIAN = DEGREE / 57.2958
X = COS(RADIAN) * Radius
Y = SIN(RADIAN) * Radius
PSET (CenterX + X, CenterY + Y), 15
LOOP
'Run it. What U got?
Now, you can try to figure out the source. Given a distance, that is radius, and degree,
you can find the exact position with COS/SIN. See this diagram.
DIAGRAM 1
{Top view}




 / <Length is 3 unit, so you got this position
 / <Length is 2 unit, so you got this position
/ <Length is 1 unit, so you got this position
o <This is your center position
**The above diagram was applying 30 degree.**(Estimated)
COS(RADIAN) * Radius and SIN(RADIAN) * Radius
what do u know about this? It just let you know the position for the given degree and after
multiplying it with the distance you get the distanced position. Which means, to find position of
a point where it's about j degree away and l distance from the center position, you would use
RADIAN = DEGREE / 57.2958
X = CenterX + (COS(RADIAN) * Distance)
Y = CenterY + (SIN(RADIAN) * Distance)
So, if U doesn't understand the source but this diagram, never mind. Just remember the source.
Beyond this point, I assume you know about COS/SIN.
Lesson 2A SIMPLE RAYCASTING AND EXAMPLE SOURCE CODE
Now that U know the method I explained in lesson 1, please don't bother it now. I am
gonna explain sumthin' else. Yeah, a bit about raycasting. So, let's preview what's raycasting
that is so rockin' till ID uses it in DOOM. Imagine you are on a ship, you have a radar, and
you gotta know what's outside. So the radar beams a sonic pulse to every direction. When a sonic
pulse hit somethin', it was reflected back and a censor detected the sonic pulse, thus, we know
there's somethin' outside. You know the direction and distance of the objects you detected.
So your radar screen draw the thin' accordingly.
Raycasting is somethin' like that, where the RAY is the SONIC PULSE. Now, for a bit more
advance, I'll explain it detailfully. On a 2D map(Didn't U know that raycasting is a 2D
technique fooling the player that it's 3D?), from your viewing field at your current position,
you fire a ray from left to right of your viewing field and it travel straight till it hit a
wall, then u got'ta position of the wall and you just draw it(if it's a far object, logically
we will draw it small to make kinda perspective feel). So, no wall? Then don't draw it! And while
you are moving, check if you are on a wall position, if yes, don't let ya got into it and don't
draw it.
So, why DOOM has multiple level where you can go up a ladder, shoot the flying monsters,
and thin's like that? And since raycasting has no Z coordinate (only X and Y, that is 2D)
That's easy, coz' it has multiple 2D map. Just like a layer cake. If ur Z position is on a higher
position, then change the map! Man, you still have to render all the maps, only if it falls into
ur viewing field.(Viewing field is about 18% of 360 degrees according to Dieter Mafurt's source)
So, ahhh, any source code for me to try on? Yes, but that's darn long. I changed it a bit
from DOOM.BAS by unknown. So try it on.
'My first Raycaster  by Tze Jian Chear  jian2587@hotmail.com
DEFSNG AZ
SCREEN 13
DIM S(31 TO 360 + 32), C(31 TO 360 + 32), Map%(12, 12)
BlockWidth = 900: Phi = 100: Strafe = .1
FOR Y = 1 TO 12
FOR X = 1 TO 12
READ Map%(X, Y)
NEXT: NEXT
PX = 9: PY = 11: Heading = 0: Stride = 3: Turn = 5
Factor = (ATN(1) * 8) / 360
FOR Degree% = 0 TO 359
Angle = CSNG(Degree%) * Factor
S(Degree%) = SIN(Angle) * .1
C(Degree%) = COS(Angle) * .1
NEXT
FOR Ray% = 31 TO 1
S(Ray%) = S(Ray% + 360)
C(Ray%) = C(Ray% + 360)
NEXT
FOR Ray% = 360 TO 360 + 32
S(Ray%) = S(Ray%  360)
C(Ray%) = C(Ray%  360)
NEXT
GOSUB Raycast
DO
K$ = INKEY$
IF K$ <> "" THEN
SELECT CASE RIGHT$(K$, 1)
CASE "K" 'strafe left
Heading = (Heading + Turn) MOD 360: GOSUB Raycast
CASE "M" 'strafe Right
Heading = (Heading + (360  Turn)) MOD 360: GOSUB Raycast
CASE "H" 'forward
NewPX = PX  (S(Heading) * Stride)
NewPY = PY  (C(Heading) * Stride)
IF Map%(NewPX, NewPY) = 0 THEN PX = NewPX: PY = NewPY: GOSUB Raycast ELSE BEEP
CASE "P" 'backward
NewPX = PX + (S(Heading) * Stride)
NewPY = PY + (C(Heading) * Stride)
IF Map%(NewPX, NewPY) = 0 THEN PX = NewPX: PY = NewPY: GOSUB Raycast ELSE BEEP
CASE CHR$(27): SCREEN 0: WIDTH 80, 25: END
END SELECT
END IF
LOOP
Raycast:
X1 = 0
CLS : WAIT &H3DA, 8
FOR Ray% = Heading + 32 TO Heading  31 STEP 1
StepX = S(Ray%): StepY = C(Ray%)
XX = PX: YY = PY: BlockDist = 0
DO
XX = XX  StepX: YY = YY  StepY
BlockDist = BlockDist + 1
K = Map%(XX, YY)
LOOP UNTIL K
DD = BlockWidth \ BlockDist
Height = DD + DD: DT = Phi  DD
LINE (X1, DT)STEP(4, Height), K, BF
X1 = X1 + 5
NEXT
RETURN
DATA 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1
DATA 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 9
DATA 9, 0, 2, 10, 0, 0, 0, 0, 0, 14, 0, 1
DATA 1, 0, 10, 2, 0, 0, 0, 0, 0, 4, 0, 9
DATA 9, 0, 0, 0, 0, 0, 0, 0, 13, 14, 0, 1
DATA 1, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 9
DATA 9, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 1
DATA 1, 0, 13, 0, 0, 0, 0, 8, 0, 12, 0, 9
DATA 9, 0, 5, 0, 0, 8, 0, 7, 0, 4, 0, 1
DATA 1, 0, 13, 0, 0, 0, 0, 8, 0, 12, 0, 9
DATA 9, 0, 5, 0, 0, 0, 0, 7, 0, 4, 0, 1
DATA 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9
Okay, try it on. Yucks! Flickers? Yeah, I don't have time for that, coz' putting
unrelated stuff would make u more complicated.
LESSON 2B SIMPLE RAYCASTING AND WALKTHROUGH
Now, let's go through this code. First you define all the variables.
DIM S(31 TO 360 + 32)...blah is to make a lookup table for speed. This is common, coz'
COS/SIN functions are slow.
Then the map%(12, 12), you make it 2 dimension with 12x12 grids.**DIM Map%(12, 12)**
BlockWidth,Phi will be explained later.
Okay, u read the map from data. **FOR Y = 1 TO 12...READ Map%(X, Y)...NEXT**
PX = 9: PY = 11 this is ur position in the map. Heading is where u are facing.
Stride is how many steps to go per pressure of key. Turn is how many degrees to turn per
pressure of key.
Between
Factor = (ATN(1) * 8) / 360
and
C(Degree%) = COS(Angle) * .1
NEXT
This is to build up ur COS/SIN lookup table.
WAIT! Why multiply COS(Angle) with .1 ? After kinda analyzation of this piece code of mine which
has some common places with other source codes, this .1 seems to be controlling the width of
every grid indirectly. Ahh, don't care 'bout it. Just proceed.
Between FOR Ray% = 31 TO 1
and
NEXT
Well, this is sumthin' used with the raycaster loop and your viewing field. from 31 to
+ 32, there's 64 rays.(Okay, wait a minute. Now, to speed up stuffs, we gotta used only
64 rays and not 320 rays!(SCREEN 13 = 320x200x256)Of course 320 rays would make thin's
clearer but also slows down everythin' unless u use ASM or whatever.) You might get
confused a bit. Our viewing field(The range ur eye can see in one direction) is as same size as
the screen, and the screen has 320 pixels horizontally(SCREEN 13). So, we fire 64 rays from our
eye, and these rays will detect the walls and give u back its distance and position, then we draw
the walls accordingly. Why 64 rays? These rays will cover about 5 pixels width, (64x5=320pixels),
in fact, it doesn't matter how many rays you fired, but here I used 64 rays to speed up stuff
coz' I just have to draw 64 times, though, it's blocky.
If U found me kept on repeating some points, I just 1 2 make sure u really understand with
kinda examples and diagrams.
DIAGRAM 3
{Top view}
Wall /Wall
/ /
/ /
/ /<THESE are your rays.So when ur rays hit the wall, you get the distance and its
/\ / position.
/\\ /
\/
% <This is ur eye.
Okay, got what I mean? WAIT! How we apply the COS/SIN method I explained earlier? You can use it
to determine where should ur ray go. Okay, let's revise the source code.
FOR Ray% = Heading + 32 TO Heading  31 STEP 1 'For each rays
StepX = S(Ray%): StepY = C(Ray%) 'Get the increment steps for our ray first
'we gotta increment the rays position with these steps
'every time to let it(the ray) know where it's going
This should be easy. If you know what do I mean about incrementing the rays position every time,
you can skip below.
DIAGRAM 4
{Top view}

~~~/~~~ <Hit a wall, so we got the wall's position and distance
 / <With distance 3 units and 30 degrees, ur ray got this position }
 / <ur ray get here with an increment of stepX from previous position }
/ <With distance 1 unit and 30 degrees, ur ray got this position with the COS/SIN }
Eye, >0 method.
or viewing field.
Okay, how does DIAGRAM 4 works in the source code? See below.
FOR Ray% = Heading + 32 TO Heading  31 STEP 1 'For each ray...
StepX = S(Ray%): StepY = C(Ray%) 'Yeah, we let the ray know how much should it move
XX = PX: YY = PY: BlockDist = 0 'Our position. BlockDist will increase indicating
'our ray's travelling further and it affects how should
'our wall drew
DO 'This is the raycasting loop
XX = XX  StepX: YY = YY  StepY 'Our ray's travelling...
BlockDist = BlockDist + 1 'Increase the distance...
K = Map%(XX, YY) 'On current ray position, is there a wall?
LOOP UNTIL K 'Just loop till we found one
DD = BlockWidth \ BlockDist 'Divide the distance we get with BlockWidth
Height = DD + DD: DT = Phi  DD 'So we have the height and the starting point 2 draw
LINE (X1, DT)STEP(4, Height), K, BF 'Okay, draw it(Go2 kindegarten if u don't know LINE)
X1 = X1 + 5 'We increase the X1 by 5 pixels for next ray
NEXT 'So NEXT
While walking, our position is also variable as the ray's position did. So, to update our
position, we use the same method. Just figure all that by ur self starting from the line
SELECT CASE RIGHT$(K$, 1)
Okay.
Mehehe PeePoPeePo WooWooWoo Weeeeeeeuuuuu poinpoinpoin tututututu pingpangkalapang 'Skip this >:D
LESSON 3 KINDA ADVANCE TOPICS REGARDING RAYCASTING
Lesson 2 is too long, I should have break it into 2 chapters. Okay, for somethin' more
advance, I'll explain the Phi variable. I thought I read some tutorial said that you really can't
see what's above u nor below ur feet in raycasting. Theoratically this is right, but not exactly
right. Go search for DOOMZ. I found one DOOMBOT and played around with it.(Pretty much like
CounterStrike)It uses SVGA screenmodes and with tons of playing method.(Capture flag, teamplay..
.blah,Oops, I shouldn't talk 'bout DOOM) DOOMZ lets you aim up and down, just like Halflife or
Quake.
How we **see** up and down in raycasting? Easy, change the Phi variable. Higher value
would make u see above and lower would do the opposite. Try modifying the above source. Assign
a couplar of keys to change the Phi variable, so u can aim. To make thin's better, put kinda
mouse routine in it.Merhehe.
If I wasn't wrong, they might trace the map horizontally also, so when you fire ur
rocket launcher, the rocket would upwards if ur aiming up side. (DOOM2 has bit autoaiming system,
when u fire, even if ur enemy is above u, ur bullet or rocket would go upward)Don't understand?
Never mind. U need no concern 'bout this. But anyway, I'll explain my idea(or maybe it's true
that they use this method).
DIAGRAM 5
{Side view}
$<Enemy, call the **enemy blastingoff animation routine**
/
/ <Ur rocket keeps on travelling
/ <U fire ur gun, and it trace on like this(just like the rays)
Eye>0/_________<Floor
Okay, got that? If not, please goto lesson 1.
LESSON 4 ANOTHER EXAMPLE OF RAYCASTING
I analyzed this source code by Jack Mallah, so let's go through the code.
horizon = 100: pi = ATN(1) * 4: px = 2: py = 2: Ch = 99: C = 900 / pi
SCREEN 13
1 : FOR xs = 160 TO 159
R = 20: theta = ATN(xs / C): ang = pa + theta
dis(0) = (10  px) / COS(ang)
dis(1) = (10  py) / COS(pi / 2  ang)
dis(2) = px / COS(pi  ang)
dis(3) = py / COS(pi * 1.5  ang)
FOR w = 0 TO 3
IF dis(w) > 0 AND dis(w) < R THEN wall = w: R = dis(w)
NEXT
x = xs + 160: Z = R * COS(theta): scrht = Ch / Z
LINE (x, 200)(x, horizon + scrht), 6
LINE (x, horizon  scrht), wall + 2
LINE (x, 1), 8
NEXT
i$ = RIGHT$(INKEY$, 1)
IF i$ = "H" THEN px = px + COS(pa) / 5: py = py + SIN(pa) / 5
IF i$ = "P" THEN px = px  COS(pa) / 5: py = py  SIN(pa) / 5
IF i$ = "K" THEN pa = pa  .1 ELSE IF i$ = "M" THEN pa = pa + .1
IF px < .4 THEN px = .4 ELSE IF px > 9.6 THEN px = 9.6
IF py < .4 THEN py = .4 ELSE IF py > 9.6 THEN py = 9.6
IF i$ <> CHR$(27) GOTO 1
'Unbelievable! Why da' code's so short? Me curious too! But the power and freedom of programming
'let's do so. Okay, I'll go on very fast.
Variable horizon is kinda like Phi.(Phi is not Pi)
Variable pi is the pi of a circle.(Since 3D stuffs're all 'bout rotation so that's why we gotta
learn Pi and circle stuffs)
px and py is ur position. Ch and C controls the wall's size.(Just experiment with it and u'll
understand)
FOR XS = 160... this is the raycasting loop
I don't know what R controls. It seems that if its above 0 it won't mess up with others.
Theta is the complement of Phi. Ang is the angle. Pa is ur heading.
dis(0)... till scrht = Ch / Z calculates all the raycasting stuffs.
Then we draw it with LINE.
Get input. Then loop if no ESC. Just that easy.
Err...If JACK read this tut, please don't feel insulted or whatever if I misinterpret ur source
or whatever.
LESSON 5 STUFFS BESIDES RAYCASTING
Get DOOM2 for USD25.00 from ID. Play it, feel it. What've u got? Nothin'? Never mind,
just smack ur CPU then try it and see. What've u got? Wallet's crying? Never mind, change a new
one.
Okay, after playing so far, DOOM2 seems to be pushing ur CPU's limit(for 386DX or 486DX),
pumping out stunning but not detail gfx, complete with texturemapping, textureshadding,and
stuff like that. Okay, that's what u gotta concern 'bout ur raycasting game. Wether it's racing,
sports, FPS or whatever, u gotta know all these tricks.
I won't explain how u do all these stuffs, but instead try my best to tell you what's it.
I might cover it on my next tutorial. Or may be raycasting tut part2. Let's cut the crap, and
let's start with texture mapping as this gives you the realistic feel to ur game.
TEXTURE MAPPING
There're tonnes of tricks and techniques to map a texture on a polygon or in our case,
raycasting. Texture mapping is a technique where you put a texture(a 2D image,it could be a door
or a window, depends on how u design ur map) on the calculated wall. We don't just GET and PUT it
in, as the wall is in 3D form, and as further places will be smaller, u gotta know the trick.
See this DIAGRAM.
DIAGRAM 6
{3D view}
___________CEILING____
 __ _
THIS  DO [_] <Window
IS ____OR_____
A /
WALL /
/
/ FLOOR
/
/
/
/
/
See the wall? It doesn't look like a rectangular, as it should be, coz' this is in 3D view in
certain direction and distance. So you got a 2D square wall texture, but can you just put it like
that? Of course not. Now, this is another tricky part. I saw some tutorial regarding texture
mapping, and it's sort of pixelbypixel calculations and plotting. Thin's have been nightmare
4 u, aren't u?
Anyway, after I analyzed a few source code regarding texturemapping, I'll write another
tutorial 4 u guyz. So long, and Adios!
Wait 4 my next tut guyz!
Completed on 12:22PM, Saturday, November 03, 2001 AD.

Here is the complete source code:

'JIAN2587 RAYCASTING ALGORITHMS
'Story behind raycasting (please jump to the end)
'First, you have to initialize a few variables. X,Y of the player, steps covered by one key
'press, heading of the player, and most important, the 2D map.
'Why I dim extra 31 to +32? That's the 64 rays I am gonna send out
'0.Dim SinuseTable(31 TO 360 + 32), CosineTable(31 TO 360 + 32)
'1.First, read the map into a 2 dimension array.
'2.Caculate the Sinuse/Cosine table to speed up calculations.
'''*****Stride  Steps moved per movement*****'''
CONST BlockWidth = 900
CONST Phi = 100
'/*Calculates Cosine/Sinuse table*/'
Factor = (ATN(1) * 8) / 360
FOR D = 0 TO 359
Angle = CSNG(D) * Factor
SinuseTable(D) = SIN(Angle) * .1 '.1 is the size of every grid X
CosineTable(D) = COS(Angle) * .1 '.1 is the size of every grid Y
NEXT
'/*Calculates steps covered(bit hard 2 explain, go down for exact explanation)*/'
FOR A = 31 TO 1
SinuseTable(A) = SinuseTable(A + 360)
CosineTable(A) = CosineTable(A + 360)
NEXT
FOR A = 360 TO 360 + 32
SinuseTable(A) = SinuseTable(A  360)
CosineTable(A) = CosineTable(A  360)
NEXT
'3.Update it
'Left
Heading = (Heading + Turn) MOD 360
'Right
Heading = (Heading + (360  Turn)) MOD 360
'Forward
NewPositionX = CurrentPositionX  (SinuseTable(Heading) * Stride)
NewPositionY = CurrentPositionY  (CosineTable(Heading) * Stride)
'/*Checks if it's walkthroughable*/'
'Backward
NewPositionX = CurrentPositionX + (SinuseTable(Heading) * Stride)
NewPositionY = CurrentPositionY + (CosineTable(Heading) * Stride)
'/*Checks if it's walkthroughable*/'
'4.For every movement and after computations, update the graphic
'**PCOPY Background1, WorkPage: Swaps Background1 with 2
'For each viewing field vertical ray from left to right you are viewing...
X1 = 0
FOR A = Heading + 32 TO Heading  31 STEP 1
StepX = SinuseTable(A): StepY = CosineTable(A)
XX = CurrentPositionX: YY = CurrentPositionY
L = 0
DO
'OK, this loop test if there's any wall to draw...
XX = XX  StepX: YY = YY  StepY
L = L + 1
K = Map's XX and YY
LOOP UNTIL K 'Yeah, found one, draw it
DD = BlockWidth \ L 'BlockWidth=900This is the wall's width
H = DD + DD 'Height of your EYE from the ground
DT = Phi  DD 'A higher value would see the ceiling 'Lower would see the floor :)
LINE (X1, DT)STEP(4, H), K ,BF
X1 = X1 + 5 'Why 5? See below for further explanation
NEXT
'Okay, See that FOR A = Heading + 32 TO Heading  31 STEP 1 ?
'That's about 64 loops, so each wall block will be cut into 64 pieces and drew separately
'Hey, that's blocky(Coz' it speeds up everythin') So, since we are using 320X200 so tha'
'X1 = X1 + 5 makes us draw each pieces with width of 5 and 64 * 5 = 320 horizontal
'phew!