I came across this 1-year old post about an awesome raycasting engine written entirely in TI-Basic, I've been reading over the code for a few hours now and there are some errors preventing it from running correctly. I would love to get it working because this seems like a great way for me to better understand the concepts of raycasting and implementation. Here's a link to the original post (https://www.cemetech.net/forum/viewtopic.php?t=13579) and here's the code

Code:

Degree
0→Xmin
{BLACK,DARKGRAY,GRAY,MEDGRAY,LTGRAY,WHITE,WHITE→L₁
70→Xmax
-20→Ymin
20→Ymax
90→θ
310→R
119→S
219→T
For(A,1,51,-1
For(B,1,100,50
Pxl-On(163-A,263-B,GRAY
Pxl-On(163-B,263-A,GRAY
Pxl-On(164-A,264-B,GRAY
Pxl-On(110+A,210+B,MEDGRAY
Pxl-On(110+B,210+A,MEDGRAY
Pxl-On(109+B,209+A,MEDGRAY
Pxl-On(109+A,209+B,MEDGRAY
End
End

For(A,1,10
For(B,1,10
If [A](A,B
Then
For(C,1,5
For(D,1,5
Pxl-On(110+(5A-4+C),210+(5B-4+D),BLACK
End
End
End
End
End
Repeat Ans
For(A,­6,13.5,.45
Line(Xmin,A,Xmax,A,WHITE
End

DelVar N
Pxl-Off(S,T
For(A,70-R,1-R,-­1
N+1→N
DelVar C
cos(A→P
sin(A→Q
Repeat K=45 or C>20
C+1→C
pxl-Test(int(S+CP),int(T+CQ
End
If C<20:Then
int(C/3→V
int(40/C)/2→W
If W>4
4→W
For(B,­-W,W
For(D,1,2
Pt-On(N,B,D,L₁(V
End
End
End
End
Pxl-On(S,T
Repeat Ans
getKey
End
Pxl-Off(S,T
Ans→K
If K=24
R-45→R
If K=26
R+45→R
R-220→θ
If K=25
Then
int(S-3cos(θ→S
int(T+3sin(θ→T
Else
If K=34
Then
int(S+3cos(θ→S
int(T-3sin(θ→T
End
End
Pxl-On(S,T
Pause
End


Here is where I'm at with it:
First off It doesn't clear the graph screen or turn off the axis, leaving it with a messy look, So the first thing I did was add those commands.

For Variables, S and T are the x,y corrdinents of the players pxl on the minimap(Not sure which ones which atm)
R is for raycast? Later on, it is subtracted from some numbers for some reason, and those numbers are responsible for shootin' out them rays.


Code:

For(A,1,51,-1
For(B,1,100,50
Pxl-On(163-A,263-B,GRAY
Pxl-On(163-B,263-A,GRAY
Pxl-On(164-A,264-B,GRAY
Pxl-On(110+A,210+B,MEDGRAY
Pxl-On(110+B,210+A,MEDGRAY
Pxl-On(109+B,209+A,MEDGRAY
Pxl-On(109+A,209+B,MEDGRAY
End
End

For(A,1,10
For(B,1,10
If [A](A,B
Then
For(C,1,5
For(D,1,5
Pxl-On(110+(5A-4+C),210+(5B-4+D),BLACK
End
End
End
End
End

This Right here just draws the mini-map in the bottom right corner and where I believe the first error is, Shouldn't it be For(A,1,51) instead of For(A,1,51,-1)? I delete the -1 and this part of the code works correctly.

The next 4 lines,

Code:

Repeat Ans
For(A,­6,13.5,.45
Line(Xmin,A,Xmax,A,WHITE
End

Draw white lines from the top of the minimap to about 3/4ths of the way up the screen. I think this is to clear what is shown once raycast projection is shown and redrawn with movement, But I haven't gotten that working so right now it's useless.


Code:

DelVar N
Pxl-Off(S,T
For(A,70-R,1-R,-­1
N+1→N
DelVar C
cos(A→P
sin(A→Q
Repeat K=45 or C>20
C+1→C
pxl-Test(int(S+CP),int(T+CQ
End
If C<20:Then
int(C/3→V
int(40/C)/2→W
If W>4
4→W
For(B,­-W,W
For(D,1,2
Pt-On(N,B,D,L₁(V
End
End
End
End

Here is where the fun happens! (And all my problems)
For(A,70-R,1-R,-1) ; Here is where I said R was subtracted from numbers for some reason.
A is used to create P & Q, which is used in the piece of code I think is broken.

Code:

Repeat K=45 or C>20
C+1→C
pxl-Test(int(S+CP),int(T+CQ
End

This pxl-Test is shooting the rays from the player, sweeping across about a 60° angle, and testing to see what pixels are black and what pixels are white. But this isn't used for anything! its just there?
The code afterward draws the raycast projection, but only if C is less than 20? But the loop above only terminates once C greater than 20?
Hoping someone else smarter than me can fix this because I'm stumped (Also I've never built a raycast So I don't know what to do to begin with Laughing )
Unfortunately, the program I posted was sub-par and completely dysfunctional.

A short while back, I happened to come back to this concept and rewrote my code for it, so here's my new code (import this with SC3):

Code:
StoreGDB 0
ZStandard
ZSquare
GridOff
AxesOff
LabelOff
BorderColor 4
BackgroundOff
{Black,DarkGray,Gray,MedGray,LtGray->L1
For(A,1,10
   For(B,1,10
      If [A](A,B
      Then
         For(C,1,5
            For(D,1,5
               Pxl-On(115+5A-C,5B-D,Black
            End
         End
      End
   End
End
122->A
5->B
DelVar DRepeat K=45
   For(theta,~3,3,.1
      Line(12+theta,3,12+theta,10,0
   End
   Circle(12,6,1.8,Black
   Line(10,6,14,6,1,Black,1
   Line(12,4,12,8,1,Black,1
   For(theta,~40+D,40+D
      If not(remainder(abs(theta-D),10
      Line(12+2.5cos(theta-2D),6+2.5sin(theta-2D),12+2cos(theta-2D),6+2sin(theta-2D),1,Red+(theta=D),1
      sin(theta->U
      cos(theta->V
      DelVar NRepeat Ans or N=10
         N+1->N
         int(A+NU->I
         int(B+NV->J
         pxl-Test(I,Ans
      End
      (theta-D)/5
      Line(Ans,5,Ans,~5,0
      If N!=10
      Line(Ans,(10-N)/2,Ans,~(10-N)/2,1,L1(1+min(int(N/2),dim(L1
   End
   If not(pxl-Test(A,B
   Pxl-On(A,B,Red
   getKey
   Repeat Ans
      getKey
   End
   Ans->K
   Pxl-Off(A,B
   D-5(K=11->D
   D+5(K=15->D
   D-2(K=12->D
   D+2(K=14->D
   int(A+(K=13)2sin(D)+(K=23)5sin(D->A
   int(B+(K=13)2cos(D)+(K=23)5cos(D->B
End
RecallGDB 0


In concept, this is essentially the same program, but I like it much better because it is more concise.

Note that to move or turn, you use the top 5 buttons on the keypad, namely [y=], [window], [zoom], [trace], and [graph].

Let's break this program down into manageable chunks, which I will explain one-by-one.


Code:
StoreGDB 0
ZStandard
ZSquare
GridOff
AxesOff
LabelOff
BorderColor 4
BackgroundOff
{Black,DarkGray,Gray,MedGray,LtGray->L1


This section is pretty much my standard "initialize the graphscreen" set of commands. I store the user's settings to GDB0 to retrieve them when the program is over.

The line storing colors of increasing brightness to L1 is important. It will be explained thoroughly later in this post.


Code:
For(A,1,10
   For(B,1,10
      If [A](A,B
      Then
         For(C,1,5
            For(D,1,5
               Pxl-On(115+5A-C,5B-D,Black
            End
         End
      End
   End
End


This code loops through matrix A, drawing a 5x5 "pixel" on the minimap in the bottom left corner of the screen if the value is truthy (i.e. not 0). We use this minimap to do the raycasting.


Code:
122->A
5->B
DelVar D


The variables A and B store the row and column (because we are using pixels, they are not the X and Y coordinates) of the player. The D variable stores our rotation. Because DelVar effectively sets it to 0 and 0° is directly left, the player starts out facing left.


Code:
Repeat K=45
   For(theta,~3,3,.1
      Line(12+theta,3,12+theta,10,0
   End


Here, we start the main loop. We clear the area where the compass will be redrawn each frame.


Code:
   Circle(12,6,1.8,Black
   Line(10,6,14,6,1,Black,1
   Line(12,4,12,8,1,Black,1


This draws a nice little compass in the top right corner of the screen, so that the player knows which direction he or she is facing.


Code:
   For(theta,~40+D,40+D
      If not(remainder(abs(theta-D),10
      Line(12+2.5cos(theta-2D),6+2.5sin(theta-2D),12+2cos(theta-2D),6+2sin(theta-2D),1,Red+(theta=D),1
      sin(theta->U
      cos(theta->V


Here is the start of the meat of the program.

We sweep through an 80° arc, centered around the player's rotation value.

The If not(remainder(abs(theta-D),10 part and the line following it draw markers on the compass to show the progress. The If statement makes sure that we only draw lines at 10-degree intervals.

As an extra layer of optimization, we store the sine and cosine values of our angle to U and V, respectively, so that we don't have to recalculate them in the next step.


Code:
      DelVar NRepeat Ans or N=10
         N+1->N
         int(A+NU->I
         int(B+NV->J
         pxl-Test(I,Ans
      End


This is where the actual raycasting happens.

I reset the variable N to 0. It stores the length of the ray.
We basically keep increasing the radius of an imaginary circle (centered on our player) by 1, calculating the point on this circle at our angle, θ, and checking if there is a pixel drawn there. To save time, if the circle has a radius equal to 10 and we still haven't hit anything, we stop calculating the ray. The value of 10 is essentially our "maximum render distance".

A raycaster basically casts a ray from the player's position at each angle in the player's FOV, measuring the distance the ray travels until it hits a wall.


Code:
      (theta-D)/5
      Line(Ans,5,Ans,~5,0
      If N!=10
      Line(Ans,(10-N)/2,Ans,~(10-N)/2,1,L1(1+min(int(N/2),dim(L1
   End


This is most of our drawing code, and also the ending of our drawing loop, where we were iterating through each of the angles.

We clear out anything on our "canvas" where the line is going to go. If we didn't prevent the ray from continuing to propagate, we draw a line on our graph. The X coordinate is reliant on the angle, while the vertical length of the line and the color of the line is solely influenced by the length of the ray. If the ray took longer to hit a wall, the line should be shorter and lighter in color, because it is further away.


Code:
   If not(pxl-Test(A,B
   Pxl-On(A,B,Red


You might have noticed that we never actually drew the player in on the minimap yet. This is because some rays would hit the player and register as there being a wall right where the player is, even if there isn't one. If we didn't stop the ray from continuing to propagate, we can safely draw this in now. I first do a check to make sure that we are not inside of a wall. I should probably add player collision checking, but it's a trivial thing in this scenario.


Code:
   getKey
   Repeat Ans
      getKey
   End
   Ans->K


This section of code heads our user input.

The first getKey prevents any keys that were pressed during the drawing from being registered in our movement-handling code. I don't fully understand how or why this happens, but it does.

We then wait for the user to press a key, and store the key code they pressed to K.


Code:
   Pxl-Off(A,B
   D-5(K=11->D
   D+5(K=15->D
   D-2(K=12->D
   D+2(K=14->D
   int(A+(K=13)2sin(D)+(K=23)5sin(D->A
   int(B+(K=13)2cos(D)+(K=23)5cos(D->B


Next, we turn off the pixel on the minimap representing the player. I have explained why we need to do this earlier in the post. The next section, which I intentionally left unoptimized, adjusts the rotation of the player. If the user presses the [y=] button, we assume they want to rotate more than if they pressed the [window] button. After this section of code, we move the player.

If the player pressed the [zoom] button, we move them forward by (approx.) 2 pixels. Because the rendering process is so slow, though, we also give them the option to move forward by 5 pixels by pressing [del].


Code:
End
RecallGDB 0


This last little bit of code closes up our main loop. When the user presses [clear], the loop stops (because of the "K=45" at the beginning), and we recall the user's graph settings back.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement