Entwickler-Ecke
Multimedia / Grafik - Brauche Hilfe für KI
Sequa - Mi 08.10.03 16:54
Titel: Brauche Hilfe für KI
Hi,
ich bin im moment dabei eine Art TowerDefense wie das für Warcraft3 zu programmieren. Sinn dieses Spiels ist es mit seinen Türmen, die mann bauen kann, zu verhindern, dass die Monster vom Startpunkt aus das Ziel erreichen. Um diese effizienter zu vernichten baut man die Türme in bestimmten Systemen, zum Beispiel Reihensystem damit die Monster möglichst oft wieder bei den Türmen vorbeikommen.
Achso: Die Monster werden von den Türmen abgeschossen, desswegen ist es auch so sinnvoll öfters an den Türmen vorbeizukommen. Sollte der Weg zum Ziel komplett mit Türmen zugebaut sein, so werden diese vom Monster zerstört.
Nun, bisher habe ich nur ein Notfallsystem entwickelt. Die Monster weichen den Türmen auf sympelste Weise aus, nämlcih wenn sie auf einen Turm stoßen versuchen sie erst zu einen Richtung zu laufen, wenn diese nicht geht zur anderen und so weiter.
So ist es unheimlich leicht das Monster auszutricksen und es in eine Endlosschleife zu verwickeln. Es läuft also nurnoch hin und her.
Was ich brauche ist eine Möglichkeit den kürzesten Weg direkt beim "Spawn" des monsters zu berechnen. Sollte der Weg druch ein neues Hindernis, einen Turm, abgeschnitten werden so wird erneut von der Position der kürzeste Weg bis zum Ziel berechnet.
Bisher bin ich soweit, dass ich eine Karte in DelphiX darstelle (22x50Felder) und habe als Startpunkt das Feld (1;1) festgelegt und das Ziel ist es die unterste Reihe (Y=50) zu erreichen. Eine Wegberechnung gibt es nicht und so versucht das Monster ersteinmal immer nur nach unten zu laufen und den Türmen auszuweichen. Dabei nimmt es oft sehr lange und umständliche Wege.
Ich habe bereits einige Tutorials zur KI gelesen, wurde aber aus den C++ Beispielen nicht recht schlau.
Könnt ihr mir bei eienr solchen Berechnungsprozedur helfen?
Hier ist meine bisherige "AI", die nur den Türmen ausweicht und immer in Endlosschleifen rennt wenn sie in Sackgassen kommt...:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110:
| procedure TGameForm.CalculateAI(startx,starty:integer); var AIposx, AIposy:integer; AIfinish:boolean; AIusedsteps:integer; AImaxsteps:integer; AIcanceled:boolean; AImovedown, AImoveup, AImoveright, AImoveleft:boolean; AImoved:boolean; AIdirection : string; begin
AIposx := startx; AIposy := starty; AIfinish := False; AIcanceled := False; AIusedsteps := 0; AImaxsteps := 999;
AImovedown := True; AImoveup := False; AImoveright := False; AImoveleft := False; AIdirection := 'right'; AImoved := False; Memo1.Lines.Add('Calculating AI from '+inttostr(startx)+'/'+inttostr(starty)+'.');
while AIfinish = False do begin
AImoved := False;
AIusedsteps := AIusedsteps+1;
Map[AIposx,AIposy].Draw := True;
if (Map[AIposx, AIposy+1].Blocked = False) and (AIposy<mapheight+1) then begin if AIdirection <> 'up' then begin AIposy := AIposy+1; AImoved := True; end else AImovedown := False; end else AImovedown := False;
if AImovedown = False then begin
if (AImoved = False) and (AIdirection = 'right') then if (Map[AIposx+1, AIposy].Blocked = False) and (AIposx<mapwidth+1) then begin AIposx := AIposx+1; AImoved := True; end else begin AImoveright := False; AIdirection := 'left'; end;
if (AImoved = False) and (AIdirection = 'left') then if (Map[AIposx-1, AIposy].Blocked = False) and (AIposx>0) then begin AIposx := AIposx-1; AImoved := True; end else begin AImoveleft := False; AIdirection := 'up'; end;
if (AImoved = False) and (AIdirection = 'up') then if (Map[AIposx, AIposy-1].Blocked = False) then begin AIposy := AIposy-1; AImoved := True; if (Map[AIposx-1, AIposy].Blocked = False) and (AIposx>0) then AIdirection := 'left'; if (Map[AIposx+1, AIposy].Blocked = False) and (AIposx<mapwidth+1) then AIdirection := 'right'; end else AImoveup := False;
end;
if AIposy >= mapheight then AIfinish := True;
if AIusedsteps >= AImaxsteps then begin AIfinish := True; AIcanceled := True; end;
end; if AIcanceled = False then Memo1.Lines.Add('Finished AI in '+inttostr(AIusedsteps)+' steps.') else Memo1.Lines.Add('Canceled AI after '+inttostr(AIusedsteps)+' steps.'); end; |
scientificus - Do 09.10.03 17:35
ich kann dir sagen, wie ich es bei einem programm gemacht habe. das war zwar einiges an quelltext, aber es hat funktioniert.
zunächst würde ich einen record deklarieren, der für jedes feld der karte den kürzesten weg vom startpunkt dorthin speichern soll:
weg : array[1..i,1..j] of record
anzahl : integer;
feldx,feldy : array[1..anzahl];
end; //oder so ähnlich
"anzahl" ist die Anzahl der Felder, die bei diesem Weg zurückgelegt werden, "feldx" und "feldy" speichern die Koordinaten der Felder, die benutzt wurden. z.B. weg[4,5].feldx[3] ist die x-koordinate des 3. Feldes, das auf dem Weg (von 1,1) nach 4,5 benutzt wird.
dieser record sollte zuerst komplett mit 0 belegt werden.
So, nun dazu, wie das record "weg" berechnet werden kann.
Man klappert in einer for-Schleife alle Felder hintereinander ab und überprüft bei jedem Feld ("aktuellesfeld",z.B. 1,1) folgendes:
Wenn die weg["umfeld"].anzahl eines benachbarten Feldes ("umfeld") kleiner ist als die eigene (oder wenn weg["aktuellesfeld"].anzahl=0) , überträgt man den wert weg["umfeld"].anzahl + 1 auf weg["aktuellesfeld"].anzahl sowie die koordinaten:
for i := 1 to weg["umfeld"].anzahl do
weg["aktuellesfeld"].feldx[i] := weg["umfeld"].feldx[i];
dasselbe mit "feldy".
jetzt speichert man in weg["aktuellesfeld"].feldx[weg["aktuellesfeld"].anzahl] die x-koordinate von "umfeld", mit "feldy" dasselbe.
Wenn man diesen Vorgang (die for-Schleife) oft genug wiederholt, liegt im record die Information des kürzesten Wegs zu jedem beliebigen Feld der Karte.
Um die Rechenzeit zu verkürzen, kann man davor noch einen array deklarieren, der alle Felder der Karte in einer sinnvollen Weise abspeichert, also spiralenförmig vom Startpunkt nach außen gehend. Das war bei meinem programm schwer zu realisieren, da ich hexagonale Felder benutzt habe, doch mit quadratischen Feldern müsste das einfacher gehen.
Am Ende sollte in diesem array z.B. stehen (mit 1,1 als Startfeld):
felderx[2,2,1,3,3,3,2,1,4,4,4,4,3,2,1,5,5,5,5,5,4,3,2,1,6,6,6,6,6,6...]
feldery[1,2,2,1,2,3,3,3,1,2,3,4,4,4,4,1,2,3,4,5,5,5,5,5,1,2,3,4,5,6...]
Wenn in der oben beschriebenen for-Schleife diese Felder abgeklappert werden, sollte das ganze schneller gehen.
Je häufiger du die for-Schleife laufen lässt, desto genauer wird die Ermittlung des kürzesten Weges. Falls also der kürzest mögliche Weg aus 60 Feldern besteht und erkannt werden soll, muss die for-Schleife 60 mal laufen gelassen werden. Im Normalfall würde ich sagen, dass 10 mal ausreicht.
Ich hoffe, es ist verständlicher als es sich anhört... :roll:
Bin für Rückfragen offen.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!