Wäre es nicht besser, m_pFormationType mit dem Wert aus der CivUnitsInfo zu initialisieren? Damit Maschinen FORMATION_TYPE_MACHINE und Menschen FORMATION_TYPE_DEFAULT erhalten?
Wäre es nicht besser, m_pFormationType mit dem Wert aus der CivUnitsInfo zu initialisieren? Damit Maschinen FORMATION_TYPE_MACHINE und Menschen FORMATION_TYPE_DEFAULT erhalten?
That's why I am here: Mein Mod
Mehr Technologien, mehr Einheiten, mehr Zivilisationen, mehr Gebäude
Die aktuelle Story zum Mod:
Die Vereinigten Staaten von Amerika
Alte Stories zu alten Versionen:
Alte Storys
Wäre möglich, aber wegen potentiellen Seiteneffekten würde ich das nicht machen. Der Basis-BTS-Code enthält zwar keinen Seiteneffekt, aber es könnte sich beispielsweise der Einheitentyp dynamisch ändern.
Die Standardvariante von getFormationType ist ja
Das ist flexibel und weniger anfällig für Seiteneffekte.Code:const char* CvUnit::getFormationType() const { return m_pUnitInfo->getFormationType(); }
Gibt IHMO keinen Grund das bei der Initialisierung auf einen fixen Wert zu setzen.
Bringt die Änderung im Konstruktor eine Verbesserung?
Ich glaube schon, dass es jetzt besser ist. Zumindest konnte ich im Weltenbauer 32 Schiffe setzen, ohne dass es abgestürzt ist. Danach habe ich den Versuch beendet. Vorher ist es gerne schon einmal beim 4. abgeschmiert. Ganz sicher sein kann ich mir natürlich nicht.
Vielen Dank für deine Hilfe.
That's why I am here: Mein Mod
Mehr Technologien, mehr Einheiten, mehr Zivilisationen, mehr Gebäude
Die aktuelle Story zum Mod:
Die Vereinigten Staaten von Amerika
Alte Stories zu alten Versionen:
Alte Storys
Muss nur mal kurz Motzen…
Aufgrund diverser Probleme mit neueren Visual Studio-Versionen habe ich für Civ4, sonst nutze ich den Krempel ja nicht, noch 'Visual Studio 2010' installiert.
Dort wollte ich nun eine C-Library von mir einbinden und muss feststellen, dass die 2010er-Version nur den C-Standard C89 unterstützt Da gibt es leider Probleme mit der Definition von Strukturen, die später flexibler wurden.
Das Umfrickeln der Header in eine konforme Syntax hatte was vom Bau einer Zeitmaschine. Und wie eine Zeitmaschine funktioniert es natürlich danach immer noch nicht.
Edit: Mittlerweile habe ich es hinbekommen, aber die externe Lib flog kurz darauf wieder raus
Geändert von Ramkhamhaeng (03. Februar 2019 um 10:19 Uhr)
Bei der Pfadsuche der Einheiten kommt ein *-Algorithmus, ausgehend vom Zielfeld (Z) zum Einsatz.
Hat mal jemand versucht diese Suche zu invertieren und vom Einheitenfeld (S) auszugehen?
Abstrakter Hintergrund der Frage:
Ich habe eine Einheit gegeben, die mit einer Eigenschaft X_S ∈ {a₁, a₂,… } auf dem Startfeld S ausgestattet ist.
In Abhängigkeit vom kürzesten Pfad von S zu Plot P kann ich dem P den Wert X_P zuweisen.
Am Ende des *-Algos erhalte ich so X_T.
Beginne ich aber auf dem Zielfeld, bin ich aufgeschmissen, und kann diese Eigenschaft nicht ableiten.
Konkretes Beispiel:
Einer Einheit ist eine Kante(=Fluss) zugewiesen. Ich möchte die Einheit nun entlang der Fluss-Kanten bewegen.
Irgend ein Mod hatte seine eigene A*-Suche reingecoded bzw habe ich mir den Code dazu mal angesehen. Müsste suchen, welcher das war.
Ich kenne zu mindest dein Problem [des Invertieren] gut. Um sicher zu gehen, muss der Code eigentlich immer Eigenschaften von Plot Px, von Plot Py und von Plot Px zu Plot Py (Fluss zum Beispiel) abfragen um alle Möglichkeiten abzudecken. Firaxis kam ja nie auf die Idee, dass man canMoveInto nicht nur ein Zielplot brauch. Irgend wo liegt noch Arbeitscode von mir Brach, der das ändern sollte.
Geändert von rucivfan (03. Februar 2019 um 22:01 Uhr)
Habe mal ein weiter geschaut. Ein großes Problem ist die Pfad-Generierung beim "g"-Befehl. (Hatte dazu, glaube ich, schon mal vor ein paar Jahren was geschrieben )
In CvSelectionGroup kann man Ziel und Start invertieren.
Hier könnte ich das Problem vermutlich lösen.Code:gDLL->getFAStarIFace()->SetData(&GC.getPathFinder(), this); bSuccess = gDLL->getFAStarIFace()->GeneratePath(&GC.getPathFinder(), pFromPlot->getX_INLINE(), pFromPlot->getY_INLINE(), pToPlot->getX_INLINE(), pToPlot->getY_INLINE(), false, iFlags, bReuse);
Aber beim "g"-Menu ruft die Exe(!) nicht getPathFinder() sondern getInterfacePathFinder() auf, und mit diesem dann GeneratePath...
Der Algorithmus heißt A* Unter Civ-Bedingungen ist der grundsätzlich nicht reziprok, vom Hügel runter braucht weniger BP als auf den Hügel hoch.
Mach doch mal bitte eine Zeichnung, welches Problem gelöst werden soll.
Das mit den Hügeln stimmt, aber wenn man die Felder in der Kostenfunktion auch invertiert, solle die invertierte Reihenfolge die gleichen Bewegungskosten erzeugen.
Skizzierung des Problems/Rule sets.
1. Schiffen wird auf Landfeldern eine Kante zugeordnet¹. Sie dürfen auf Ozeanfelder, Städte & Festungen (wie bisher) ziehen, aber
sich nun auch noch entlang von Flußkanten bewegen.
(=> Auf andere, an das gleiche Feld grenzende, Flüsse darf nicht gesprungen werden.)
2. Die Bewegungskosten werden pro benutzter Flußkante berechnet, nicht pro Feld.
(Die Funktion für die passenden Transitionskosten (und weiteren Meta-Daten) habe ich auch bereits erstellt.)
Mein derzeitiger Kommentar im Code dazu.
Den aber nicht zu wörtlich nehmen, da alles noch „im Fluss“, um im Bilde zu bleiben Beispielsweise wird am Anfang noch von NORTH, etc. geschrieben während es später NORTH_CW, NORTH_CCW, etc sind. Da habe ich später den vier Kanten noch eine Ausrichtung gegeben (D.h. es sind 8 Fälle.)
Achtung Spoiler:
3. Nur wenn die Route für die nächsten zwei Felder definiert ist, kann man für das mittlere Feld die minimalen Kosten berechnen.
Steht man beispielsweise auf der Nordkante von Plot1, kann man auf zwei Arten nach Plot2, ziehen, aber minimale Kosten bei der Bewegung nach Plot3 hat man nur bei einer Variante.
Statt einem Schiff also eine Kante zuzuweisen, ist es hilfreich während der Pfadberechnung mehrere mögliche Kanten (Quantenzustand ^^) als Ort für das Schiff anzunehmen und sich dann bei der Bewegung nach Plot 3 für die billigste zu entscheiden.Code:======= ======= ^^ || {Plot1}||{Plot2} || || ||{Plot3} || =,|| — Flusskante, ^^ — Startposition
Ich bin mir noch nicht sicher, ob man diesen Quantenzustand beibehält, wenn der Spieler in Plot2 zwischenhält, oder ob man sich dann für eine Variante entscheiden (Der Spieler beobachtet das Schiff ja ^^).
Wenn man sich dann aber zufällig für eine Kante entscheidet, kann das die Kosten erhöhen.
Das könnte den Spieler verwundern.
Hier nun mal ein Beispiel, was das Backtracking-Problem beim jetzigen A*-Algo zeigt:
Das Schiff soll von Kante '^^' zu '°°' ziehen und der Spieler klickt auf das Target-Feld.Code:====== ====== ====== ====== || || || {X2} || || || ======= ====== ====== ^^ || || °° {Start} || || {X1} {Targ.} || || ====== ====== || || ||
Speichert man nun immer nur die Daten mit den minimalen Kosten zwischen,
so wird man beim Backtracking auf Feld X2 die Kosten für den südlicheren Fluss gespeichert
haben.
Ab da ist es, auf dem Weg zum Start-Plot, nicht mehr möglich die korrekten Kosten zu rekonstrieren.
Wenn ich das zugehörige Auslesen des Pfades auch invertiere scheint das schon zu klappen
Das mit den Hügel-Kosten, etc muss ich aber noch abklopfen.
PHP-Code:
#if 0
FAStarNode* CvSelectionGroup::getPathLastNode() const
{
return gDLL->getFAStarIFace()->GetLastNode(&GC.getPathFinder());
}
CvPlot* CvSelectionGroup::getPathFirstPlot() const
{
FAStarNode* pNode;
pNode = getPathLastNode();
if (pNode->m_pParent == NULL)
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
}
while (pNode != NULL)
{
if (pNode->m_pParent->m_pParent == NULL)
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
}
pNode = pNode->m_pParent;
}
FAssert(false);
return NULL;
}
CvPlot* CvSelectionGroup::getPathEndTurnPlot() const
{
FAStarNode* pNode;
pNode = getPathLastNode();
if (NULL != pNode)
{
if ((pNode->m_pParent == NULL) || (pNode->m_iData2 == 1))
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
}
while (pNode->m_pParent != NULL)
{
if (pNode->m_pParent->m_iData2 == 1)
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_pParent->m_iX, pNode->m_pParent->m_iY);
}
pNode = pNode->m_pParent;
}
}
FAssert(false);
return NULL;
}
#else
/* Flip start and end of path. Required if GeneratePath plots are filpped. */
// Helper function
FAStarNode* /*CvSelectionGroup::*/getPathFirstNode() //const
{
FAStarNode* pNode;
pNode = gDLL->getFAStarIFace()->GetLastNode(&GC.getPathFinder());
while (pNode->m_pParent != NULL)
{
pNode = pNode->m_pParent;
}
FAssert(pNode != NULL);
return pNode;
}
FAStarNode* CvSelectionGroup::getPathLastNode() const
{
return getPathFirstNode();
}
CvPlot* CvSelectionGroup::getPathFirstPlot() const
{
FAStarNode* pNode;
pNode = gDLL->getFAStarIFace()->GetLastNode(&GC.getPathFinder());
if( pNode == NULL || pNode->m_pParent == NULL){
return NULL;
}
pNode = pNode->m_pParent;
return GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
}
CvPlot* CvSelectionGroup::getPathEndTurnPlot() const
{
FAStarNode* pNode;
pNode = gDLL->getFAStarIFace()->GetLastNode(&GC.getPathFinder());
if (NULL != pNode)
{
//TODO "max-1" statt 1 als Abbruchkriterium?
if ((pNode->m_pParent == NULL) || (pNode->m_iData2 == 1))
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
}
while (pNode->m_pParent != NULL)
{
//TODO "max-1" statt 1 als Abbruchkriterium?
if (pNode->m_pParent->m_iData2 == 1)
{
return GC.getMapINLINE().plotSorenINLINE(pNode->m_pParent->m_iX, pNode->m_pParent->m_iY);
}
pNode = pNode->m_pParent;
}
}
FAssert(false);
return NULL;
}
#endif
Ehrlich gesagt, sehe ich keinen Vorteil einer Invertierung. Auf jeden Fall wird das eine lustige Sache, da eine Einheit ja einen Plot zugeordnet sein muss aus Verwaltungstechnischen Gründen und auch um sie anzugreifen.
Ich bin jedenfalls gespannt, wie das bei dir läuft. Es klingt sehr interessant und ist natürlich auch realistischer als das, was ich derzeit mit meinen Flussschiffen mache. Aber einen hilfreichen Beitrag kann ich, fürchte ich, hier nicht machen.
That's why I am here: Mein Mod
Mehr Technologien, mehr Einheiten, mehr Zivilisationen, mehr Gebäude
Die aktuelle Story zum Mod:
Die Vereinigten Staaten von Amerika
Alte Stories zu alten Versionen:
Alte Storys
Der Vorteil ist, dass ich die erlaubten Kanten eines Feldes kenne.
Gehen wir es mal an einem, ans vorherige angelehnte, Beispiel durch.
N_i sind die Nachbarfelder des Starts.
N3 ist nicht direkt erreichbar, aber über eine Schleife des Flusses.
Nun wollen wir die Kosten bis zu N3 ermitteln.
Code:====== ====== ====== ====== || || N1 || N2 X2 Y1 Y2 || || || ======= ====== ====== ^^ || || || °° || {Start} N3 || Berg || X1 || Y3 || || || || || ====== ====== || N4 N5 || ||
- Der Test der Vorbedingung canMoveInto(N3, ...) liefert True, da N3 eine Flusskante hat.
- Die Kosten für die N_i werden berechnet:
- N1 ist mit Kosten 0 (intern 1/60-Bewegungspunkt, um unendliche Schleifen zu vermeiden.) erreichbar. (Das sollte ggf. noch mal überdacht/verfeinert werden, da es den A*-Algo ausbremmst.)
- N2 ist auf einer Kante mit den Kosten 1 erreichbar. Die (Kante, unitID) wird im CvPlot-Objekt zwischengespeichert.
- N3-N5 erhalten die Kosten 'unendlich', da kein direkter Weg existiert.
- Der A*-Algo geht zum Plot mit den geringsten Kosten, N2. Von dort kann nur X2 erreicht
werden, wobei diesmal die Kosten 2 betragen, da die Zwischenwerte die Westkante als Start vorgeben.- Y1 wird mit Kosten 1 erreicht.
- Y2 wird mit Kosten 1 erreicht.
- Y3 wird mit Kosten 2 und zwei möglichen Zielkanten erreicht.
- X1 kann nun mit Kosten 1 oder 2 erreicht werden, je nachdem ob man an der Nord- oder Ost-Kante von Y3 startet.
- Jetzt haben wir ein ungelöstes Problem: X2 ist schon abgehandelt und kann nicht erneut betreden werden (TODO ^^) Ignorieren wird das mal...
- N3 wird von X2 aus erreicht.
Wenn der Fluss nördlich von X2 zu X2 gehört, müsste der Fluss südlich logisch folgend zu den Feld Berg gehören. X2 kommt doch nur einmal vor.
Bei Civ4 sind die Flüsse intern immer einem Feld zugeordnet, aber hier sollten sie als beiden Nachbarfeldern zugehörig angesehen werden.
Auf das Feld von X1 habe ich einen Berg gesetzt, um zu verdeutlichen, dass dieses Feld nicht betreten werden kann. Dann würde das „Problem“ des A*-Algo, dass er kein Feld zweimal abarbeiten kann, nicht zum tragen kommen.