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" hidden>
var Randdir = [];
for (var h = 0; h < 500; h++) {
var tem=[1,2,3,4];
Randdir[h] =tem;
Randdir[h]=Shuffle(Randdir[h]);
}
function Generate(location){
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 = ("img" + k + "a" + l);
img.onclick = (function () {
var currentK = k;
var currentL = l;
return function () {
change(currentK, currentL);
}
})();
para.appendChild(img);
}
document.getElementById(location).appendChild(para);
}
}
function FloodGenerate(xcor, ycor, dep, curdep) {
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 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 update() {
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 );
}
}
}
function Reset(){
if (map.length > 0) {
for (var i = 0; i < x; i++) {
for (var j = 0 ; j < y ; j++) {
var img = document.getElementById("img" + i + "a" + j);
img.parentNode.removeChild(img);
}
var para = document.getElementById("para" + i);
para.parentNode.removeChild(para);
}
map = [];
}
}
</code>
<run id="5a970440ea0ca" id="5a96fe2eaedb4>
var map = [];
x=40;
y=60;
Reset();
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.
<run id="5a97103fd45d7">
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; //Imperfection
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; }
}
}}
var map = [];
x=40;
y=60;
Reset();
Generate("MapWindow2");
Imp(30,36);
FloodGenerate(1, 1, 10, 0);
update();
</run>
<p id="MapWindow2"></p>