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:
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