Programming by Example

 

A BB4W Compendium

freeman69@gmx.com

IDIC BBC_Owl2 M&P

Maze

The first of a group of programs demonstrating basic 3D principles.

 

         W = Forward

         S = Back

         A = Turn Anticlockwise

         D = Turn Clockwise

 

This program covers perspective and inhibiting the drawing of planes not facing the viewer.

 

  10 MODE 9:OFF

  20 VDU 24,0;0;959;959;:REM Graphics window

  30 ORIGIN 480,480

  40 DIM map(9,9)

  50 REM Read maze pattern into 'map' array

  60 FOR z=0 TO 9:READ m$

  70   FOR x=0 TO 9:map(x,9-z)=INSTR(" X",MID$(m$,x+1,1))-1

  80   NEXT

  90 NEXT

 100 DIM box(9):box()=1,0,1,2,-1,2,-1,0,1,0:REM Wall square

 110 VDU 23,23,2;0;0;0;:REM Line width 2

 120 MAXd=9:REM Max depth of vision

 130 px=6:pz=8:REM Location

 140 pdir=3:REM 0=North, 1=East, 2=South, 3=West

 150 lastr=-99:REM Used to inhibit drawing of left side of wall

 160

 170 SLOWDRAW=FALSE:REM Set to TRUE to see how it works

 180 IF NOT SLOWDRAW THEN

 190   *REFRESH OFF

 200 ENDIF

 210

 220 REPEAT

 230   CLS

 240   COLOUR 7:PROCeyeball(px,pz,pdir)

 250   COLOUR 1:PRINTTAB(px+30,9-pz);MID$("^>v<",pdir+1,1)

 260   RECTANGLE -476,-480,955,956

 270   *REFRESH

 280   k$=CHR$(ASC(GET$)AND223)

 290   IF k$="A" pdir-=1:IF pdir<0 pdir+=4

 300   IF k$="D" pdir+=1:IF pdir>3 pdir-=4

 310   IF k$="W" PROCreqmove(1)

 320   IF k$="S" PROCreqmove(-1)

 330 UNTIL FALSE

 340 END

 350

 360 DEF PROCreqmove(d)

 370 LOCAL x,z

 380 CASE pdir OF

 390   WHEN 0 : x=px:z=pz+d

 400   WHEN 1 : x=px+d:z=pz

 410   WHEN 2 : x=px:z=pz-d

 420   WHEN 3 : x=px-d:z=pz

 430 ENDCASE

 440 IF FNwayclear(x,z) px=x:pz=z

 450 ENDPROC

 460

 470 DEF FNwayclear(x,z)

 480 IF x<0 OR x>9 OR z<0 OR z>9 THEN

 490   =FALSE

 500 ELSE

 510   IF map(x,z)=0 THEN

 520     =TRUE

 530   ELSE

 540     =FALSE

 550   ENDIF

 560 ENDIF

 570

 580 REM Reads the map array in a V shape extending from viewer

 590 REM Always reads/scans: far to near, left to right

 600 DEF PROCeyeball(px,pz,pdir)

 610 LOCAL dx,dz,rx,rz,xb,zb

 620 CASE pdir OF

 630   WHEN 0 : dx=0:dz=-1:rx=1:rz=0:xb=px-MAXd:zb=pz+MAXd-1:REM N

 640   WHEN 1 : dx=-1:dz=0:rx=0:rz=-1:xb=px+MAXd-1:zb=pz+MAXd:REM E

 650   WHEN 2 : dx=0:dz=1:rx=-1:rz=0:xb=px+MAXd:zb=pz-MAXd+1:REM S

 660   WHEN 3 : dx=1:dz=0:rx=0:rz=1:xb=px-MAXd+1:zb=pz-MAXd:REM W

 670 ENDCASE

 680 FOR d=0 TO MAXd-1

 690   FOR r=d-MAXd TO MAXd-d

 700     IF xb>=0 AND xb<=9 AND zb>=0 AND zb<=9 THEN

 710       IF map(xb,zb)=1 PROCdrawwall(r,MAXd-d,pdir,xb,zb)

 720       PRINTTAB(xb+30,9-zb);MID$(".X",map(xb,zb)+1,1);

 730     ENDIF

 740     xb+=rx

 750     zb+=rz

 760   NEXT

 770   xb-=rx*(MAXd-d)*2

 780   zb-=rz*(MAXd-d)*2

 790   xb+=dx

 800   zb+=dz

 810 NEXT

 820 ENDPROC

 830

 840 DEF PROCdrawwall(cx,cz,pdir,xb,zb)

 850 LOCAL sf,h,x1,z1,x2,z2,sx1,sy1,sx2,sy2,ny

 860 sf=960:h=1

 870 FOR side=0 TO 3

 880   x1=box(side*2+0)+cx*2:z1=box(side*2+1)+cz*2

 890   x2=box(side*2+2)+cx*2:z2=box(side*2+3)+cz*2

 900   sx1=sf*x1/z1:sy1=sf*h/z1

 910   sx2=sf*x2/z2:sy2=sf*h/z2

 920   ny=sx2-sx1

 930   REM Does this side face the viewer? (side 1 never drawn)

 940   IF ny>0 THEN

 950     REM Prevent side(2) of current wall overwriting front of previous adjoining wall

 960     IF side<>2 OR cx<>lastr+1 THEN

 970       GCOL 0,5:MOVE sx1,sy1:MOVE sx1,-sy1:REM Solid wall

 980       PLOT 85,sx2,sy2:PLOT 85,sx2,-sy2

 990       GCOL 0,7:MOVE sx1,sy1:DRAW sx2,sy2:REM Outline

1000       DRAW sx2,-sy2:DRAW sx1,-sy1:DRAW sx1,sy1

1010       lastr=cx

1020     ENDIF

1030   ENDIF

1040   IF SLOWDRAW WAIT 1

1050 NEXT

1060 ENDPROC

1070

1080 DATA "XXXXXXXXXX"

1090 DATA "X X      X"

1100 DATA "X   XXXX X"

1110 DATA "X X      X"

1120 DATA "X X X XXXX"

1130 DATA "XXX X    X"

1140 DATA "X X XXX XX"

1150 DATA "X    X   X"

1160 DATA "X  X   X X"

1170 DATA "XXXXXXXXXX"

Maze

Maze: Code explained...

 

Drawing in 3D involves creating the illusion of depth upon a 2D screen (window).

 

PROCeyeball reads the 'map' data in the order in which they need to be drawn i.e. from far to near (scanning left to right). By amending line 170 SLOWDRAW=TRUE, the program will demonstrate how the contents of the image are created, wall segment by wall segment.

 

The illusion of depth is achieved by projecting points in 3D space onto our 2D window. This is accomplished using simple formulae:

 

Xs = f X / Z

Ys = f Y / Z

 

Where 'Xs','Ys' are the 2D screen co-ordinates and 'f' is a factor directly related to the angle of view:

Conversion

The blue circles represent points in 3D space.

'w' is the width of the window.

'f' is the angle of view factor.

 

When f  = w, the angle of view is approximately 53 degrees.

When f = w/2, the angle of view is 90 degrees.

 

(Note that complications arise when a line or plane intersects the window. The maze program was written so this situation does not arise: the viewer is always placed on the edge of the current grid square, with a view across the current grid square. i.e. not in the centre of a grid square.)

 

 

Hiding planes not facing the viewer:

 

This is a technique employed to reduce the amount of graphics needing to be drawn. Recall from the Vector Demo that we can determine which side of a line a point is. This principle can be applied to determine if a line is facing the viewer, or facing away. Lines 920 & 940 execute a simplified version of this test to determine which of the four sides of a wall segment (a square based column) face the viewer.

 

    Side zero only faces the viewer when the wall segment is to our left.

    Side 1 always faces away

    Side 2 only faces the viewer when the wall segment is to our right.

    (N.b. because we always draw wall segments from left to right, it is possible that side 2 of a wall segment may (incorrectly and) partially overwrite side 3 of the previously drawn, adjacent wall segment. Temporarily REM/remove line 960 to see how this error affects the display.)

    Side 3 always faces the viewer.

Hidden faces Arrow black large Arrow black large