Anybody remember ‘DungeonMaster’ or ‘Eye of the Beholder’? 1988 or so? Probably before some of you were born. Of course, PONG was a wonder when I was a kid.
Everybody yawn audibly, now. One of the first things I wrote in Flash, looks like in March.
The one zip contains a Flash 8 project, the other contains the compiled result. If it comes up blank, mash on the arrow keys and/or QWE/ASD a bit to bring something near enough to see. Looking at the code, it looks like I hacked in 0/1 for changing map contents, just to be clever.
Anyway, one nice thing about this method is, it will run on ANY hardware and keep up a marvelously high frame rate. High enough to animate other Flash stuff. On the down-side, you’re locked into 90 degree turning and block-by-block movement…
Ultra-super-simple stuff. Just draw the blocks and whatever else in the right order, and up it pops, just like a cheezy 3D dungeon crawl of yore.
I wrote it as a demo. It doesn’t do anything the way it ought to, but it does illustrate how it’s done clearly enough. Really, it should have one set of blocks that are dynamically grown/shrunk to fit, but I was too lazy to do anything but sit like a vegetable and drag things around in the Flash UI the afternoon I hacked it up.
I had a version of this running on the Atari ST using Line-A calls, once upon a time. It allowed you to look up/down and rotate… all on 90 degree increments and bock boundaries.
// Do a little '3D' block dungeon crawl demo
// Up/Down -> Forward, Backward
// Left/Right -> Turn
// Make a map of chars.
//
// This doesn't work like it should.
// There should be a map, with details and collections of junk
// Instead of static blocks hidden/shown, this should maintain a set of MovieClip things, dynamically created, destroyed and replaced.
// Needs selection of art for things that are rotated versus always pointed the same way.
// 1-way (shrubs, columns), 2-way (some doors), 4-way (characters, switches on walls, hooks, decorations, signs, etc.)
// Things that do rotate need to rotate appropriately when player rotates
//
//
// Define how we 'see' the world
//
var viewLutBlock:Array = new Array
( // Block art in 'sequential' order
_level0.BL02,_level0.BL01,_level0.BC0,_level0.BR01,_level0.BR02,
_level0.BL12,_level0.BL11,_level0.BC1,_level0.BR11,_level0.BR12,
_level0.BL21,_level0.BC2,_level0.BR21,
_level0.BL31, _level0.BR31
);
var viewLutN:Array = new Array
( // Blocks facing North
-2,-3, -1,-3, 0,-3, 1,-3, 2,-3,
-2,-2, -1,-2, 0,-2, 1,-2, 2,-2,
-1,-1, 0,-1, 1,-1,
-1,0, 1,0
);
var viewLutE:Array = new Array
( // Blocks facing East
3,-2, 3,-1, 3,0, 3,1, 3,2,
2,-2, 2,-1, 2,0, 2,1, 2,2,
1,-1, 1,0, 1,1,
0,-1, 0,1
);
var viewLutS:Array = new Array
( // Blocks facing South
2,3, 1,3, 0,3, -1,3, -2,3,
2,2, 1,2, 0,2, -1,2, -2,2,
1,1, 0,1, -1,1,
1,0, -1,0
);
var viewLutW:Array = new Array
( // Blocks facing West
-3,2, -3,1, -3,0, -3,-1, -3,-2,
-2,2, -2,1, -2,0, -2,-1, -2,-2,
-1,1, -1,0, -1,-1,
0,1, 0,-1
);
var viewLut:Array = new Array
( // Block addressing, per facing direction, clockwise
viewLutN,
viewLutE,
viewLutS,
viewLutW
);
var moveLut:Array = new Array
(
0,-1,
1,0,
0,1,
-1,0
);
var worldSize:Number = 32; // Width and height of world
var world:Array = new Array(worldSize*worldSize); // Allocate an array to hold it.
//
// Where we stand in the world
//
var wpX:Number = 10;
var wpY:Number = 10;
var wpFace:Number = 0;
//
// Build a little world randomly
//
function initWorld()
{
var index:Number;
var numCels:Number = worldSize*worldSize;
while( numCels-- )
{
if( random(3) < 1 )
{
world[numCels] = 1;
}
else
{
world[numCels] = 0;
}
}
// Make sure the player isn't buried
worldSet( wpX-1, wpY-1, 0 );
worldSet( wpX, wpY-1, 0 );
worldSet( wpX+1, wpY-1, 0 );
worldSet( wpX-1, wpY, 0 );
worldSet( wpX+1, wpY, 0 );
worldSet( wpX, wpY, 0 );
worldSet( wpX-1, wpY+1, 0 );
worldSet( wpX, wpY+1, 0 );
worldSet( wpX+1, wpY+1, 0 );
}
// Set a map value in the world, return previous value
function worldSet( cx:Number, cy:Number, value:Number ) : Number
{
// Out of bounds is 'solid wall', and won't be changed.
if( cx < 0 || cx >= worldSize || cy < 0 || cy >= worldSize )
return 1;
var index:Number = (cy * worldSize) + cx;
var ret:Number = world[index];
world[index] = value;
return ret;
}
// Get a map value from the world
function worldGet( cx:Number, cy:Number ) : Number
{
// Out of bounds is 'solid wall'.
if( cx < 0 || cx >= worldSize || cy < 0 || cy >= worldSize )
return 1;
var index:Number = (cy * worldSize) + cx;
var ret:Number = world[index];
return ret;
}
initWorld();
Key.addListener(this); // Listen for keydown events
// Each frame, look at world array around current location and hide/show blocks accordingly
// Eventually needs doors, decorations, characters,
function onEnterFrame()
{
// Check for keyboard input, change world position accordingly
var cBlocks:Number = viewLutBlock.length;
var ilut:Number = 0;
var iblock:Number = 0;
while( iblock < cBlocks )
{
var vLut:Array = viewLut[wpFace];
var xc:Number = wpX + vLut[ilut++];
var yc:Number = wpY + vLut[ilut++];
var bVisible = worldGet( xc, yc ) == 1;
viewLutBlock[iblock++]._visible = bVisible;
}
// The one we're standing on is always invisible (delete it?)
//_level0.BR31._visible = false;
_level0.StatusLine.text = "Rotation: " + wpFace + " Location: " + wpX + "," + wpY;
}
// Be all interactive 'n stuff.
function onKeyDown()
{
var code:Number = Key.getCode();
if( code == Key.RIGHT || code == 69 ) //'E'
{ // Turn right
if( ++wpFace > 3 )
wpFace = 0;
}
else if( code == Key.LEFT || code == 81 ) // 'Q'
{ // Turn left
if( --wpFace < 0 )
wpFace = 3;
}
else if( code == Key.UP || code == 87 ) // 'W'
{ // Go forward
var iDir:Number = wpFace*2;
wpX += moveLut[iDir];
wpY += moveLut[iDir+1];
if( worldGet( wpX, wpY ) != 0 )
{
wpX -= moveLut[iDir];
wpY -= moveLut[iDir+1];
}
}
else if( code == Key.DOWN || code == 83 ) // 'S'
{ // Go backward
var iDir:Number = wpFace*2;
wpX -= moveLut[iDir];
wpY -= moveLut[iDir+1];
if( worldGet( wpX, wpY ) != 0 )
{
wpX += moveLut[iDir];
wpY += moveLut[iDir+1];
}
}
else if( code == Key.DELETEKEY || code == 65 ) // 'A'
{ // Go left
var iDir:Number = ((wpFace+1)&3)*2;
wpX -= moveLut[iDir];
wpY -= moveLut[iDir+1];
if( worldGet( wpX, wpY ) != 0 )
{
wpX += moveLut[iDir];
wpY += moveLut[iDir+1];
}
}
else if( code == Key.PGDN || code == 68 ) // 'D'
{ // Go right
var iDir:Number = ((wpFace-1)&3)*2;
wpX -= moveLut[iDir];
wpY -= moveLut[iDir+1];
if( worldGet( wpX, wpY ) != 0 )
{
wpX += moveLut[iDir];
wpY += moveLut[iDir+1];
}
}
else if( code >= 48 && code <= 57 ) // '0'~'9'
{ // Change map
var iDir:Number = wpFace*2;
worldSet( wpX + moveLut[iDir], wpY + moveLut[iDir+1], code-48 );
}
}