scripting:tutorials:level3:armies
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
Beide Seiten, vorherige ÜberarbeitungVorherige ÜberarbeitungNächste Überarbeitung | Vorherige Überarbeitung | ||
scripting:tutorials:level3:armies [2025/07/08 10:13] – fritz_98 | scripting:tutorials:level3:armies [2025/07/11 14:55] (aktuell) – [Beispiel für komplexe Armeesteuerung] fritz_98 | ||
---|---|---|---|
Zeile 13: | Zeile 13: | ||
Falls du den Abschitt zu [[ scripting: | Falls du den Abschitt zu [[ scripting: | ||
- | Armeen werden mit der Funktion '' | + | Armeen werden mit der Funktion '' |
^ Key ^ Value-Typ ^ Bedeutung ^ | ^ Key ^ Value-Typ ^ Bedeutung ^ | ||
| **player** | Player Id | Spieler-Id des Spielers, dem die Armee gehören soll | | | **player** | Player Id | Spieler-Id des Spielers, dem die Armee gehören soll | | ||
Zeile 87: | Zeile 87: | ||
===GetClosestEntity=== | ===GetClosestEntity=== | ||
- | '' | + | '' |
===GetPosition=== | ===GetPosition=== | ||
Zeile 160: | Zeile 160: | ||
'' | '' | ||
- | Der Zweck dieser Funktion ist es, eine Armee einfach einer anderen folgen lassen zu können. Es wäre zum Beispiel denkbar, für Armee 1 einen komplexen Controller zu schreiben, dem Armee 2 dann einfach | + | Der Zweck dieser Funktion ist es, eine Armee einfach einer anderen folgen lassen zu können. Es wäre zum Beispiel denkbar, für Armee 1 einen komplexen Controller zu schreiben, dem Armee 2 dann nur folgen soll, anstatt ihren eigenen zu verwenden. |
---- | ---- | ||
Zeile 173: | Zeile 173: | ||
====Truppen spawnen==== | ====Truppen spawnen==== | ||
- | CreateFormation + ConnectLeader | + | Um Truppen zu spawnen und einer Armee hinzuzufügen, |
+ | |||
+ | ^ Key ^ Value-Typ ^ Bedeutung ^ | ||
+ | | **leaderType** | EntityType | Der Entity-Typ des Hauptmanns, für den ein Trupp erschaffen werden soll. **Achtung: | ||
+ | | **position** | Position (Table der Form '' | ||
+ | | **experiencePoints** | Integer | Die Erfahrung, mit der der Hauptmann starten soll. In der [[scripting: | ||
+ | | **maxNumberOfSoldiers** | Integer | Die Anzahl an Soldaten, mit denen der Hauptmann startet und auf die ggf. wieder verstärkt wird. Wenn nichts angegeben wird, wird automatisch ein voller Trupp erstellt. | | ||
+ | | **minNumberOfSoldiers** | Integer | Die Anzahl an Soldaten, ab der die KI versucht, den Trupp an einer Kaserne zu verstärken. Bei '' | ||
+ | |||
+ | **Wichtig: | ||
+ | |||
+ | Wir wollen ein Beispiel geben, in dem zwei verschiedene Truppentypen vor dem passenden Militärgebäude gespawnt werden. Dazu muss jeweils ein '' | ||
+ | <code lua> | ||
+ | function CreateSpawnArmy() | ||
+ | SpawnArmy = { | ||
+ | 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(" | ||
+ | rodeLength = 4000, | ||
+ | beAgressive = true, | ||
+ | strength = 6 | ||
+ | } | ||
+ | |||
+ | SetupArmy(SpawnArmy) | ||
+ | |||
+ | local TroopDescriptionSword = { | ||
+ | leaderType = Entities.PU_LeaderSword1, | ||
+ | position = GetPosition(" | ||
+ | experiencePoints = MEDIUM_EXPERIENCE, | ||
+ | maxNumberOfSoldiers = 4, | ||
+ | minNumberOfSoldiers = 0 | ||
+ | } | ||
+ | |||
+ | local TroopDescriptionPoleArm = { | ||
+ | leaderType = Entities.PU_LeaderPoleArm2, | ||
+ | position = GetPosition(" | ||
+ | experiencePoints = LOW_EXPERIENCE, | ||
+ | maxNumberOfSoldiers = 4, | ||
+ | minNumberOfSoldiers = 0 | ||
+ | } | ||
+ | |||
+ | local TroopDescriptionBow = { | ||
+ | leaderType = Entities.PU_LeaderBow1, | ||
+ | position = GetPosition(" | ||
+ | experiencePoints = HIGH_EXPERIENCE, | ||
+ | maxNumberOfSoldiers = 4, | ||
+ | minNumberOfSoldiers = 0 | ||
+ | } | ||
+ | |||
+ | -- Erstelle jeweils 2 Trupps Schwertkämpfer, | ||
+ | for _ = 1, 2 do | ||
+ | EnlargeArmy(SpawnArmy, | ||
+ | EnlargeArmy(SpawnArmy, | ||
+ | EnlargeArmy(SpawnArmy, | ||
+ | end | ||
+ | end | ||
+ | </ | ||
+ | |||
+ | Im [[scripting: | ||
====Truppen rekrutieren==== | ====Truppen rekrutieren==== | ||
- | BuyLeader + ConnectUnemployedLeader | + | Für das Rekrutieren von Truppen gibt es leider keine vorgefertigte Comfortfunktion, |
+ | |||
+ | <code lua> | ||
+ | function ArmyBuyLeader(_ArmyTable, | ||
+ | -- Check if Army is already at full strength | ||
+ | if HasFullStrength(_ArmyTable) then | ||
+ | return | ||
+ | end | ||
+ | |||
+ | local NumberOfLeaders = GetNumberOfLeaders(_ArmyTable) | ||
+ | -- Try to add a leader to the army first | ||
+ | AI.Entity_ConnectUnemployedLeaderToArmy(_ArmyTable.player, | ||
+ | |||
+ | -- Check if a new leader could be connected. If yes, no further action is required | ||
+ | if GetNumberOfLeaders(_ArmyTable) > NumberOfLeaders then | ||
+ | return | ||
+ | end | ||
+ | |||
+ | -- If no unemployed leader was added, recruit a new one | ||
+ | AI.Army_BuyLeader(_ArmyTable.player, | ||
+ | AI.Entity_ConnectUnemployedLeaderToArmy(_ArmyTable.player, | ||
+ | end | ||
+ | </ | ||
+ | |||
+ | Die '' | ||
+ | <code lua> | ||
+ | ArmyBuyLeader(RecruitArmy, | ||
+ | </ | ||
+ | Das hat den Grund, dass sich die KI an ihre Technologiestufe halten muss. Im Skript kann man nur die Truppenkategorie angeben, die rekrutiert werden soll. Der konkrete Truppentyp muss der KI via [[scripting: | ||
+ | |||
+ | **Achtung: | ||
+ | |||
+ | Auch hierfür wollen wir ein kleines Beispiel schreiben. Die Voraussetzungen für das folgende Skript sind eine Map, die folgendes enthält: | ||
+ | * Mindestens eine Kaserne und ein Schießplatz für Spieler 2 | ||
+ | * Ein '' | ||
+ | * Ausreichend Taler, Holz und Eisen für Spieler 2 | ||
+ | * Eine aktive KI für Spieler 2 | ||
+ | <code lua> | ||
+ | function CreateRecruitArmy() | ||
+ | RecruitArmy = { | ||
+ | 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(" | ||
+ | rodeLength = 4000, | ||
+ | beAgressive = true, | ||
+ | strength = 6 | ||
+ | } | ||
+ | |||
+ | SetupArmy(RecruitArmy) | ||
+ | |||
+ | -- Rekrutieren muss etwas zeitverzögert stattfinden, | ||
+ | -- zur Armee hinzugefügt werden. Wir starten darum einen SimpleJob, der nach einigen Sekunden | ||
+ | -- Verzögerung die Truppen rekrutieren lässt | ||
+ | StartSimpleJob(" | ||
+ | end | ||
+ | |||
+ | function InitRecruitArmy() | ||
+ | if Counter.Tick2(" | ||
+ | -- Erstelle jeweils 3 Trupps Schwertkämpfer und Bogenschützen | ||
+ | for _ = 1, 3 do | ||
+ | ArmyBuyLeader(RecruitArmy, | ||
+ | ArmyBuyLeader(RecruitArmy, | ||
+ | end | ||
+ | -- Sobald das passiert ist, beende den Job | ||
+ | return true | ||
+ | end | ||
+ | end | ||
+ | </ | ||
====Probleme beim Rekrutieren==== | ====Probleme beim Rekrutieren==== | ||
+ | |||
+ | Einer KI ihre Truppen über Rekrutierung zu stellen ist die optisch schönere Lösung, bringt aber einige Probleme mit sich. Diese zu umgehen ist mit den Armee-Funktionen, | ||
===Wenig Kontrolle über die Truppentypen=== | ===Wenig Kontrolle über die Truppentypen=== | ||
+ | |||
+ | Wenn du dir den Code für die Funktion '' | ||
+ | |||
+ | Das liegt daran, dass die Funktion '' | ||
+ | |||
+ | Folglich ist die Rekrutierung von beispielsweise reinen Reiterarmeen nicht zuverlässig möglich, wenn auch andere Armeetypen entstehen sollen. | ||
===Wenig Kontrolle über das Verstärkungsverhalten=== | ===Wenig Kontrolle über das Verstärkungsverhalten=== | ||
+ | |||
+ | Wie weiter oben angemerkt, wird die KI versuchen, angeschlagene Truppen zurückzuziehen und an der nächsten Kaserne zu verstärken. Bei Spawntruppen lässt sich der Schwellwert der Truppenstärke, | ||
===Wenig Kontrolle über den Ausbildungsort=== | ===Wenig Kontrolle über den Ausbildungsort=== | ||
+ | |||
+ | Wie bei [[scripting: | ||
+ | |||
+ | ---- | ||
=====Beispiel für komplexe Armeesteuerung===== | =====Beispiel für komplexe Armeesteuerung===== | ||
+ | |||
+ | Wir wollen die Status-, Steuerungs- und Spawnfunktionen, | ||
+ | |||
+ | Um die Armeesteuerung zu demonstrieren, | ||
+ | |||
+ | Das Verhalten der Banditen soll dabei ganz einfach gehalten sein: Sie sollen nur verteidigen und in regelmäßigen Abständen aus ihren Türmen respawnen. Nach einiger Zeit stoppt der Respawn, sodass Crawford letztendlich die Oberhand gewinnt. | ||
+ | |||
+ | Das Verhalten der Truppen von Crawford soll komplexer sein: | ||
+ | * Während die Armee nicht die volle Truppenzahl umfasst, soll sie eine defensive Position einnehmen | ||
+ | * Auf der defensiven Position wird die Armee durch den Spawn neuer Truppen verstärkt | ||
+ | * Sobald die Armee ihre volle Stärke besitzt, soll sie zu einer offensiven Position vorrücken | ||
+ | * Auf der offensiven Position läuft ein Counter. Sobald dieser abgelaufen ist, greift die Armee an | ||
+ | * Falls sie so viele Truppen verliert, dass sie als " | ||
+ | * Falls keine Gegner mehr im Angriffsgebiet sind, zieht sie sich auf die offensive Position zurück | ||
+ | Beide Armeen sollen den Respawn einstellen, wenn die Spawngebäude zerstört wurden. Der Ablauf wird in diesem Zustandsdiagramm noch einmal verdeutlicht: | ||
+ | |||
+ | {{ scripting: | ||
+ | |||
+ | Um die Zustände in Lua umzusetzen, programmieren wir einen [[https:// | ||
+ | |||
+ | Folgende Positionen und Gebäude setzen wir auf die Karte: | ||
+ | * **Für die Crawford-Armee: | ||
+ | * Eine Kaserne mit dem Namen ''" | ||
+ | * Vor der Kaserne und dem Schießplatz jeweils ein '' | ||
+ | * Dem Banditenlager abgewandt ein '' | ||
+ | * **Für das Banditenlager: | ||
+ | * Zwei Banditentürme mit den Namen ''" | ||
+ | * Ein '' | ||
+ | |||
+ | Damit dieser Artikel nicht mit Lua-Code überquillt, | ||
+ | |||
+ | Wichtig sind insbesondere folgende Aspekte: | ||
+ | |||
+ | ===Definition der Zustände=== | ||
+ | Wir nummerieren die Zustände einer Armee durch, sodass sie im Skript besser lesbar sind und beim Programmieren Fehler vermieden werden. Dafür definieren wir ein globales Table, das aussagekräftige Namen verwendet: | ||
+ | <code lua> | ||
+ | ArmyStates = { | ||
+ | Defense = 1, | ||
+ | Offense = 2, | ||
+ | Attack = 3 | ||
+ | } | ||
+ | </ | ||
+ | In der Kontrollfunktion für die Crawford-Armee prüfen wir dann immer zuerst, in welchem Zustand sich die Armee befindet, fragen dann ab, ob ein Zustandsübergang möglich ist und führen anschließend die dem Zustand zugehörige Aktion aus. | ||
+ | |||
+ | ===Zusätzliche Parameter im Army-Table=== | ||
+ | Für beide Armeen brauchen wir zusätzliche Parameter, die über die [[scripting: | ||
+ | <code lua> | ||
+ | spawnDelay = 3, | ||
+ | spawnAmount = 2, | ||
+ | spawnCounter = 0, | ||
+ | spawnCycles = 12, | ||
+ | spawnLifelines = {" | ||
+ | </ | ||
+ | Diese Parameter müssen wir selbst in den jeweiligen eigenen Kontrollfunktionen verwenden - sie haben keinen Einfluss auf die vom Spiel bereitgestellten Funktionen. | ||
+ | |||
+ | ===Nur eingeschränkt zum Copy-Paste geeignet=== | ||
+ | Die Kontrollfunktionen, | ||
+ | |||
+ | ====Beispielmap von Play4Fun==== | ||
+ | |||
+ | Play4Fun hat ebenfalls eine Karte erstellt, die komplexe Armeesteuerung demonstriert. Wenn du dir Armeeverhalten anschauen möchtest, das über den Rahmen dieses Wikis hinaus geht, findest du hier den Downloadlink: | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Im nächsten Kapitel wird beschrieben, | ||
+ | |||
+ | [[ scripting: | ||
+ | [[ scripting: | ||
+ |
scripting/tutorials/level3/armies.1751969598.txt.gz · Zuletzt geändert: 2025/07/08 10:13 von fritz_98