There’s a 3D Flying Dragon inside microM8’s [Applesoft] microBASIC Interpreter!

Why you ask? Because we can! And now you can too!

It made a sort of logical sense that if our microM8 Apple II emulator rendered in 3D (it does) and it had its own Applesoft-compatible BASIC interpreter (it does), we should make a 3D graphics mode. That way, users could create Applesoft BASIC games in 3D (because why not? It would be cool!) And so we did.

As a demonstration of that mode, we wrote a program that creates a little dragon flying inside a rotating cube. It uses GR5, which is a 48x48x48 graphics mode. (GR6 is 96x96x96 and GR7 is 128x128x128, For reference, GR is 40×40, GR2 is 40×48, GR3 is 80×40 and GR4 is 80×48 – these first four modes are two-dimensional).

Since you can control the camera with BASIC commands, we took advantage of that as well – as time goes on the camera zooms in and out, as well as rotating. This is the result:

This slideshow requires JavaScript.

Now, we understand that our little dragon may not be easily recognisable as one at first glance (it is made out of cubes, after all) but I think you can agree it’s got potential… a second dragon, some controls and there could be a game here!

Take a look at the program’s listing below to get a sense of how it works, and maybe give you some ideas for a game or demo of your own…

This is the program. It could use better commenting to be more readable but the important bits are that the PLOT command takes 3 parameters (X, Y and Z) instead of just two, and the @RENDER.SUSPEND{0 for off, 1 for on} function suspends rendering so that the bird can be drawn and undrawn without causing flicker.

Random triggers are used to cause the dragon or the 'cage' to change colour, and the dragon to change direction (there are eight plot subroutines, consisting of wings up and wings down in four directions).

1000  rem 3D Dragon Demo

1001  gr5 : rem Go into 48x48x48 graphics mode

1005  @CAMERA.ASPECT{ 1 } : rem Set the camera aspect ratio to 1 to 1 (square). microM8's non-standard functions are prefixed by @ and use braces {} to avoid breaking compatibility with existing Applesoft BASIC listings.

1010  BIRDCOL = INT( RND( 1 ) * 15 ) + 1 : rem Set the initial colour of the 'bird' (the dragon). gr5 uses the standard Apple II low-resolution colours.

1015  CAGECOL = INT( RND( 1 ) * 15 ) + 1 : rem Set the initial colour of the 'cage'

1020  DIR = 0 : rem Set the initial DIRection of the dragon

1030  M = 14.5 : rem Variable holding the camera zoom level

1031  N = 0.1 : rem Variable holding the amount of zoom change per cycle

1035  @CAMERA.ZOOM{ M } : rem Set the camera zoom to M

1040  gosub 2000 : rem Jump to cage drawing routine

1050  X = 20 : Y = 20 : Z = 20 : rem Set the initial position of the dragon

1060  if U = 0 then @CAMERA.ROTATE{ .25 , .25 , 0 } else @CAMERA.ROTATE{ - .25 , .25 , 0 } : rem Rotate the camera one way or the other depending on switch U

1065  COLOR= BIRDCOL : rem Set the drawing colour to BIRDCOL

1070  gosub 1250 + DIR : rem Jump to the dragon 'first pose' (wings down) drawing subroutine indexed by the dragons DIRection. This technique is common and useful.

1071  @RENDER.SUSPEND{ 0 } : rem Restart rendering if stopped.

1072  for W = 1 to 50 : next W : rem A simple pause

1075  if U = 0 then @CAMERA.ROTATE{ .25 , .25 , 0 } else @CAMERA.ROTATE{ - .25 , .25 , 0 } : rem Rotate more. There are multiple rotate statements in order to make the motion more fluid.

1080  COLOR= 0 : rem Set the drawing colour to black (to erase the dragon)

1085  @RENDER.SUSPEND{ 1 } : rem Stop rendering while we erase and redraw the dragon.

1090  gosub 1250 + DIR : rem Jump to the dragon drawing subroutine indexed by DIR and erase the dragon. We can't see this happen because rendering is suspended.

1095  if D = 1 then gosub 2100 : rem If we're currently redrawing (changing the colour of) the cage, jump to the redraw subroutine

1100  R = INT( RND( 1 ) * 10 ) + 1 : rem Generate a random number used to determine the dragon's movement

1105  rem Move the dragon based on random number R

1110  if R = 1 then X = X + 1

1120  if R = 2 or R = 9 then Y = Y + 1

1130  if R = 3 or R = 4 then Z = Z + 1

1140  if R = 5 then X = X - 1

1150  if R = 6 or R = 10 then Y = Y - 1

1160  if R = 7 or R = 8 then Z = Z - 1

1161  rem Check to see if we're out of bounds and move back in if we are

1162  if X > 41 then X = 41

1163  if Y > 41 then Y = 41

1164  if Z > 41 then Z = 41

1165  if X < 6 then X = 6

1166  if Y < 6 then Y = 6

1167  if Z < 6 then Z = 6

1168  rem Add a little extra 'push' in the direction the dragon is facing

1170  if DIR = 0 then Z = Z - 1

1180  if DIR = 100 then Z = Z + 1

1190  if DIR = 200 then X = X - 1

1200  if DIR = 300 then X = X + 1

1205  if U = 0 then @CAMERA.ROTATE{ .25 , .25 , 0 } else @CAMERA.ROTATE{ - .25 , .25 , 0 } : rem Rotate camera

1210  COLOR= BIRDCOL : rem The following statements draw the dragon again, in its alternate pose (wings up)

1211  gosub 1290 + DIR

1212  @RENDER.SUSPEND{ 0 } : rem Turn rendering back on. By turning rendering off and on, we only see the dragon in its fully drawn state

1213  for W = 1 to 50 : next W

1214  if U = 0 then @CAMERA.ROTATE{ .25 , .25 , 0 } else @CAMERA.ROTATE{ - .25 , .25 , 0 }

1215  if U = 0 then V = V + 1 else V = V - 1 : rem V is a counter that is used to change direction of rotation

1216  M = M + N : rem Change the zoom amount M by N

1217  @CAMERA.ZOOM{ M }

1218  if M > 20 then N = -0.1 : rem If M is more than 20, make N negative. This will make us start to zoom back out.

1219  if M < 9 then N = 0.1 : rem If M is less than 9, make N positive. This will make us start to zoom in again.

1220  COLOR= 0 : rem 'Undraw' the dragon

1221  @RENDER.SUSPEND{ 1 }

1222  gosub 1290 + DIR

1230  W = INT( RND( 1 ) * 100 ) : rem Generate a random number W used to determine changes

1231  if W > 90 then DIR = 100 * INT( RND( 1 ) * 4 ) : rem Pick a new random direction

1232  if W > 95 then BIRDCOL = INT( RND( 1 ) * 15 ) + 1 : rem Pick a new dragon colour

1235  W = INT( RND( 1 ) * 100 ) : rem Generate a new random number W

1236  if W < 5 then if D = 0 then D = 1 : CAGECOL = INT( RND( 1 ) * 15 ) + 1 : L = 0 : rem If W is less than 5 then change the cage colour if its not already being changed

1237  if D = 1 then gosub 2100 : rem Incrementally change the cage colour

1238  if V = -50 then U = 0 : rem Change the direction of rotation

1239  if V = 50 then U = 1

1240  goto 1060 : rem Repeat forever

1245  rem Dragon drawing routines follow. They are broken up into wings down and wings up, in each of four directions

1250  plot X , Y , Z : plot X - 1 , Y , Z : plot X - 2 , Y + 1 , Z

1251  plot X , Y , Z + 1 : plot X - 1 , Y , Z + 1 : plot X - 2 , Y + 1 , Z + 1

1252  plot X - 3 , Y + 2 , Z : plot X - 3 , Y + 2 , Z + 1

1260  plot X + 1 , Y - 1 , Z : plot X + 1 , Y - 1 , Z + 1 : plot X + 1 , Y - 2 , Z - 1 : plot X + 1 , Y - 2 , Z + 2 : plot X + 1 , Y - 2 , Z - 2 : plot X + 1 , Y - 3 , Z + 3

1270  plot X + 2 , Y , Z : plot X + 3 , Y , Z : plot X + 4 , Y + 1 , Z

1271  plot X + 2 , Y , Z + 1 : plot X + 3 , Y , Z + 1 : plot X + 4 , Y + 1 , Z + 1

1272  plot X + 5 , Y + 2 , Z : plot X + 5 , Y + 2 , Z + 1

1280  return

1290  plot X , Y - 2 , Z : plot X - 1 , Y - 2 , Z : plot X - 2 , Y - 3 , Z

1291  plot X , Y - 2 , Z + 1 : plot X - 1 , Y - 2 , Z + 1 : plot X - 2 , Y - 3 , Z + 1

1292  plot X - 3 , Y - 4 , Z + 1 : plot X - 3 , Y - 4 , Z

1300  plot X + 1 , Y - 1 , Z : plot X + 1 , Y - 1 , Z + 1 : plot X + 1 , Y - 2 , Z - 1 : plot X + 1 , Y - 2 , Z + 2 : plot X + 1 , Y - 2 , Z - 2 : plot X + 1 , Y - 3 , Z + 3

1310  plot X + 2 , Y - 2 , Z : plot X + 3 , Y - 2 , Z : plot X + 4 , Y - 3 , Z

1311  plot X + 2 , Y - 2 , Z + 1 : plot X + 3 , Y - 2 , Z + 1 : plot X + 4 , Y - 3 , Z + 1

1312  plot X + 5 , Y - 4 , Z + 1 : plot X + 5 , Y - 4 , Z

1320  return

1350  plot X , Y , Z : plot X - 1 , Y , Z : plot X - 2 , Y + 1 , Z

1351  plot X , Y , Z - 1 : plot X - 1 , Y , Z - 1 : plot X - 2 , Y + 1 , Z - 1

1352  plot X - 3 , Y + 2 , Z : plot X - 3 , Y + 2 , Z - 1

1360  plot X + 1 , Y - 1 , Z : plot X + 1 , Y - 1 , Z - 1 : plot X + 1 , Y - 2 , Z + 1 : plot X + 1 , Y - 2 , Z - 2 : plot X + 1 , Y - 2 , Z + 2 : plot X + 1 , Y - 3 , Z - 3

1370  plot X + 2 , Y , Z : plot X + 3 , Y , Z : plot X + 4 , Y + 1 , Z

1371  plot X + 2 , Y , Z - 1 : plot X + 3 , Y , Z - 1 : plot X + 4 , Y + 1 , Z - 1

1372  plot X + 5 , Y + 2 , Z : plot X + 5 , Y + 2 , Z - 1

1380  return

1390  plot X , Y - 2 , Z : plot X - 1 , Y - 2 , Z : plot X - 2 , Y - 3 , Z

1391  plot X , Y - 2 , Z - 1 : plot X - 1 , Y - 2 , Z - 1 : plot X - 2 , Y - 3 , Z - 1

1392  plot X - 3 , Y - 4 , Z - 1 : plot X - 3 , Y - 4 , Z

1400  plot X + 1 , Y - 1 , Z : plot X + 1 , Y - 1 , Z - 1 : plot X + 1 , Y - 2 , Z + 1 : plot X + 1 , Y - 2 , Z - 2 : plot X + 1 , Y - 2 , Z + 2 : plot X + 1 , Y - 3 , Z - 3

1410  plot X + 2 , Y - 2 , Z : plot X + 3 , Y - 2 , Z : plot X + 4 , Y - 3 , Z

1411  plot X + 2 , Y - 2 , Z - 1 : plot X + 3 , Y - 2 , Z - 1 : plot X + 4 , Y - 3 , Z - 1

1412  plot X + 5 , Y - 4 , Z - 1 : plot X + 5 , Y - 4 , Z

1420  return

1450  plot X , Y , Z : plot X , Y , Z - 1 : plot X , Y + 1 , Z - 2

1451  plot X + 1 , Y , Z : plot X + 1 , Y , Z - 1 : plot X + 1 , Y + 1 , Z - 2

1452  plot X , Y + 2 , Z - 3 : plot X + 1 , Y + 2 , Z - 3

1460  plot X , Y - 1 , Z + 1 : plot X + 1 , Y - 1 , Z + 1 : plot X - 1 , Y - 2 , Z + 1 : plot X + 2 , Y - 2 , Z + 1 : plot X - 2 , Y - 2 , Z + 1 : plot X + 3 , Y - 3 , Z + 1

1470  plot X , Y , Z + 2 : plot X , Y , Z + 3 : plot X , Y + 1 , Z + 4

1471  plot X + 1 , Y , Z + 2 : plot X + 1 , Y , Z + 3 : plot X + 1 , Y + 1 , Z + 4

1472  plot X , Y + 2 , Z + 5 : plot X + 1 , Y + 2 , Z + 5

1480  return

1490  plot X , Y - 2 , Z : plot X , Y - 2 , Z - 1 : plot X , Y - 3 , Z - 2

1491  plot X + 1 , Y - 2 , Z : plot X + 1 , Y - 2 , Z - 1 : plot X + 1 , Y - 3 , Z - 2

1492  plot X , Y - 4 , Z - 3 : plot X + 1 , Y - 4 , Z - 3

1500  plot X , Y - 1 , Z + 1 : plot X + 1 , Y - 1 , Z + 1 : plot X - 1 , Y - 2 , Z + 1 : plot X + 2 , Y - 2 , Z + 1 : plot X - 2 , Y - 2 , Z + 1 : plot X + 3 , Y - 3 , Z + 1

1510  plot X , Y - 2 , Z + 2 : plot X , Y - 2 , Z + 3 : plot X , Y - 3 , Z + 4

1511  plot X + 1 , Y - 2 , Z + 2 : plot X + 1 , Y - 2 , Z + 3 : plot X + 1 , Y - 3 , Z + 4

1512  plot X , Y - 4 , Z + 5 : plot X + 1 , Y - 4 , Z + 5

1520  return

1550  plot X , Y , Z : plot X , Y , Z - 1 : plot X , Y + 1 , Z - 2

1551  plot X - 1 , Y , Z : plot X - 1 , Y , Z - 1 : plot X - 1 , Y + 1 , Z - 2

1552  plot X , Y + 2 , Z - 3 : plot X - 1 , Y + 2 , Z - 3

1560  plot X , Y - 1 , Z + 1 : plot X - 1 , Y - 1 , Z + 1 : plot X + 1 , Y - 2 , Z + 1 : plot X - 2 , Y - 2 , Z + 1 : plot X + 2 , Y - 2 , Z + 1 : plot X - 3 , Y - 3 , Z + 1

1570  plot X , Y , Z + 2 : plot X , Y , Z + 3 : plot X , Y + 1 , Z + 4

1571  plot X - 1 , Y , Z + 2 : plot X - 1 , Y , Z + 3 : plot X - 1 , Y + 1 , Z + 4

1572  plot X , Y + 2 , Z + 5 : plot X - 1 , Y + 2 , Z + 5

1580  return

1590  plot X , Y - 2 , Z : plot X , Y - 2 , Z - 1 : plot X , Y - 3 , Z - 2

1591  plot X - 1 , Y - 2 , Z : plot X - 1 , Y - 2 , Z - 1 : plot X - 1 , Y - 3 , Z - 2

1592  plot X , Y - 4 , Z - 3 : plot X - 1 , Y - 4 , Z - 3

1600  plot X , Y - 1 , Z + 1 : plot X - 1 , Y - 1 , Z + 1 : plot X + 1 , Y - 2 , Z + 1 : plot X - 2 , Y - 2 , Z + 1 : plot X + 2 , Y - 2 , Z + 1 : plot X - 3 , Y - 3 , Z + 1

1610  plot X , Y - 2 , Z + 2 : plot X , Y - 2 , Z + 3 : plot X , Y - 3 , Z + 4

1611  plot X - 1 , Y - 2 , Z + 2 : plot X - 1 , Y - 2 , Z + 3 : plot X - 1 , Y - 3 , Z + 4

1612  plot X , Y - 4 , Z + 5 : plot X - 1 , Y - 4 , Z + 5

1620  return

2000  rem Draw cage

2005  for L = 0 to 47

2010  COLOR= CAGECOL

2020  plot L , 0 , 0 : plot 0 , L , 0 : plot 0 , 0 , L

2030  plot L , 47 , 0 : plot 47 , L , 0 : plot 47 , 0 , L

2040  plot L , 0 , 47 : plot 0 , L , 47 : plot 0 , 47 , L

2050  plot L , 47 , 47 : plot 47 , L , 47 : plot 47 , 47 , L

2060  next L

2070  return

2100  rem Redraw cage

2110  COLOR= CAGECOL

2120  plot L , 0 , 0 : plot 0 , L , 0 : plot 0 , 0 , L

2130  plot L , 47 , 0 : plot 47 , L , 0 : plot 47 , 0 , L

2140  plot L , 0 , 47 : plot 0 , L , 47 : plot 0 , 47 , L

2150  plot L , 47 , 47 : plot 47 , L , 47 : plot 47 , 47 , L

2160  L = L + 1 : if L = 48 then L = 0 : D = 0

2170  return

You can find the dragon inside microM8 at /appleii/basic/3d/dragon.apl

Be the first to comment

Leave a Reply