Benutzer-Werkzeuge

Webseiten-Werkzeuge


scripting:tutorials:level3:armies

Dies ist eine alte Version des Dokuments!


Armeesteuerung

In vorigen Kapiteln wie im Artikel zur Erstellung einfacher Computergegner oder im Artikel zu Banditenlagern und Spawnern haben wir große Teile der Armeesteuerung an Comfortfunktionen übergeben und lediglich Parameter definiert. In diesem Kapitel werden dagegen elementare Kontrollfunktionen für Armeen vorgestellt, wie sie funktionieren und wo ihre Grenzen sind. Für das Verständnis dieses Artikels ist es von Vorteil, mit Banditenlagern und Spawnern vertraut zu sein und damit bereits experimentiert zu haben.

Armeen sind Truppenverbände, die immer im Gesamten gesteuert werden (es werden also nie die einzelnen Truppen angesprochen). Jede Armee hat eine Position (auch Anker genannt) und einen Radius, in dem sie um den Anker herum operiert. Das Gebiet innerhalb des Radius um den Anker werden wir im Folgenden als Aktionsgebiet bezeichnen. Eine Armee wird ausschließlich über das Verschieben des Aktionsgebietes gesteuert. Im Abschnitt zu den Armeesteuerungsfunktionen findest du einige Grafiken, die das Prinzip verdeutlichen.

Der Artikel ist folgendermaßen strukturiert: Zuerst beschreiben wir, wie Armeen aufgesetzt werden. Anschließend listen wir Funktionen, mit denen sich der Status einer Armee abfragen lässt. Darauf folgen die elementaren Kontrollfunktionen, die für die Steuerung einer Armee zur Verfügung stehen und wie diese einzusetzen sind. Zum Schluss wollen wir all diese Funktionen zu einer komplexeren Armeesteuerung vereinen, die es zum Beispiel TickOffensiveAIController auch macht.


Aufsetzen einer Armee

Falls du den Abschitt zu Armee-Basisparametern schon kennst, kannst du diesen Abschnitt überspringen. Die Inhalte sind hier der Vollständigkeit halber enthalten.

Armeen werden mit der Funktion SetupArmy(_ArmyTable) aufgesetzt. Der Anker wird auf die angegebene Position gelegt und der angegebene Radius grenzt das Aktionsgebiet ein. Der Parameter _ArmyTable ist ein Table mit den folgenden Key-Value-Paaren:

Key Value-Typ Bedeutung
player Player Id Spieler-Id des Spielers, dem die Armee gehören soll
id Ganze Zahl (0 - 9) Id der Armee. Es darf pro Spieler-Id und Armee-Id maximal eine Armee geben. Somit ist die Anzahl der Armeen pro Spieler auf 10 beschränkt
position Position (Table der Form {X = x, Y = y}) Anfangsposition der Armee
rodeLength Number Radius um position, innerhalb dessen sich die Armee bewegt
beAgressive Boolean Legt fest, ob die Armee auf dem Weg zu einem Angriffsziel Gegner angreifen soll (für ausschließlich defensive Armee irrelevant). true ist hier eigentlich immer sinnvoll

Für einige der Funktionen, die den Armeestatus abfragen, brauchen außerdem eine Angabe der maximalen Armeestärke im Parameter strength:

strength Integer ≤ 8 Anzahl der Truppen, die eine Armee maximal besitzen können soll. Da eine Armee nur höchstens 8 Truppen steuern kann, sollte die Armeestärke 8 nicht überschreiten

Eine Armee für Spieler 2 kann also beispielsweise so aufgesetzt werden:

function CreateArmy()
    -- Wir definieren das Armee-Table
    -- Um die Armee später steuern zu können, muss dieses Table global sein!
    Army = {
        player = 2,
        -- wir wählen die Id 0
        -- bei mehreren Armeen für Spieler 2 müssen alle unterschiedliche Ids haben
        id = 0,
        position = GetPosition("PositionArmy"),
        rodeLength = 4000,
        beAgressive = true,
        strength = 6
    }
 
    SetupArmy(Army)
end

Spieler-Id, Armee-Id, und Aggressivität bleiben für jede Armee fest. Um die Armee zu steuern, werden später nur die Position und die rodeLength, also der Radius, modifiziert.

Achtung: Die Verwendung von Armeen setzt voraus, dass für den KI-Spieler, dem die Armee gehört, auch eine KI aktiv ist! SetupPlayerAi ist also zwingend notwendig! Weil das Spiel abstürzt, wenn eine KI aktiviert wird, ohne, dass der KI-Spieler ein Gebäude besitzt, muss für jeden Spieler mit einer Armee auch mindestens 1 Gebäude auf der Map stehen.
Gleichzeitig gibt es Konflikte mit der Funktion MapEditor_SetupAI. Die Angabe einer Armee-Id ist zwingend erforderlich. MapEditor_SetupAI belegt einige dieser Ids, abhängig vom angegebenen _Strength-Wert und „bemächtigt“ sich aller Armeen, deren Ids MapEditor_SetupAI für sich reserviert. Dadurch werden sie der Steuerung durch eigene Skripte entzogen. Die Tabelle unten zeigt die Armee-Ids, die durch MapEditor_SetupAI nicht mehr zur Verfügung stehen.

_Strength Reservierte Armee-Ids
0 keine
1 1 bis 2
2 1 bis 4
3 1 bis 6

Wichtig: Mit dem Aufruf von SetupArmy hat die Armee keine Truppen und wird sie auch nicht selbstständig rekrutieren! Es wird dem Spiel lediglich klargemacht, dass der angegebene Spieler eine Armee mit der designierten Id bekommt und ein leerer Container dafür erstellt. Wie die Armee Truppen erhalten kann (sowohl gespawnt als auch rekrutiert) wird im Abschnitt zu Truppenerstellung beschrieben.

Ein Wort zur automatischen Truppenverstärkung

Unabhängig davon, wie wir weiter unten die Armee befüllen, wird die KI automatisch versuchen, angeschlagene Truppen zu verstärken, indem sie sie zu einer Kaserne (falls vorhanden) zurückzieht und dort neue Soldaten anwirbt. Dieses Verhalten ist nicht abstellbar und kann insbesondere bei rekrutierten Truppen für unerwünschtes Verhalten sorgen (mehr dazu siehe im entsprechenden Abschnitt). Solange die KI einen Trupp zur Verstärkung zurückzieht, ist dieser kein Teil der Armee und kann darum auch nicht mehr über Armeefunktionen gesteuert werden. Erst nachdem der Trupp wieder komplett ist, gibt die KI ihn frei und fügt ihn automatisch zurück zur Armee hinzu.


Funktionen zum Armeestatus

Mit diesen Funktionen kannst du den Status deiner Armee abfragen: Wie viele Truppen sind darin noch enthalten und wo ist der nächste Gegner zum aktuellen Anker? Alle diese Funktionen haben gemeinsam, dass sie als (ersten) Parameter das Armeetable erwarten, das wir oben angelegt haben. Denke wie weiter oben beschrieben daran, dass Truppen, die von der KI gerade verstärkt werden, nicht Teil der Armee sind, bis sie wieder volle Truppstärke haben!

IsDead

Mit IsDead(_ArmyTable) fragst du ab, ob die angegebene Armee besiegt wurde. Eine Armee gilt als besiegt, wenn sie keine Truppen mehr besitzt. Die Funktion gibt einen Boolean zurück. Die komplette Beschreibung findest du hier.

IsAlive

IsAlive(_ArmyTable) entspricht genau not IsDead(_ArmyTable).

HasFullStrength

HasFullStrength(_ArmyTable) gibt Auskunft darüber, ob die Armee volle Stärke besitzt, das heißt, ob sie über mindestens so viele Hauptmänner verfügt, wie bei strength im ArmyTable angegeben ist. Der Rückgabewert ist ein Boolean. Näheres dazu findest du hier.

IsWeak

IsWeak(_ArmyTable) entspricht genau not HasFullStrength(_ArmyTable).

IsVeryWeak

Mit IsVeryWeak(_ArmyTable) wird ermittelt, ob die Armee weniger als ein Drittel ihrer maximalen Truppenstärke besitzt. Beispielsweise haben wir oben eine strength von 6 angegeben. IsVeryWeak wäre in dem Fall true, wenn die Armee weniger als 2 Truppen hat. Zur Funktionsreferenz gehts hier entlang.

GetNumberOfLeaders

Wenn du abseits von IsDead, HasFullStrength und IsVeryWeak genaue Auskunft über die Sträke einer Armee abfragen möchtest, kannst du das mit GetNumberOfLeaders(_ArmyTable) tun. Die Funktion gibt die genaue Anzahl an Hauptmännern zurück, die sich aktuell in der Armee befinden. Der Artikel in der Funktionsreferenz ist hier.

GetClosestEntity

GetClosestEntity(_ArmyTable, _Radius) gibt die Entity-Id des nächsten Gegners an, der innerhalb des angegebenen _Radius dem aktuellen Anker am nächsten liegt. Ist kein _Radius angegeben, wird der aktuelle Aktionsradius/rodeLength verwendet. Die genaue Beschreibung der Funktion findest du hier.

GetPosition

GetPosition(_ArmyTable) bezogen auf die Armee im _ArmyTable gibt die aktuelle Position des Ankers der Armee zurück. Beachte, dass eine Armee sich auch auf dem Weg zum Anker befinden kann, wodurch die Position der Truppen nicht mehr der Position des Ankers entspricht. Den Referenzartikel findest du hier.


Elementare Funktionen zur Armeesteuerung

Alle Funktionen, die zum Steuern der Armee verwendet werden, müssen ständig wiederholt aufgerufen werden (üblicherweise alle 5 - 10 Sekunden). Das liegt daran, dass jede dieser Funktionen lediglich das Aktionsgebiet verschiebt (also den Anker und den Radius modifiziert). Anschließend wird geprüft, ob alle Truppen der Armee im Aktionsgebiet sind und ob sich Gegner darin befinden. Würden die Funktionen nicht wiederholt aufgerufen werden, würden die Truppen der Armee nicht zuverlässig im Aktionsgebiet stationiert werden und auch nicht gemeinsam einen eindringenden Gegner angreifen.

In der Regel wird also so oder so ähnlich beim Erstellen einer Armee ein SimpleJob aufgesetzt, der die Armee steuert:

function CreateExampleArmy()
    -- hier steht der Code für das ArmyTable
    -- [ ... ]
    SetupArmy(ExampleArmy)
 
    StartSimpleJob("ControlExampleArmy")
end
 
function ControlExampleArmy()
    -- Die Kontrollbefehle, die weiter unten folgen, sollen nur alle 5 Sekunden aufgerufen werden
    -- Denke daran, jeder Kontrollfunktion einen eigenen Counternamen zu geben
    if not Counter.Tick2("ControlExampleArmyCounter", 5) then
        return
    end
 
    -- Wenn die Armee tot ist (= keine Truppen mehr übrig), wird der Kontrolljob beendet
    -- Natürlich kann auch eine andere Bedingung den Job abbrechen. Beispielsweise kann
    -- eine Armee aus einem Gebäude spawnen. Dann ist es viel sinnvoller, den Tod des Gebäudes
    -- statt den der Armee zur Bedingung für das Ende des Kontrolljobs zu definieren
    if IsDead(ExampleArmy) then
        return true
    end
 
    -- hier werden nun die Kontrollfunktionen, die wir weiter unten vorstellen, eingefügt. Diese
    -- können untereinander kombiniert und beispielsweise nur unter verschiedenen
    -- Voraussetzungen aufgerufen werden. Ein komplexeres Beispiel findest du im letzten Abschnitt
    -- dieses Artikels
 
    -- [ ... ]
end

Redeploy

Redeploy(_ArmyTable, _Position, _Radius) ist die wichtigste Funktion zur Steuerung von Armeen.

Defend

FrontalAttack

Advance

Retreat

Synchronize

Truppenerstellung und andere wichtige Armeefunktionen

Probleme beim Rekrutieren

Wenig Kontrolle über die Truppentypen

Wenig Kontrolle über das Verstärkungsverhalten

Beispiel für komplexe Armeesteuerung

scripting/tutorials/level3/armies.1751902251.txt.gz · Zuletzt geändert: 2025/07/07 15:30 von fritz_98