| Wednesday, August 27, 2008 |
 An Iteration, A Combination |
Posted - 8/27/2008 10:14:02 PM | 
(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:
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;
maze = [];
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;
}
}
}
if(isRandom==true) {
mx = (Math.floor(Math.random() * (cellsX-2)))+1;
my = (Math.floor(Math.random() * (cellsY-2)))+1;
} else {
mx = Math.floor(cellsX/2);
my = Math.floor(cellsY/2);
}
maze[mx][my] &= ~48;
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;
}
}
while(todonum > 0) {
if(isRandom==true) {
nextCell = (Math.floor(Math.random() * todonum));
} else {
seed++;
nextCell = seed % todonum;
}
mx = todo[nextCell] >> 16;
my = todo[nextCell] & 65535;
todo[nextCell] = todo[--todonum];
do{
if(isRandom==true) {
d = Math.floor(Math.random()*4);
} else {
seed++;
d = seed%4;
}
} while((maze[mx + dx[d]][my + dy[d]] & 32) != 0);
maze[mx][my] &= ~((1 << d) | 32);
maze[mx + dx[d]][my + dy[d]] &= ~(1 << (d ^ 1));
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;
}
}
}
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) {
mazeBitmapData.fillRect (new Rectangle(i * cellWidth, j * cellHeight, cellWidth+wallThickness, wallThickness),0xff000000);
}
if((maze[i][j] & 2) != 0) {
mazeBitmapData.fillRect(new Rectangle(i * cellWidth, j * cellHeight + cellHeight, cellWidth+wallThickness, wallThickness),0xff000000);
}
if((maze[i][j] & 4) != 0) {
mazeBitmapData.fillRect(new Rectangle(i * cellWidth, j * cellHeight, wallThickness, cellHeight+wallThickness),0xff000000);
}
if((maze[i][j] & 8) != 0) {
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.
| |
 Passages, All Alike |
Posted - 8/22/2008 10:17:13 AM | 
(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.
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;
maze = [];
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;
}
}
}
mx = (Math.floor(Math.random() * (cellsX-2)))+1;
my = (Math.floor(Math.random() * (cellsY-2)))+1;
maze[mx][my] &= ~48;
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;
}
}
while(todonum > 0) {
n = (Math.floor(Math.random() * todonum));
mx = todo[n] >> 16;
my = todo[n] & 65535;
todo[n] = todo[--todonum];
do{
d = Math.floor(Math.random() * 4);
} while((maze[mx + dx[d]][my + dy[d]] & 32) != 0);
maze[mx][my] &= ~((1 << d) | 32);
maze[mx + dx[d]][my + dy[d]] &= ~(1 << (d ^ 1));
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;
}
}
}
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) {
g.fillRect (new Rectangle(mx * cellWidth, my * cellHeight, cellWidth+1, 1),0xff000000);
}
if((maze[mx][my] & 2) != 0) {
g.fillRect(new Rectangle(mx * cellWidth, my * cellHeight + cellHeight, cellWidth+1, 1),0xff000000);
}
if((maze[mx][my] & 4) != 0) {
g.fillRect(new Rectangle(mx * cellWidth, my * cellHeight, 1, cellHeight+1),0xff000000);
}
if((maze[mx][my] & 8) != 0) {
g.fillRect(new Rectangle(mx * cellWidth+cellWidth, my * cellHeight, 1, cellHeight+1),0xff000000);
}
}
}
mazeBitmap = new Bitmap(mazeBitmapData);
addChild(mazeBitmap);
}
}
}
| |
 The Work Proceedeth Apace |
Posted - 8/19/2008 9:09:35 AM | 
(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.
| |
 From one project to another |
Posted - 8/12/2008 10:15:27 PM | 
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.
| |
|
| 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
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
|