Dungeon-Generator mit KI Ansatz

Ich bin z.Z. damit beschäftigt ein Flashgame mit AS3 zu programmieren, welches klassische (zufallsgenerierte) Fantasy Dungeons haben soll - Stichwort 'roguelike'


Die Sache ist nun - einfach nur Räume find ich ziemlich langweilig + das sich roguelike dungeons i.d.R. jeglichem Realismus in ihrer Anordnung entziehen ... ich versuche mich daher an einem Ansatz welcher auf crawlern basiert, die den dungeon "graben" und auch neue crawler generieren etc., somit der dungeon hübsch "wächst"

geplant sind in einem ersten (optionalen) durchlauf natürliche felstunnel crawler welche "organische" formen erzeugen. also halt stumpf zufallsversetzte kreise hinterlassen.

und danach halt richtige tunnelgräber die rechteckige wege schlagen und somit den dungeon form geben - und auch raumspawner generieren, welche räume an tunnelgrenzen generieren.


Das ganze läuft generationsbasiert ab - jede generation verichtet zuerst ihr werk simultan zu ende bevor die nächste in gang gerät und so weiter


da ich aber nunmal nur ein 08/15 hobbycoder bin und mein uni-fach nicht weiter von informatik hätte entfernt sein können - hab ich halt sichtlich probleme bei vermutlich eher leichten algorithmen.

Ich fänd's daher klasse wenn mir hier jemand bei der fehlersuche behilflich sein könnt und vllt. paar tipps geben könnt.

hier sind paar bilder vom letzten build:
dungeon3.PNG

dungeon1.PNG

dungeon2.PNG

dungeon4.PNG

ihr könnt den hier in aktion anschauen: Link

ich habe seitdem den code größtenteils neugeschrieben (weil er häufig sehr verbuggte Dungeons generiert hat und auch recht unelegant war) und hab jetzt mit einem lächerlichen bug zu kämpfen den ich einfach nicht gebacken kriege:

Link zur SWF (im Browser ausführbar)
(mit Space startet ihr's neu)

ich habe halt im moment alles bis auf einen einzelnen tunneler deaktiviert (er spawnt auch keine weiteren generationen etc.)

es hapert halt an der Erkennung der Levelbegrenzungen - wenn er gegen eine rennt soll er nunmal die richtung wechseln (um +90° oder -90°) und weitergraben

wie ihr aber sehen könnt wechselt der tunneler stattdessen munter die richtung ohne voranzukommen - was ich einfach absolut nicht gefixt kriege

der algorithmus läuft wie folgt:

ein tunneler hat eine schrittgröße (gleich seitenlänge) die er pro schritt zurücklegt. dann wird gecheckt ob er diesen ausschnitt graben soll. falls ja, gräbt er ihn, falls nein wird er wieder zurückbewegt und wechselt die richtung.

nein wird zurückgegeben wenn er entweder in einen raum graben will oder eben das level verlässt. (außerdem soll es eine chance geben das er nach dem graben in einen anderen tunnel stirbt)

in AS3 mit Nutzung der Flashpunk Bibliothek schaut das so aus:

(ich weiß ihr habt wohlmöglich nie was von Flashpunk gehört und AS3 ist auch nicht die allerhäufigste programmiersprache - aber die syntax ist sehr verständlich und wenn ihr zu irgendeiner klasse ne frage habt beantworte ich das natürlich gern)

es wäre jedenfalls großartig wenn jemand mir beim fixen dieses bugs helfen könnt. es ist wohlmöglich was sehr stupides und simples aber ich komm einfach nicht drauf

(btw. arbeitet der Code mit einer Level variable welche als 2-dimensionales array gesehen werden kann und für jede position (schachfeld-artig) einen tileindex speichert - halt was sich an dieser position befindet. einen anderer teil von meinem code kümmert sich daran darum diese tiles grafisch darzustellen.

0 = schwarz/nichts
1 = tunnel
3 = raum
andere zahlen sind halt noch wände usw. die aber im moment unwichtig sind)


die move() function
bewegt den tunneler vorwärts und checkt ob er sich danach außerhalb des levels oder über einem raum befindet, falls ja wird er zurückgesetzt und die richtung gewechselt. falls nein wird gecheckt ob er einen bereits existenten tunnel angräbt (in welchem fall er eine chance hat nach dem graben zu sterben) und gegraben
Code:
		private function move():void {
			
			if (tunneler_direction == "right") x += tunneler_step * generator_world.TILE_SIZE;
			if (tunneler_direction == "down") y += tunneler_step * generator_world.TILE_SIZE;
			if (tunneler_direction == "left") x -= tunneler_step * generator_world.TILE_SIZE;
			if (tunneler_direction == "up") y -= tunneler_step * generator_world.TILE_SIZE;
			
			if (!checkBoundaries(tile_x, tile_y, tunneler_step, tunneler_step, tunneler_direction) && !checkRect(tile_x, tile_y, tunneler_step, tunneler_step, 0, 0, 3)) { 
				
				if (checkRect(tile_x, tile_y, tunneler_step, tunneler_step, 0, 0, 1) && chance_die > FP.rand(100)) {
					
					generator_world.Level.setRect(tile_x, tile_y, tunneler_step, tunneler_step, 1);
					counter_direction++;
					die();
					
				} else {
					
					generator_world.Level.setRect(tile_x, tile_y, tunneler_step, tunneler_step, 1);
					counter_direction++;
					
				}
				
			} else {
				
				if (tunneler_direction == "right") x -= tunneler_step * generator_world.TILE_SIZE;
				if (tunneler_direction == "down") y -= tunneler_step * generator_world.TILE_SIZE;
				if (tunneler_direction == "left") x += tunneler_step * generator_world.TILE_SIZE;
				if (tunneler_direction == "up") y += tunneler_step * generator_world.TILE_SIZE;
				
				changeDirection();
				
			}
			
		}

die changeDirection() function:
ändert die richtung des tunnelers um 90° und setzt einen direction counter zurück (welcher genutzt wird um die minimale zurückgelegte distanz vor einem freiwilligen zufälligen richtungswechsel zu ermitteln)
Code:
		private function changeDirection():void {
			
			counter_direction = 0;
			
			if (tunneler_direction == "up" || tunneler_direction == "down") {
				
				if (FP.rand(2) == 0) {
					
					tunneler_direction = "left";
					
				} else {
					
					tunneler_direction = "right";
					
				}
				
			} else {
				
				if (FP.rand(2) == 0) {
					
					tunneler_direction = "up";
					
				} else {
					
					tunneler_direction = "down";
					
				}
				
			}
			
		}

die checkBoundaries() function:
checkt ob sich ein rechteck innerhalb der levelgrenzen befindet
(der area_offset_direction parameter ist ein überbleibsel eines bugfix versuchs)

Code:
		private function checkBoundaries(area_x:uint, area_y:uint, area_width:uint, area_heigth:uint, area_offest_direction:String):Boolean {
			
			if (area_x <= 0 + 1 || area_y <= 0 + 1 || area_x + area_width >= generator_world.LEVEL_WIDTH - 1 || area_y + area_heigth >= generator_world.LEVEL_HEIGHT - 1) {
				
				return(true);
				
			} else {
				
				return(false);
				
			}
			
		}

die checkRect() function:
scannt ein rechteck auf das vorhandensein eines bestimmten tiles
1 = tunnel
3 = raum

Code:
		private function checkRect(area_x:uint, area_y:uint, area_width:uint, area_heigth:uint, x_offset:uint, y_offset:uint, check_tile:uint):Boolean {
			
			var check:Boolean = false;
			
			for (var i:uint = area_x - x_offset; i < area_x + area_width + x_offset && !check; i++) {
				for (var j:uint = area_y - y_offset; j < area_y + area_heigth + y_offset && !check; j++) {
					
					if (generator_world.Level.getTile(i, j) == check_tile) check = true;
					
				}
			}
			
			return(check);
			
		}

die anderen funktionen, welche sich z.B. um die erstellung anderer tunneler etc. kümmern sind im moment irrelevant
 
Hallo :)

Für dich interessant ist vielleicht der Source von Minicraft vom Persson. Er hat da ja auch eine Art Level-Generator gebaut.

Hier gibts den Source zum Downloaden;
Ludum Dare » Ludum Dare 22

Ich hoffe das ist zumindest nicht komplett fehl am Platz, hab mir den Code selber noch nicht angesehen. Das Ding ist zwar in Java, aber vielleicht kann man ja die eine oder andere Idee da rausholen?
 
Hey Orniflyer,
hast du das Problem eigentlich in den Griff bekommen? Es scheint ja an der CheckBoundaries() Funktion zu liegen. Das Programm geht immer in den Else-Fall deiner move() Funktion. Hast du mal die "verundeten" Bedingungen einzeln prüfen und dir ein paar Debug-Ausgaben geben lassen?

P.S.: Der Thread ist zwar ein halbes Jahr alt, aber weil ich mir schon 30 Minuten den Code angeguckt habe bevor ich das bemerkte, interessiert mich zumindest, ob es mittlerweile funktioniert;)
 
Zurück
Oben