IKG2018/Einführung/ Philipp
Aus ProgrammingWiki
Wiki-Quelltext dieser Seite.
<language>Javascript</language>
<h1>Prozeduale Content Generierung in Vidospielen</h1><br>
<h2>Der Dungeon Generator</h2><br>
Zuffalsgenerierter Spielinhalt spielt in den Videospielen von Heute eine größere Rolle als je zuvor. Von einzelnen Gegenständen zu ganzen Welten können viele Dinge so kostensparender erstellt werden.
In vielen Spielen existieren Strukturen wie Höhlen, Verließe, Festungen und Türme deren innenleben leicht Zufallsgeneriert werden können, man fasst diese heutzutage unter dem Englischen Begriff für Verließ zusammen: Dungeon.<br>
<h2>Erstellung eines Dungeon Generators</h2>
Es gibt mehrere Methoden einen Dungeon Generator zu schreiben, welche man nimmt kommt auf das Game Design an. So kann man zum Beispiel Handgemachte Räume in zufälliger Anordnung aneinanderreihen da es dem Designer Bestimmte Vorteile gibt Kämpfe und Schätze vorsichtiger zu Verteilen. Man kann jedoch auch alles von Grund auf zufallsgenerieren zu lassen.<br>
Ein erster Schritt sollte sein ein Perfektes Labyrinth zu generieren. Dazu gibt es einfach Methoden. Das hier ist Flood Fill mit Zuffalszahlen.
<code id="5a970b4522086">
var Randdir = []; //Erstellung von Zufallszahlen für spätere Verwendung
for (var h = 0; h < 500; h++) {
var tem=[1,2,3,4];
Randdir[h] =tem;
Randdir[h]=Shuffle(Randdir[h]);
}
function Reset(window){
if (map.length > 0) {
for (var i = 0; i < x; i++) {
for (var j = 0 ; j < y ; j++) {
var img = document.getElementById(window + i + "a" + j);
img.parentNode.removeChild(img);
}
var para = document.getElementById("para" + i);
para.parentNode.removeChild(para);
}
map = [];
}
}
function Shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function Generate(window){ //Erstellung des Spielfeldes
for (var i = 0; i <x; i++) {
var arr = [];
for (var j = 0 ; j < y; j++) {
arr[j] = 1;
}
map[i] = arr;
}
for (var k = 0; k < map.length; k++) {
var para = document.createElement("DIV");
para.id=("para"+k);
for (var l = 0 ; l < map[0].length ; l++) {
var img = document.createElement("IMG");
img.setAttribute("src", "http://programmingwiki.de/images/1/19/Philipp_Malchin_Tile_12.png");
img.setAttribute("height", 24);
img.setAttribute("width", 24);
img.id = (window + k + "a" + l);
para.appendChild(img);
}
document.getElementById(window).appendChild(para);
}
}
function FloodGenerate(xcor, ycor, dep, curdep) { //Rekursive Generierung der Gänge
map[xcor][ycor]= 2;
for (var i = 0; i < 4; i++) {
if (Randdir[curdep][i] == 1) {
if (xcor + 2 < map.length-1) {
if (map[xcor + 2][ ycor] != 2) {
map[xcor + 1][ ycor]= 2;
FloodGenerate(xcor + 2, ycor, dep, curdep + 1);
}}}
if (Randdir[curdep][i] == 2){
if (ycor + 2 < map[0].length-1){
if (map[xcor][ ycor + 2]!= 2){
map[xcor][ ycor + 1] = 2;
FloodGenerate(xcor, ycor + 2, dep, curdep + 1);
}}}
if (Randdir[curdep][i] == 3){
if (xcor - 2 > 0){
if (map[xcor - 2][ ycor] != 2){
map[xcor - 1][ ycor]= 2;
FloodGenerate(xcor - 2, ycor, dep, curdep + 1);
}}}
if (Randdir[curdep][i] == 4){
if (ycor - 2 > 0){
if (map[xcor][ ycor - 2] != 2){
map[xcor][ ycor - 1] = 2;
FloodGenerate(xcor, ycor - 2, dep, curdep + 1);
}}}
}
return;
}
function update() { //Anwendung der Änderungen auf das Spielfeld
for (var xd = 0; xd < x; xd++) {
for (var yd = 0 ; yd <y ; yd++) {
if (map[xd][yd]==2){
path="http://programmingwiki.de/images/7/7e/Philipp_Malchin_Tile_2.png";
}
else{
path="http://programmingwiki.de/images/1/19/Philipp_Malchin_Tile_12.png";
}
var img = document.getElementById("img" + xd + "a" + yd);
img.setAttribute("src", path );
}
}
}
</code>
<run id="5a9713f173c46">
var map = []; //Festlegen von Startparametern
x=40;
y=40;
Reset("MapWindow1")
Generate("MapWindow1");
FloodGenerate(1, 1, 10, 0);
update();
</run>
<p id="MapWindow1"></p>
Da Ein Perfektes Labyrinth jedoch zumeist langweilig ist fügt man Imperfektionen ein, das heißt das Gänge miteinander verbunden werden die es dem Spieler erlauben im kreis zu gehen. Dies wirkt natürlicher. Ein bestimmter Prozentsatz an Wänden mit zwei angrenzenden gegenüberliegenden Gängen wird entfernt. Dieses Labyrinth ist auserdem viel zu voll, ein typischer Dungeon besteht nicht nur aus Gängen sondern vorallendingen aus Räumen. Ein bestimmter Prozentsatz der Sackgassen, also Gängen mit drei angrenzenden Wänden wird Stück für Stück wieder geschlossen.
<code id="5a97103fd45d7">
function Imp(percor,imp){
function Imp(percor,imp){
var cw=0; //Zählen der Gänge
for (var xl = 0; xl < x; xl++)
{
for (var yl = 0; yl < y; yl++)
{
if (map[xl][ yl] == 2) {
cw++;
}
}
}
var ch = cw; //Ausdünnung
cw = Math.round(cw - (cw / 100) * percor);
while (ch > cw) {
var gxy = [];
for (var xl = 0; xl < x; xl++)
{
for (var yl = 0; yl < y; yl++)
{
if (map[xl][yl] == 2)
{
var m=0;
if (map[xl + 1][ yl]!= 2) { m++; }
if (map[xl - 1][ yl]!= 2) { m++; }
if (map[xl][yl-1]!= 2) { m++; }
if (map[xl][yl+1]!= 2) { m++; }
if (m >= 3) { gxy.push([xl,yl]); }
}
}
}
Shuffle(gxy);
var gc = gxy.length;
for (var i = 0; i < gc; i++) {
map[gxy[i][0]][gxy[i][1]] = ObjectsBackup[gxy[i][0]][gxy[i][1]];
ch--;
if (ch < cw) { break; }
}
}
cw = ch; //Imperfektion
cw = Math.round(cw + (cw / 100) * (percor/imp));
while (ch < cw)
{
var gxy = [];
for (var xl = 1; xl < x-1; xl++)
{
for (var yl = 1; yl < y-1; yl++)
{
if (map[xl][yl] != 2)
{
var m = 0;
if (map[xl + 1] [yl]!= 2) { m++; }
if (map[xl - 1] [yl]!= 2) { m++; }
if (map[xl] [yl - 1]!= 2) { m++; }
if (map[xl] [yl + 1]!= 2) { m++; }
if (m == 2) { gxy.push([xl,yl]) }
}
}
}
Shuffle(gxy);
var gc = gxy.length;
for (var i = 0; i < gc; i++)
{
map[gxy[i][0]][gxy[i][1]] = 2;
ch++;
if (ch > cw) { break; }
}
}}
</code>
<run id="5a9713f173cc6">
var map = [];
x=40;
y=40;
Reset("MapWindow2");
Generate("MapWindow2");
Imp(30,36);
FloodGenerate(1, 1, 10, 0);
update();
</run>
<p id="MapWindow2"></p>
Damit unterschiedlichen Spielumgebungen eine unterschiedliche Ästhetik gegeben werden kann, kann man diese Parameter verändern.