Author Topic: FlxDigger (it digs your map!)[for flixel 2.55] [8 Aug 2013 EDIT]  (Read 5813 times)

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
hi guys, i'm here to share with you a method i use to generate random levels in my game MadAubergine.

one day, while i was searching for a cool method for generating random levels, i've found this tut http://noelberry.ca/2011/04/procedural-generation-the-caves/ and i've decided to follow it.

this is the result:

FlxDigger.as
Code: [Select]
package 
{
import org.flixel.*
/**
* ...
* @author Grey
*/
public class FlxDigger extends FlxSprite
{

public var Diggers:FlxGroup;
public var mat:Array;
public var cols:int = 0;
public var rows:int = 0;
public var cntdig:int = 0;
public var stuck:Number = 0;
public var oldx:int = 0;
public var oldy:int = 0;
private var szX:int;
private var szY:int;

public function FlxDigger(matrix:Array,Digga:FlxGroup,MapWidth:int,MapHeight:int,X:int,Y:int,sizeX:int=16,sizeY:int=16)
{
super();
makeGraphic(sizeX, sizeY, 0xff000000);
Diggers = Digga;
mat = matrix;
cols = MapWidth/sizeX;
rows = MapWidth/sizeY;
x = X;
y = Y;
szX = sizeX;
szY = sizeY;
}

override public function update():void
{
super.update();
if (stuck == 0)
{
oldx = x;
oldy = y;
}
cntdig = Registry.totdiggers - Registry.ddig;
stuck += FlxG.elapsed;
if (x-(szX*2) <= 0)
    kill();
if (x+(szX*2)>= cols * szX - szX)
    kill();
if (y-(szY*2) <= 0)
    kill();
if (y+(szY*2)>= rows * szY-szY)
    kill();
if (mat[y/szY][x/szX]==1)
mat[y / szY][x / szX] = 0;
chooseDir();
var f:Number = Math.random();
if (f > 0.91)
{
var d:FlxDigger = new FlxDigger(mat, Diggers, 640, 640, x, y, szX, szY);
Diggers.add(d);
Registry.totdiggers++;
}
if (Registry.totdiggers >= 340)
    kill();
if (stuck >= 1)
{
if (oldx == x || oldy == y)
{
Registry.ddig++;
    kill();
}
else
    stuck = 0;
}
}
private function chooseDir():void
{
var f:Number = 0;
//check if there are non-empty blocks
if (mat[y/szY][(x - szX)/szX] == 0)
{
if (mat[y/szY][(x + szX)/szX] == 0)
{
if (mat[(y - szY)/szY][x/szX] == 0)
{
if (mat[(y + szY) / szY][x / szX] == 0)
{
if(cntdig>1)
kill();
}
else
    y += szY;
}
else
{
if (mat[(y + szY)/szY][x/szX] == 0)
    y -= szY;
else
{
f = Math.floor(Math.random()*2)
if (f == 0)
   y -= szY;
else
   y += szY;
}
}
}
else
{
if (mat[(y - szY)/szY][x/szX] == 0)
{
if (mat[(y + szY)/szY][x/szX] == 0)
    x += szX;
else
{
f = Math.floor(Math.random()*2);
if (f == 0)
   x += szX;
else
   y += szY;
}
}
else
{
if (mat[(y + szY)/szY][x/szX] == 0)
{
f = Math.floor(Math.random()*2);
if (f == 0)
   x += szX;
else
   y -= szY;
}
else
{
f = Math.floor(Math.random() * 3);
if (f == 0)
    x += szX;
if (f == 1)
    y -= szY;
if (f == 2)
    y += szY;
}
}
}
}
else
{
if (mat[y / szY][(x + szX) / szX]==0)
{
if (mat[(y - szY) / szY][x / szX] == 0)
{
if (mat[(y + szY) / szY][x / szX] == 0)
   x -= szX;
else
{
f = Math.floor(Math.random() * 2);
if (f == 0)
    x -= szX;
    else
    y += szY;
}
}
else
{
if (mat[(y + szY) / szY][x / szX] == 0)
{
f = Math.floor(Math.random() * 2);
if (f == 0)
    x += szX;
    else
    y -= szY;
}
else
{
f = Math.floor(Math.random() * 3);
if (f == 0)
    x -= szX;
if (f == 1)
    y -= szY;
if (f == 2)
    y += szY;
}
}
}
else
{
if (mat[(y - szY) / szY][x / szX] == 0)
{
if (mat[(y + szY) / szY][x / szX] == 0)
{
f = Math.floor(Math.random() * 2);
if (f == 0)
    x -= szX;
    else
    x += szX;
}
else
{
f = Math.floor(Math.random() * 3);
if (f == 0)
    x -= szX;
if (f == 1)
    x += szX;
if (f == 2)
    y += szY;
}
}
else
{
f = Math.floor(Math.random() * 4);
if (f == 0)
    x += szX;
if (f == 1)
    y -= szY;
if (f == 2)
    x -= szX;
if (f == 3)
    y += szY;
}
}
}
}
override public function kill():void
{
if(!alive) return;
    velocity.x = 0;
    velocity.y = 0;
    alive = false;
    exists = false;
cntdig--;
}
}

}


how can you use it? do something like this:


DigState.as
Code: [Select]
package 
{
import org.flixel.*;
/**
* ...
* @author Grey
*/
public class DigState extends FlxState
{
[Embed(source = "media/wt.png")]public var wT:Class;
[Embed(source = "media/cursor.png")] private var ImgCursor:Class;

public var diggroup:FlxGroup;
public var splscrn:FlxSprite;
public var matrix:Array;
public var month:Number = 0;
public var mindiggrs:int = 300;

override public function create():void
{
super.create();
diggroup = new FlxGroup();
Registry.mapray = new Array();
Registry.totdiggers = 1;
Registry.ddig = 0;
Registry.mapray = genInitMatrix(640/8, 640/8);
splscrn = new FlxSprite(0, 0, wT);
var d:FlxDigger = new FlxDigger(Registry.mapray, diggroup, 640, 640, 5 * 16, 7 * 16, 8, 8);
diggroup.add(d);
add(diggroup);
add(splscrn);
FlxG.mouse.show(ImgCursor);
}

override public function update():void
{
super.update();
if (Registry.totdiggers >= mindiggrs)
{
    FlxG.fade(0xff000000, 1, go);
}
if (Registry.totdiggers < mindiggrs-50 && diggroup.countLiving() == 0)
    FlxG.switchState(new DigState);
}
private function go():void
{
FlxG.switchState(new PlayState);
}

public function genInitMatrix( rows:uint, cols:uint ,negative:Boolean=false):Array
{
// Build array of 1s
var mat:Array = new Array();
for ( var y:uint = 0; y < rows; ++y )
{
mat.push( new Array );
for ( var x:uint = 0; x < cols; ++x )
{
if (!negative)
    mat[y].push(1);
else
    mat[y].push(0);
}
}

return mat;
}
}
}
« Last Edit: Thu, Aug 8, 2013 by !8bit!man »

Jeff

  • Active Member
  • ***
  • Posts: 127
  • Karma: +0/-0
    • View Profile
    • Entertainment Evolution
Re: FlxDigger (it digs your map!)
« Reply #1 on: Mon, Nov 5, 2012 »
That's cool! And nice from you to share that.

I checked the noelberry's tut and it was fun to find out how simple the logic is. That's something I could have even personally come up with, which is not something that I could that easily say from some other game programming mechanisms. ;) And thinking about that random map creation I first started thinking about it in a far too complex way. It's usually the most simple solutions that work the best.
Please give me feedback for my latest game in progress:
Blog: Entertainment Evolution

Phillament

  • New Member
  • *
  • Posts: 1
  • Karma: +0/-0
    • View Profile
Re: FlxDigger (it digs your map!)
« Reply #2 on: Mon, Feb 4, 2013 »
This is amazing! Thanks for sharing. I'm trying to learn from this code, but I can't seem to get it running. For some reason a few things don't work. In FlxDigger I get an error message when calling "makeGraphic," "alive," and "Digger." Do you have any idea what the issue could be?

Here's the code:

Code: [Select]
package
{
import org.flixel.*;


public class MapGenTest extends FlxSprite
{

public var Diggers:FlxGroup;
public var mat:Array;
public var cols:int = 0;
public var rows:int = 0;
public var cntdig:int = 0;
public var stuck:Number = 0;
public var oldx:int = 0;
public var oldy:int = 0;
public var spawnRate:int;
public var tileWidth:int;
public var tileHeight:int;



public function MapGenTest(matrix:Array, Digga:FlxGroup, nCols:int, nRows:int, X:int, Y:int, sR:int=0.91, tW:int=16, tH:int=16)
{
super();
makeGraphic(16, 16, 0xff000000);
Diggers = Digga;
mat = matrix;
cols = nCols;
rows = nRows;
spawnRate = sR;
tileWidth = tW;
tileHeight = tH;
x = X;
y = Y;

}

override public function update():void
{
super.update();
cntdig = Registry.totdiggers - Registry.ddig;

if(x-(tileWidth*2) <= 0){
kill();
}
if(x+(tileWidth*2) >=cols * tileWidth - tileWidth){
kill();
}
if(y-(tileHeight*2) <= 0){
kill();
}
if(y+(tileHeight*2) >= rows * tileHeight - tileHeight){
kill();
}

if(mat[y/tileHeight][x/tileWidth]==1){
mat[y/tileHeight][x/tileWidth] = 0;
}

chooseDir();

var f:Number = Math.random();

if(f > spawnRate){
var d:Digger = new Digger(mat, Diggers, cols, rows, x, y);
Diggers.add(d);
Registry.totdiggers++;
}

if(Registry.totdiggers >=340){
kill();
}

stuck += FlxG.elapsed;

if(stuck == 0){
oldx = x;
oldy = y;
}

if(stuck >= 1){
if(oldx == x || oldy == y){
kill();
}
else{
stuck = 0;
}
}

}

private function chooseDir():void
{
var f:Number = 0;

if(mat[y/16][(x - 16)/16] == 0)
{
if(mat[y/tileHeight][(x + tileWidth)/tileWidth] == 0)
{
if(mat[(y + tileHeight) / tileHeight][x/tileWidth] == 0)
{
if(cntdig > 1)
{
kill();
}
else
{
y += tileHeight;
}
}
else
{
if(mat[(y + tileHeight)/tileHeight][x/tileWidth] == 0)
{
y -= tileHeight;
}
else
{
f = Math.floor(Math.random()*2)
if (f == 0)
{
y -= tileHeight;

}
else
{
y += tileHeight;
}
}
}
}
else
{
if (mat[(y - tileHeight)/tileHeight][x/tileWidth] == 0)
{
if(mat[(y + tileHeight)/tileHeight][x/tileWidth] == 0)
{
x += tileWidth;
}
else
{
f = Math.floor(Math.random()*2);
if(f == 0)
{
x += tileWidth;
}
else
{
y += tileHeight;
}
}
}
else
{
if(mat[(y + tileHeight)/tileHeight][x/tileWidth] == 0)
{
f = Math.floor(Math.random()*2);
if(f == 0)
{
x += tileWidth;
}
else
{
f = Math.floor(Math.random()*3);
if(f == 0)
{
x += tileWidth;
}
if(f == 1)
{
y -= tileHeight;
}
if(f == 2)
{
y += tileHeight;
}
}
}
}
}
}
else
{
if (mat[y/tileHeight][(x + tileWidth) / tileWidth] == 0)
{
if(mat[(y - tileHeight) / tileHeight][x / tileWidth] == 0)
{
if(mat[(y + tileHeight) / tileHeight][x / tileWidth] == 0)
{
x -= tileWidth;
}
else
{
f = Math.floor(Math.random()*2);
if(f == 0)
{
x -= tileWidth;
}
else
{
y += tileHeight;
}
}
}
else
{
if(mat[(y + tileHeight) / tileHeight][x / tileWidth] == 0)
{
f = Math.floor(Math.random() * 2);
if(f == 0)
{
x += tileWidth;
}
else
{
y -= tileHeight;
}
}
else
{
f = Math.floor(Math.random()*3);
if(f == 0)
{
x -= tileWidth;
}
if(f == 1)
{
y -= tileHeight;
}
if(f == 2)
{
y += tileHeight;
}
}
}
}
else
{
if(mat[(y - tileHeight) / tileHeight][x/tileWidth] == 0)
{
if(mat[(y + tileHeight) / tileHeight][x/tileWidth] == 0)
{
f=Math.floor(Math.random()*2);
if (f == 0)
{
x -= tileWidth;
}
else
{
x += tileWidth;
}
}
else
{
f = Math.floor(Math.random() *3);
if(f == 0)
{
x -= tileWidth;
}
if(f == 1)
{
x +=tileWidth;
}
if(f == 2)
{
y += tileHeight;
}
}
}
else
{
f = Math.floor(Math.random()* 4);
if(f == 0)
{
x += tileWidth;
}
if(f == 1)
{
y -= tileHeight;
}
if(f == 2)
{
x -= tileWidth;
}
if(f == 3)
{
y += tileHeight;
}
}
}
}
}
override public function kill():void
{
if(!alive) return;
velocity.x = 0;
velocity.y = 0;
alive = false;
exists = false;
Registry.ddig++;
}
}
}

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
Re: FlxDigger (it digs your map!)
« Reply #3 on: Sun, Feb 10, 2013 »
Do you mean you've got errors in the class i've posted or in your Playstate class?

Gama11

  • Contributor
  • ****
  • Posts: 390
  • Karma: +0/-0
    • View Profile
Re: FlxDigger (it digs your map!)[for flixel 2.55]
« Reply #4 on: Mon, Feb 11, 2013 »
My guess is that Phillament is using an outdated version of flixel, as in versions before 2.5, "makeGraphic" was named "createGraphic", which is likely where the error is from.

camasthecat

  • Contributor
  • ****
  • Posts: 461
  • Karma: +0/-0
  • WOW! 400+ Posts!? Jeez! Do I have a life!?!?
    • View Profile
    • My site for thermalJS, an HTML5/Javascript engine kinda-like flixel:
Re: FlxDigger (it digs your map!)[for flixel 2.55]
« Reply #5 on: Thu, Mar 14, 2013 »
Could You add an example game/pictures?
Sounds nice though...

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
Re: FlxDigger (it digs your map!)[for flixel 2.55]
« Reply #6 on: Wed, Mar 27, 2013 »
Could You add an example game/pictures?
Sounds nice though...

why not? it will take some time though.... this week i'm really busy

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
Re: FlxDigger (it digs your map!)[for flixel 2.55]
« Reply #7 on: Thu, Aug 8, 2013 »
well, goodmorning bros!!


after an extreme inactivity, i'm back and i also brought a demo project for you!


take a look:

<a href="http://greyproductions.altervista.org/DiggerDemo.swf" target="_blank" class="new_win">http://greyproductions.altervista.org/DiggerDemo.swf</a>          (arrow keys to move around)

also you can download the project here (only for flashdevelop)

NOTE: i modified some part of the code. Now diggers are easier to adapt to different tile size.

hope this will help you guys.
« Last Edit: Thu, Aug 8, 2013 by !8bit!man »

Gama11

  • Contributor
  • ****
  • Posts: 390
  • Karma: +0/-0
    • View Profile
Re: FlxDigger (it digs your map!)[for flixel 2.55]
« Reply #8 on: Thu, Aug 8, 2013 »
That demo isn't showing up for me, just as a big white square. Wondering if others have the same issue, anyway, here's the direct link to it.

The demo is a bit annoying because of the fading it uses everywhere, making you wait for a few seconds each time for no reason. Also kind of annoying that the map isn't regenerated instantly when pressing R. The jumping soundeffect hurts your eyes more than anything else.

Other than that, nice little demo. Seems like FlxDigger is extremely similar to FlxCaveGenerator. A demo for that one can be found here.

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
That demo isn't showing up for me, just as a big white square. Wondering if others have the same issue, anyway, here's the direct link to it.

The demo is a bit annoying because of the fading it uses everywhere, making you wait for a few seconds each time for no reason. Also kind of annoying that the map isn't regenerated instantly when pressing R. The jumping soundeffect hurts your eyes more than anything else.

Other than that, nice little demo. Seems like FlxDigger is extremely similar to FlxCaveGenerator. A demo for that one can be found here.

Well, we must say that a demo isn't made to let people have fun but to demonstrate what the code generates.
Also, nothing happens instantly: when you press R the demo redo all the procedure used to make a random map.


And i have to say that FlxDigger and FlxCaveGen. may have the same result (not every time you run them though), but they use different procedures: FlxCaveGen. infact firstly generates a 'noisy' matrix that must be processed afterward to obtain a playable level (that's why it uses what we call Cellular Automata). FlxDigger, instead, digs out on a full matrix a lot of tunnels.

The difference between the two methods, then, is that with FlxCaveGenerator you obtain large and almost empty rooms that aren't always connected each other while FlxDigger generates a map that is more suitable for platform games, as there are a lot of platforms and every single corner of the map is easy to explore.
« Last Edit: Thu, Aug 8, 2013 by !8bit!man »

Gama11

  • Contributor
  • ****
  • Posts: 390
  • Karma: +0/-0
    • View Profile
Quote
The difference between the two methods, then, is that with FlxCaveGenerator you obtain large and almost empty rooms that aren't always connected each other while FlxDigger generates a map that is more suitable for platform games, as there are a lot of platforms and every single corner of the map is easy to explore.

You're right about that! The results seem to be better. I might look into porting FlxDigger to Haxe / HaxeFlixel eventually actually. :)

!8bit!man

  • Active Member
  • ***
  • Posts: 210
  • Karma: +0/-0
  • Hack Hack Hack!!
    • View Profile
You're right about that! The results seem to be better. I might look into porting FlxDigger to Haxe / HaxeFlixel eventually actually. :)

Don't forget to say that Grey ported it to flixel then ;)
« Last Edit: Thu, Aug 8, 2013 by !8bit!man »