Intel sponsors gamedev.net search:   
Tesseract's Game Development JournalBy Tesseract      

How I Work
Preferred Language
Actionscript 3
Development Environment
Notepad++ for coding
Flex 3 SDK for compiling
Graphics
The GIMP, among others

See my personal weblog

Completed (and published) Flash Game Projects:
Games
TriGavoid
Tutorials
Creating Textures with Perlin Noise
Simple Trigonometry and Curves Tutorial



Wednesday, August 27, 2008
mazes and spaceships

(click the image to play).

I suppose it was inevitable, but I combined the bitmap navigator functionality from the earlier experiments with the maze generator from a few days ago, and now I have an infinite variety of mazes which can be flown over. I haven't implemented wall/collision detection yet, but I should have it completed fairly soon. It won't be difficult; a simple color sniffer will give me pixel-level control over what is hitting what.

I also figured out how to add seeds to the maze generator, so now I can have the same mazes for everyone who plays, or each game session will now be able to be saved, without loading the game and discovering yourself in a new maze.

The code for the maze generator class is here:

/*
 * For the benefit of novice programmers, I've listed
 * the various bitmasks used to the best of my ability
 * (I don't use Java, I'm assuming it uses standard coordinates where 0,0 is the top left)
 * I've commented on the drawing code as well
 *
 *  1: Wall above
 *  2: Wall below
 *  4: Wall left
 *  8: Wall right
 * 16: queued to be added
 * 32: "in" the maze
 *
 *  - quin/10-24-06
 *	-	translated to Actionscript 3 by John Winkelman, 2008.08.25
 */
package {
	import flash.display.BitmapData;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	
	public class MazeConstructor {
		private var mazeBitmapData:BitmapData;
		private var cellsX:Number;
		private var cellsY:Number;
		private var cellWidth:Number;
		private var cellHeight:Number;
		private var isRandom:Boolean = false;
		private var seed:Number;
		private var wallThickness:Number;
		private var hasExits:Boolean;

		private var maze:Array;		
		private var mx:Number = 0;
		private var my:Number = 0;
		private var nextCell:Number = 0;
		private var d:Number = 0;
		private var dx:Array = [ 0, 0, -1, 1 ];
		private var dy:Array = [ -1, 1, 0, 0 ];
		private var todo:Array = [];
		private var todonum:Number = 0;
		
		public function MazeConstructor() {}
		public function initMaze($mazeBitmapData:BitmapData,$cellsX:Number,$cellsY:Number,$cellWidth:Number,$cellHeight:Number,$wallThickness:Number=1,$seed:Number=0,$hasExits:Boolean=false):void {
			mazeBitmapData = $mazeBitmapData;
			cellsX = $cellsX;
			cellsY = $cellsY;
			cellWidth = $cellWidth;
			cellHeight = $cellHeight;
			wallThickness = $wallThickness;
			seed = $seed;
			hasExits = $hasExits;
			if(seed==0) isRandom = true;
			createMaze();
		}
		private function createMaze():void {
			mx = 0;
			my = 0;
			nextCell = 0;
			d = 0;
			dx = [ 0, 0, -1, 1 ];
			dy = [ -1, 1, 0, 0 ];
			todo = [];
			todonum = 0;

			/* We want to create a maze on a grid. */
			maze = [];
		
			/* We start with a grid full of walls. */
			for(mx = 0; mx < cellsX; mx++) {
				maze[mx] = [];
				for(my = 0; my < cellsY; my++) {
					if(mx == 0 || mx == cellsX-1 || my == 0 || my == cellsY-1) {
						maze[mx][my] = 32;
					} else {
						maze[mx][my] = 63;
					}
				}
			}
			/* Select any square of the grid, to start with. */

			/*	RANDOM CHECK	*/
			if(isRandom==true) {	//	start at a random cell
				mx =  (Math.floor(Math.random() * (cellsX-2)))+1;
				my =  (Math.floor(Math.random() * (cellsY-2)))+1;
			} else {	//	start at the center-most cell
				mx = Math.floor(cellsX/2);
				my = Math.floor(cellsY/2);
			}
			/*	RANDOM CHECK	*/

			/* Mark this square as connected to the maze. */
			maze[mx][my] &= ~48;

			/* Remember the surrounding squares, as we will */
			for(d = 0; d < 4; d++) {
				if((maze[mx + dx[d]][my + dy[d]] & 16) != 0) {
					todo[todonum++] = ((mx + dx[d]) << 16) | (my + dy[d]);
					maze[mx + dx[d]][my + dy[d]] &= ~16;
				}
			}
			
			// We won't be finished until all is connected. 
			while(todonum > 0) {// We select one of the squares next to the completed parts of the maze. 
				

				/*	RANDOM CHECK	*/
				if(isRandom==true) {
					nextCell = (Math.floor(Math.random() * todonum));
				} else {
					seed++;
					nextCell = seed % todonum;
				}
				/*	RANDOM CHECK	*/

				mx = todo[nextCell] >> 16; // the top 2 bytes of the data 
				my = todo[nextCell] & 65535; // the bottom 2 bytes of the data 
				// We will connect it, so remove it from the queue. 
				todo[nextCell] = todo[--todonum];
				// Select a direction, which leads to the maze. 
				do{
					/*	RANDOM CHECK	*/
					if(isRandom==true) {
						d = Math.floor(Math.random()*4);
					} else {
						seed++;
						d = seed%4;
					}
					/*	RANDOM CHECK	*/
				} while((maze[mx + dx[d]][my + dy[d]] & 32) != 0);

				// Connect this square to the maze. 
				maze[mx][my] &= ~((1 << d) | 32);
				maze[mx + dx[d]][my + dy[d]] &= ~(1 << (d ^ 1));
				
				// Remember the surrounding squares, which aren't 
				for(d = 0; d < 4; d++) {
					if((maze[mx + dx[d]][my + dy[d]] & 16) != 0) {
						// connected to the maze, and aren't yet queued to be. 
						todo[todonum++] = ((mx + dx[d]) << 16) | (my + dy[d]);
						maze[mx + dx[d]][my + dy[d]] &= ~16;
					}
				}
				// Repeat until finished.
			}

			/* One may want to add an entrance and exit. */
			if(hasExits==true) {
				maze[1][1] &= ~1; 
				maze[cellsX-2][cellsY-2] &= ~2;
			}
			
			renderMaze();
		}

		private function renderMaze():void {
			var i:int;
			var j:int;

			for(i = 1; i < cellsX-1; i++) {
				for(j = 1; j < cellsY-1; j++) {
					if((maze[i][j] & 1) != 0) {/* This cell has a top wall */
						mazeBitmapData.fillRect (new Rectangle(i * cellWidth, j * cellHeight, cellWidth+wallThickness, wallThickness),0xff000000);
					}
					if((maze[i][j] & 2) != 0) {/* This cell has a bottom wall */
						mazeBitmapData.fillRect(new Rectangle(i * cellWidth, j * cellHeight + cellHeight, cellWidth+wallThickness, wallThickness),0xff000000);
					}
					if((maze[i][j] & 4) != 0) {/* This cell has a left wall */
						mazeBitmapData.fillRect(new Rectangle(i * cellWidth, j * cellHeight, wallThickness, cellHeight+wallThickness),0xff000000);
					}
					if((maze[i][j] & 8) != 0) {/* This cell has a right wall */
						mazeBitmapData.fillRect(new Rectangle(i * cellWidth+cellWidth, j * cellHeight, wallThickness, cellHeight+wallThickness),0xff000000);
					}
				}
			}
		}
	}
}


Looking at this, it occurred to me that it wouldn't be too difficult to modify this so that it can draw hex mazes. Really, it is just two more walls, and modify the positioning of the cells. The bitwise/boolean operators will work just as well, I think. But this is a project for when the weather is ugly, and the girlfriend is out of town.

Also: I discovered a port of Telengard (Windows) which has had me in a big ol' nostalgia kick for the past several days. It holds up surprisingly well for a game I first played on my Commodore 64, back in 1984.

Comments: 0 - Leave a Comment

Link



Friday, August 22, 2008


(click the screen-shot play)

In my research into procedurally/randomly/runtime generating assets for Flash, I discovered the vast world of maze generator algorithms. And lo and behold, the Wikipedia had some example code, written in Java. It took me about three hours to translate it into Actionscript 3; a good chunk of that time was spent trying to figure out whether the Java random() call was equivalent to Math.ceil(Math.random()), Math.round(Math.random()), or Math.floor(Math.random()). Now things are running smoothly. Since I have this licked, pretty much, there are a couple of things I can do with the code:

1. make the maze generation seeded instead of random, so I can get repeatable mazes. Already took a stab at this and crashed my browser several times. Once this is solved, Telengard, here I come!

2. add collision detection so I can make any number of variations on maze games, from Berzerk to the most aggravatingly complex of dungeon crawls.

Oh: And here is the new AS3 class.

/*
 * For the benefit of novice programmers, I've listed
 * the various bitmasks used to the best of my ability
 * (I don't use Java, I'm assuming it uses standard coordinates where 0,0 is the top left)
 * I've commented on the drawing code as well
 *
 *  1: Wall above
 *  2: Wall below
 *  4: Wall left
 *  8: Wall right
 * 16: queued to be added
 * 32: "in" the maze
 *
 *  - quin/10-24-06
 */
package {
	import flash.display.*;
	import flash.geom.*;
	import flash.events.*;
	[SWF(backgroundColor="0xffffff",width="400",height="400",frameRate="10")]
	
	public class Maze extends Sprite {
		private var maze:Array;
		private var mazeBitmapData:BitmapData;
		private var mazeBitmap:Bitmap;
		private var cellsX:Number = 50
		private var cellsY:Number = 50;
		private var cellWidth:Number = 8;
		private var cellHeight:Number = 8;
		private var debugPane:TextField;
		public function Maze() {
			addEventListener(Event.ENTER_FRAME,init);
			stage.addEventListener(MouseEvent.CLICK,init);
		}
		private function init(e:Event):void {
			if(!stage) return;
			removeEventListener(Event.ENTER_FRAME,init);
			var mx:Number = 0;
			var my:Number = 0;
			var n:Number = 0;
			var d:Number = 0;
			var dx:Array = [ 0, 0, -1, 1 ];
			var dy:Array = [ -1, 1, 0, 0 ];
			var todo:Array = [];
			var todonum:Number = 0;

			/* We want to create a maze on a grid. */
			maze = [];

			/* We start with a grid full of walls. */
			for(mx = 0; mx < cellsX; mx++) {
				maze[mx] = [];
				for(my = 0; my < cellsY; my++) {
					if(mx == 0 || mx == cellsX-1 || my == 0 || my == cellsY-1) {
						maze[mx][my] = 32;
					} else {
						maze[mx][my] = 63;
					}
				}
			}
			/* Select any square of the grid, to start with. */
			mx =  (Math.floor(Math.random() * (cellsX-2)))+1;
			my =  (Math.floor(Math.random() * (cellsY-2)))+1;
	  
			/* Mark this square as connected to the maze. */
			maze[mx][my] &= ~48;

			/* Remember the surrounding squares, as we will */
			for(d = 0; d < 4; d++) {
				if((maze[mx + dx[d]][my + dy[d]] & 16) != 0) {
					todo[todonum++] = ((mx + dx[d]) << 16) | (my + dy[d]);
					maze[mx + dx[d]][my + dy[d]] &= ~16;
				}
			}
			
			// We won't be finished until all is connected. 
			while(todonum > 0) {
				// We select one of the squares next to the maze. 
				n = (Math.floor(Math.random() * todonum));
				mx = todo[n] >> 16; // the top 2 bytes of the data 
				my = todo[n] & 65535; // the bottom 2 bytes of the data 
			// We will connect it, so remove it from the queue. 
				todo[n] = todo[--todonum];
				// Select a direction, which leads to the maze. 
				do{
					d = Math.floor(Math.random() * 4);
				} while((maze[mx + dx[d]][my + dy[d]] & 32) != 0);

				// Connect this square to the maze. 
				maze[mx][my] &= ~((1 << d) | 32);
				maze[mx + dx[d]][my + dy[d]] &= ~(1 << (d ^ 1));
				// Remember the surrounding squares, which aren't 
				for(d = 0; d < 4; d++) {
					if((maze[mx + dx[d]][my + dy[d]] & 16) != 0) {
						// connected to the maze, and aren't yet queued to be. 
						todo[todonum++] = ((mx + dx[d]) << 16) | (my + dy[d]);
						maze[mx + dx[d]][my + dy[d]] &= ~16;
					}
				}
			// Repeat until finished.
			}

			/* One may want to add an entrance and exit. */
			//maze[1][1] &= ~1; 
			//maze[cellsX-2][cellsY-2] &= ~2;
			
			mazeBitmapData = new BitmapData(400,400);			
			paint(mazeBitmapData);
		}

		public function paint(g:BitmapData):void {
			var mx:int;
			var my:int;

			for(mx = 1; mx < cellsX-1; mx++) {
				for(my = 1; my < cellsY-1; my++) {
					if((maze[mx][my] & 1) != 0) {/* This cell has a top wall */
						g.fillRect (new Rectangle(mx * cellWidth, my * cellHeight, cellWidth+1, 1),0xff000000);
					}
					if((maze[mx][my] & 2) != 0) {/* This cell has a bottom wall */
						g.fillRect(new Rectangle(mx * cellWidth, my * cellHeight + cellHeight, cellWidth+1, 1),0xff000000);
					}
					if((maze[mx][my] & 4) != 0) {/* This cell has a left wall */
						g.fillRect(new Rectangle(mx * cellWidth, my * cellHeight, 1, cellHeight+1),0xff000000);
					}
					if((maze[mx][my] & 8) != 0) {/* This cell has a right wall */
						g.fillRect(new Rectangle(mx * cellWidth+cellWidth, my * cellHeight, 1, cellHeight+1),0xff000000);
					}
				}
			}
			mazeBitmap = new Bitmap(mazeBitmapData);
			addChild(mazeBitmap);
		}
	}
}


Comments: 0 - Leave a Comment

Link



Tuesday, August 19, 2008


(click the image to play)

Encouraging improvements! I debugged a couple of things, removed a lot of extraneous code, and added the beginnings of keeping score.

I think I will go for an underwater theme for this game - more of a vortex than a space warp feel. I see much work learning how to make pixel graphics in my near future.



Comments: 2 - Leave a Comment

Link



Tuesday, August 12, 2008
Gyruss clone in Adobe Flash, with procedurally generated graphics

My work on the tile games led me to do some experimenting, and now I have the background for my Gyruss Clone. Click the image to see it in action

I was playing around with the Perlin Noise generation functionality in Flash, and co-incidentally came across a polar distortion class at BIT-101. I took it, played with it a bit, and figured out how to go from Polar Distortion to Twirl, with the addition of one very short line of code. Voila! I had the thing you see above. The addition of a simple gradient provides the depth.

Comments: 0 - Leave a Comment

Link


All times are ET (US)

 
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
13
14
15
16
17
18
20
21
23
24
25
26
28
29
30
31

OPTIONS
Track this Journal

 RSS 

ARCHIVES
November, 2009
March, 2009
January, 2009
November, 2008
October, 2008
September, 2008
August, 2008
July, 2008
February, 2008
December, 2007
November, 2007
October, 2007
September, 2007
July, 2007
June, 2007
March, 2007
February, 2007
January, 2007
December, 2006
August, 2006
July, 2006
June, 2006