KI
Home Nach oben

 

Nach oben
Dungeons
Detailmap
Einführung
Multitexture Terrain
Plantbox
Aussenlevel (Block)
Tag & Nachtwechsel
KI
Waving Grass
Framerate

KI für NPC

Die NPC in Darkmountain verfügen über eine einfache KI die Ihre Umgebung erfasst und einfache Aktionen durchführen kann.

Fähigkeiten der KI:

  • Umgebung nach Gegnern scannen
  • Annäherung an den Gegner bis auf Kampfreichweite
  • Gegner attackieren
  • Vordefinierte Aktionen durchführen (Folge Pfad und Gehe zu Punkt X,Y)

Die weiteren Aktionen der NPC wie z.B. Pfadverfolgung werden über einen Aktionsstack gesteuert der der Reihe nach abgearbeitet wird.

Der Aktionsstack kann hier die Befehle Folge dem Pfad "X" oder gehe zu Punkt "X,Y" enthalten. Der Befehl kann auch an eine Uhrzeit gekoppelt werden um z.B. den Jäger um 7:00 Uhr morgens auf die Jagd zu schicken.

Der aktuelle Befehl des Aktionsstacks wird durch die Wahrnehmungsroutinen der KI bei Bedarf unterbrochen. Folgt der NPC z.B. gerade einem Pfad auf dem sich ein Gegner befindet wird er die Pfadverfolgung unterbrechen und den Gegner attackieren.

Nach erfolgreichem Kampf wird die vorherige Aktion wieder aufgenommen.


Guter NPC - Böser NPC

Die Logik der KI ist hier noch sehr einfach aber wirkungsvoll. Über ein Flag wird festgelegt ob der NPC gutartig oder bösartig reagiert. Gute NPC greifen böse NPC automatisch an und umgekehrt.

Bild: NPC werden mit der KI aufeinander losgelassen

Definition Gut - Böse:

my.flag2 = ON/OFF;     (gut/böse)

In oben dargestellter Szene scannen die NPC Ihre Umwelt. Sobald Sie einen feindlichen NPC finden nähern Sie sich und bekämpfen Ihn. Ist der Gegner gefallen wird der nächste gesucht und bekämpft. Sind nur noch NPC mit der gleichen Flag2 Einstellung vorhanden wird der Kampfmodus beendet und der NPC geht wieder seiner vorherigen Tätigkeit nach oder verbleibt im Idle - Modus.

 

Finde den Gegner

Umgebung absuchen

Zuerst benötigen wir eine Routine die den Scan der Umgebung anstößt. Hierzu wird die Funktion scan_entity verwendet.

Die Scanfunktion kann eine Kugel oder ein Segment einer Kugel um den NPC durchsuchen. Bei meiner KI wird der gesamte Umkreis abgesucht. Es hilft also derzeit nichts sich im Rücken des NPC anzuschleichen.

temp.pan=360;            // Volle 360° werden horizontal abgesucht
temp.tilt=360;               // Volle 360° werden vertikal abgesucht

temp.z=800;                 // Hierdurch wird der Radius in Quad angegeben der um den NPC abgesucht 
                                      // werden soll

Damit der NPC auf die Scanergebnisse reagieren kann muss das Flag ENABLE_DETECT bei dem NPC gesetzt werden. Es können nur Entities gefunden werden bei denen das Flag ENABLE_SCAN gesetzt wurde.

Da die NPC meiner KI aufeinander reagieren sollen müssen beide Flags gesetzt werden.

my.ENABLE_DETECT=ON;
my.ENABLE_SCAN=ON; 

Die Funktion scan_entity liefert als Ergebnis nur die Distanz bis zum ersten gefundenen Entity oder eine 0 wenn kein Entity gefunden wurde. Auf die gefundenen Objekte muss an anderer Stelle der KI reagiert werden.

Routine npc_human_search_enemy zur Suche nach anderen NPC oder dem Spieler

//
// ===============================================================================
// SEARCH_ENEMY
// ===============================================================================
// Suche den nächsten Gegner (NPC oder Spieler)

define NPC_SCAN_RANGE 800;
define NPC_SCAN_REPLAY 5;

function npc_human_search_enemy() {
    var temp;
    var acttime;
    var ret;

    temp.pan = 360;
    temp.tilt = 360;
    temp.z = NPC_SCAN_RANGE;  

    acttime=total_secs; 


    // Alle 5 Sekunden nach neuem Ziel scannen
    if((acttime-my.skill95)>NPC_SCAN_REPLAY) {
        my.skill95=acttime; 
        ret=scan_entity(my.x, temp);
    }
    return(ret);
}

Ergebnisse des Scans verarbeiten

Die gefundenen Entities des Scans werden in der Eventfunktion abgearbeitet. Diese muss innerhalb der Action dem Entity zugewiesen werden.

ACTION NPC_HUMAN {

    MY.EVENT = npc_human_event; 
    ...
}

In der Eventfunktion kann jetzt auf den Scan reagiert werden. Der Scan wird für jede gefundene Entity automatisch die Eventfunktion einmal anstoßen. Das gefundene Entity kann dabei über den you Pointer angesprochen werden.

Das schwierige bei der Verarbeitung ist dass die Scanfunktion nicht sequentiell arbeitet. Das Script läuft nach dem Befehl scan_entity weiter während die Ergebnisse parallel an die Eventfunktion weitergegeben werden.

Soll mit dem gefundenen Entity weitergearbeitet werden muss der Handle unbedingt gemerkt werden da das Ergebnis des Scans über den you Pointer zurückgeliefert wird. Dieser ändert sich aber beim nächsten Scan oder bei einer Trace Anweisung.

Da ein Entity keinem Skillwert direkt zugewiesen werden kann muss der Handle ausgelesen und in der Skillvariable gespeichert werden.

my.skill90=handle(you);

Es ist nicht ratsam sich eine Variable vom Typ Entity im Script zu definieren und das Ergebnis hier zu speichern. Diese Variable ist Global und wird dann von allen NPC gemeinsam verwendet. Dies führt dann zu einem völlig undefinierbarem Verhalten.

Es ist grundsätzlich wichtig alle Ergebnisse die gemerkt werden sollen nur in SKILL oder FLAG Parametern des Entity abzulegen.

Falscher Code:

entity* gegner;
gegner=you;

Dieses Script funktioniert für einen NPC hervorragend. Sobald das Script aber mehreren NPC zugewiesen wird gibt es völlig undefinierte Zustände.


Routine npc_human_event - Die Eventfunktion des NPC

//
// ===============================================================================
// Eventfunktion
// ===============================================================================
// Click, Touch, Detect
function npc_human_event() {

    if(EVENT_TYPE == EVENT_CLICK) {
        ...
    }

    if(EVENT_TYPE == EVENT_TOUCH) {
        ...
    }

    if(EVENT_TYPE == EVENT_RELEASE) {
        ...
    }

    //
    // Dieses Event wird ausgelöst wenn mit scan_entity ein Entity gefunden wurde
    //


    if(EVENT_TYPE == EVENT_DETECT) {
        // Monster greifen nicht Monster an
        if(my.flag2==ON) {                                    // NPC ist ein Monster
            if(you.flag2==OFF) {                             // Greife nur entgegengesetzte NPC an 
                if(you.skill2>0) {                                 // Hat der NPC noch Lebenspunkte ?
                    if(my.skill90==0) {                          // Ist bereits schon ein NPC gefunden =
                        my.skill90=handle(you);            // Merke den Gegner
                    }
                }
            }
        }
    
        // Nicht Monster greifen Monster an
        else {
            if(you.flag2==ON) {
                if(you.skill2>0) {
                    if(my.skill90==0) {
                        my.skill90=handle(you);
                    }
                }
            }
        }
    }
}

(to be continued...)