Simple raycaster sample in 8th

Here is a simple raycaster sample in 8th:

\
\ Simple raycaster
\
\ Raycasting code "borrowed" from Lode Vandevenne and adapted for the 8th programming language.
\
\ Original graphics files, before I messed them up are from Al Steven's raycaster demo: Tubas of Terror
\ published in Dr. Dobbs journal 1995. Used with permission.
\
needs nk/gui
needs nk/render-timed
needs nk/keyboard
needs math/trigd

libbin font/Roboto-Regular.ttf
font/Roboto-Regular.ttf font:new "font1" font:atlas! drop

640 constant WIDTH
480 constant HEIGHT

24 constant MAP-WIDTH
24 constant MAP-HEIGHT

1.0 constant CEILING-HEIGHT

64 constant TEXTURE-WIDTH
85 constant TEXTURE-HEIGHT

nullvar texture

"tiles/wall01.png" app:asset img:new constant texture1
"tiles/wall02.png" app:asset img:new constant texture2
"tiles/door01.png" app:asset img:new constant texture3
"tiles/door02.png" app:asset img:new constant texture4
"tiles/door03.png" app:asset img:new constant texture5
"tiles/door04.png" app:asset img:new constant texture6

[ @texture1 , @texture3 , @texture5 ] constant textures
[ @texture2 , @texture4 , @texture6 ] constant dim-textures

[ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,
  1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,
  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
  1,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ] [ 24, 24 ] mat:new  var, map

[ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,
  1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,
  1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ] [ 24, 24 ] mat:new  var, collision-map

14.0 var, pos-x
16.0 var, pos-y
-0.6 var, dir-x
0.0  var, dir-y

var old-dir-x
var old-dir-y

0.0 var, plane-x
0.66 var, plane-y

\ Tiles per second.
5 constant MOVE-SPEED
3 constant ROT-SPEED

0 var, time
var old-time
var frame-time

ns? ns: raycast

\ vars used in raycasting walls
var camera-x
var ray-dir-x
var ray-dir-y
var map-x
var map-y
var side-dist-x
var side-dist-y
var delta-dist-x
var delta-dist-y
var perp-wall-dist
var step-x
var step-y
var side
var line-height
var draw-start
var draw-end
var wall-x
var texture-x
var texture-y-start
var texture-y-end

: half 2 n:/ ;
: half+ half n:+ ;
: half- half n:- ;

: raycast-loop
  dup
  2 WIDTH n:*/ n:1- camera-x !
  plane-x @ camera-x @ n:* dir-x @ n:+ ray-dir-x !
  plane-y @ camera-x @ n:* dir-y @ n:+ ray-dir-y !

  pos-x @ n:int map-x !
  pos-y @ n:int map-y !

  1 ray-dir-x @ n:/ n:abs delta-dist-x !
  1 ray-dir-y @ n:/ n:abs delta-dist-y !

  ray-dir-x @ 0 n:< if
    -1 step-x !
    pos-x @ map-x @ n:- delta-dist-x @ n:* side-dist-x !
  else
    1 step-x !
    map-x @ 1.0 n:+ pos-x @ n:- delta-dist-x @ n:* side-dist-x !
  then

  ray-dir-y @ 0 n:< if
    -1 step-y !
    pos-y @ map-y @ n:- delta-dist-y @ n:* side-dist-y !
  else
    1 step-y !
    map-y @ 1 n:+ pos-y @ n:- delta-dist-y @ n:* side-dist-y !
  then

  repeat
    side-dist-x @ side-dist-y @ n:< if
      delta-dist-x @ side-dist-x n:+!
      step-x @ map-x n:+!
      0 side !
    else
      delta-dist-y @ side-dist-y n:+!
      step-y @ map-y n:+!
      1 side !
    then

    map-x @ map-y @ map @ mat:@ nip dup if
      \ Leave texture number on TOS.
      n:int n:1-
      break
    else
      drop
    then
  again

  side @ !if
    map-x @ pos-x @ n:- 1 step-x @ n:- half+ ray-dir-x @ n:/ perp-wall-dist !
  else
    map-y @ pos-y @ n:- 1 step-y @ n:- half+ ray-dir-y @ n:/ perp-wall-dist !
  then

  HEIGHT CEILING-HEIGHT perp-wall-dist @ n:*/ line-height !

  line-height @ n:neg half HEIGHT half+ draw-start !
  draw-start @ 0 n:max draw-start !

  line-height @ half HEIGHT half+ draw-end !
  draw-end @ HEIGHT n:< !if
    HEIGHT n:1- draw-end !
  then

  side @ !if
    perp-wall-dist @ ray-dir-y @ n:* pos-y @ n:+ wall-x !
  else
    perp-wall-dist @ ray-dir-x @ n:* pos-x @ n:+ wall-x !
  then

  wall-x @ dup n:int n:- TEXTURE-WIDTH n:* texture-x !

  side @ !if
    textures swap a:_@ texture !
    ray-dir-x @  if
      TEXTURE-WIDTH texture-x @ n:- n:1- texture-x !
    then
  else
    dim-textures swap a:_@ texture !
    ray-dir-y @  !if
      TEXTURE-WIDTH texture-x @ n:- n:1- texture-x !
    then
  then

  >r  \ store column

  draw-start @ HEIGHT half- line-height @ half+
  TEXTURE-HEIGHT line-height @ n:*/ texture-y-start !

  draw-end @ HEIGHT half- line-height @ half+ 
  TEXTURE-HEIGHT line-height @ n:*/ texture-y-end !

  r> draw-start @ 1 draw-end @ draw-start @ n:- 4 a:close
  texture @
  "white"
  texture-x @ texture-y-start @ 1 texture-y-end @ texture-y-start @ n:- 4 a:close
  nk:draw-sub-image ;

: gen-view
  ' raycast-loop 0 WIDTH n:1- loop ;

ns

: secs
  d:ticks d:ticks/sec n:/ ;

: draw-game
  \ Ceiling
  [ 0, 0, @WIDTH  , ` HEIGHT 2 n:/ n:int ` ] 0 "darkgrey" nk:fill-rect

  \ Walls
  raycast:gen-view ;

 : update-game
   time @ old-time ! 
   secs time !
   time @ old-time @ n:- frame-time !

   scan:RIGHT nk:scancode? if
     dir-x @ >r
     r@ ROT-SPEED frame-time @ n:* n:neg n:cos n:* dir-y @ ROT-SPEED frame-time @ n:* n:neg n:sin n:* n:- dir-x !
     r> ROT-SPEED frame-time @ n:* n:neg n:sin n:* dir-y @ ROT-SPEED frame-time @ n:* n:neg n:cos n:* n:+ dir-y !
     plane-x @ >r
     r@ ROT-SPEED frame-time @ n:* n:neg n:cos n:* plane-y @ ROT-SPEED frame-time @ n:* n:neg n:sin n:* n:- plane-x !
     r> ROT-SPEED frame-time @ n:* n:neg n:sin n:* plane-y @ ROT-SPEED frame-time @ n:* n:neg n:cos n:* n:+ plane-y !
   then

   scan:LEFT nk:scancode? if
     dir-x @ >r
     r@ ROT-SPEED frame-time @ n:* n:cos n:* dir-y @ ROT-SPEED frame-time @ n:* n:sin n:* n:- dir-x !
     r> ROT-SPEED frame-time @ n:* n:sin n:* dir-y @ ROT-SPEED frame-time @ n:* n:cos n:* n:+ dir-y !
     plane-x @ >r
     r@ ROT-SPEED frame-time @ n:* n:cos n:* plane-y @ ROT-SPEED frame-time @ n:* n:sin n:* n:- plane-x !
     r> ROT-SPEED frame-time @ n:* n:sin n:* plane-y @ ROT-SPEED frame-time @ n:* n:cos n:* n:+ plane-y !
   then

   scan:DOWN nk:scancode? if
     pos-x @ dir-x @ MOVE-SPEED frame-time @ n:* n:* n:- n:int pos-y @ n:int collision-map @ mat:@ nip not if
       pos-x @ dir-x @ MOVE-SPEED frame-time @ n:* n:* n:- pos-x !
     then
     pos-x @ n:int pos-y @ dir-y @ MOVE-SPEED frame-time @ n:* n:* n:- n:int collision-map @ mat:@ nip not if
       pos-y @ dir-y @ MOVE-SPEED frame-time @ n:* n:* n:- pos-y !
     then
   then

   scan:UP nk:scancode? if
     dir-x @ MOVE-SPEED frame-time @ n:* n:* pos-x @ n:+ n:int pos-y @ n:int collision-map @ mat:@ nip not if
       dir-x @ MOVE-SPEED frame-time @ n:* n:* pos-x @ n:+ pos-x !
     then
     pos-x @ n:int dir-y @ MOVE-SPEED frame-time @ n:* n:* pos-y @ n:+ n:int collision-map @ mat:@ nip not if
       dir-y @ MOVE-SPEED frame-time @ n:* n:* pos-y @ n:+ pos-y !
     then
   then

   scan:ESCAPE nk:scancode? if
     bye
   then ;

: new-win
  {
    name: "main",
    wide: @WIDTH,
    high: @HEIGHT,
    resizable: false,
    bg: "black",
    title: "Raycaster"
  }

  nk:win ;

: main-render
  {
    title: "game",
    bg: "black",
    padding: [0,0],
    flags: [ @nk:WINDOW_NO_SCROLLBAR ]
  }
  
  nk:begin
    draw-game
    nk:timer? if
      update-game
    then  
  nk:end ;

: app:main
  new-win ' main-render 100 60 n:/ nk:render-timed ;

4 Likes

nice kind of Wolfenstein3D :stuck_out_tongue:

2 Likes

Scenekit in Swift does a much better/faster/cleaner job of it… and is way easier to understand than this 8th “stuff”

The 8th code looks pretty straightforward to me

Cool!

Probably, but there are also better game libraries available that don’t lock you into Apple ecosystem.

I personally think Apple made an error purchasing NEXT and using NeXTSTEP as a basis for the succesor to the classic Mac OS. Purhasing Be and using BeOS would have been much more interesting option at the time.

1 Like

A post was split to a new topic: Reminiscing about Daystar & BeOS