Die Reihenfolge, die ich in meinem letzten Post beschrieben hatte, habe ich aus einigen Gründen nochmal überarbeitet. Das eigentlich einzige InputEvent was "Zustimmung" von Avatar- und Physik-System braucht, ist Movement. Alles andere kann alleine vom Avatar-System entschieden und umgesetzt werden.
Betrachten wir mal den Fall dieser Bewegungs-Events... Verhindert werden kann eine Bewegung durch Kollision mit dem Terrain / einem anderen Objekt (Erkennung ist Aufgabe meines Physik-Systems) oder durch Bewegungsunfähigkeit der Spielfigur (Lähmung, Tod etc.). Andere Events (Angreifen, Zaubern, Item benutzen, eine Truhe looten etc.) bedürfen im Grunde nur noch der "Zustimmung" des Avatar-Systems (nach wie vor als Subsummierung der "eigentlichen" Spiel-Logik). Also ist es prinzipiell möglich das InputEvent zunächst der Physik zu übergeben. Daher ist meine Überlegung folgende:
- Das Input- bzw. KI-System erzeugen gemäß Eingabe bzw. KI-Logik die neuen InputEvents und leitet sie an das Avatar-System weiter.
- Das Avatar-System prüft nun, ob die gegebenen InputEvents erlaubt sind (z.B. ob gelähmt oder tot, ob genug Manavorrat etc.). Sind die Events erlaubt, wird das Behavior state des Objekts entsprechen manipuliert (Move, Attack, Cast, etc.) und die Events nun an das Physik-System weitergeleitet.
Alle Events die nicht zugelassen wurden (z.B. weil die Figur gelähmt ist) werden "verschluckt". Das Input-System erhält keine Aussage darüber. Ggf. könnte das Audio-System informiert werden, um einen "Not enough Mana"-Sound abzuspielen
Mehr passiert im Avatar-System an dieser Stelle noch nicht, d.h. erstmal nur das Reihe behandeln der InputEvents.
- Weiter geht es im Physik-System: Hier sind inzwischen die vom Avatar-System abgesegneten InputEvents eingegangen. Für die Bewegungen unter den Events prüft das System auf Kollision. Kommt es zu einer Kollision, wird ein Physik-Event mit näheren Details an das Avatar-System gesendet. Ggf. könnte es auch an das Audio-System geschickt werden, um einen Kollisions-Sound (irgendwo-dagegenrenn-Sfx ) abzuspielen.
Alle InputEvents die vom Physik-System abgesegnet wurden, werden an das Grafik-System weitergeleitet.
Im Anschluss werden die eigentlichen Bewegungsinterpolationen und ggf. weitere Kollisionstests durchgeführt (und damit ggf. weitere Physik-Events generiert).
- Nun kommt das Avatar-System zum "richtigen" Einsatz: Erstmal werden die angesammelten Physik-Events abgearbeitet, d.h. Figuren die bisher das Behavior Move hatten werden wieder auf Idle zurückgesetzt. Sollte ein Bewegungs-InputEvent vom Avatar-System zuvor akzeptiert worden, vom Physik-System aber aufgrund Kollision abgelehnt worden sein, ist das Gesamtbild ab dieser Stelle wieder stimmig.
Danach werden diverse spiellogische Sachen erledigt: Schadensberechnung, Looting, Skill-Cooldowns etc. durchführen.
- Danach kommt schließlich das Grafik-System zum Zuge: Hier sind inzwischen InputEvents eingegangen die dazu verwendet werden neue Animationen abzuspielen. So weiß das Grafik-System z.B., dass sich eine bestimmte Figur X nun in Richtung Y bewegt und kann die entsprechende Animation abspielen.
Zum Schluss wird die Szene dann noch gezeichnet
Oder etwas verknappt (aus Sicht der Events):
- InputEvents von Input-/KI-System an Avatar-System senden
- InputEvents im Avatar-System prüfen, ggf. an Physik-System weiterleiten
- InputEvents im Physik-System prüfen, ggf. umsetzen und an das Grafik-System weiterleiten oder PhysicsEvents an das Avatar-System senden
- PhysikEvents im Avatar-System auswerten
- InputEvents im Grafik-System auswerten.
Hmmm...mir gefällt das weiterleiten des Events nicht so.
Du musst dann ja quasi sicher stellen das es auch durch alle Systeme durch geht und wenn du mal vergisst weiterzuleiten dann kriegen die anderen es vll nicht.
Naja, ich habe mir Utility-Klassen geschrieben, hier mal die Auszüge aus der Headerfile dazu:
|
C-/C++-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
|
template <typename E>
class EventListener {
private:
std::vector<E> in;
public:
/// Push an event to the listener
void pushEvent(E const & event);
/// Dispatch all using a concrete listener
template <typename Listener>
void dispatch(Listener& listener);
};
template <typename E>
class EventSender {
private:
std::vector<E> out;
std::vector<EventListener<E>*> target;
public:
/// Enqueue event for sending
void propagate(E const & event);
/// Register listener
void addListener(EventListener<E>& listener);
/// Unregister listener
void removeListener(EventListener<E>& listener);
/// Send all enqueued events
void broadcast();
};
|
Ich denke wenn ich die
addListener-Aufrufe beim Erzeugen der System-Instanzen nicht vergesse, dürfte das ganze relativ glatt laufen
Das PhysikSystem setzt dann z.b. isCollision-Flag und das AvatarSystem dann den isDead-Flag.
Zum Schluss entscheidet das MovementSystem ob sich das Object weiter bewegen darf.
Ich weis ja nicht was in ein PhysicSystem noch so alles drin steck aber vll könntest du es noch mehr aufteilen z.b. in eine CollisionsSystem, GravitationsSystem, BulletSystem, ...
Das MovementSystem soll dann auch wirklich NUR prüfen ob es ich bewegen darf bzw. die neue Position setzen.
Alles andere wie Kollisionsprüfung macht dann z.b. das CollisionsSystem.
Ich persönlich mag eine "exzessive" Aufteilung in Subsysteme nicht so sehr .. für mich sollte ein System in sich "rund" sein, so dass ich es als Black-Box verwenden kann: Es erwartet gewisse eingehende Events, manipuliert die Daten entsprechend bzw. liefert es bestimmte Events zurück. Wenn ich z.B. das Physik-System in Kollisions- und Bewegungssystem zerlege - oder das Grafik-System in Animations- und Render-System, habe ich zwischen den Physik- bzw. Grafik-Sub-Systemen ziemlich viele Abhängigkeiten:
- Das Kollisionssystem muss wissen wohin sich ein Objekt bewegt .. und damit nicht nur Position + Richtung, sondern auch dass es sich bewegt.
- Das Bewegungssystem braucht die Angaben auch, um die Bewegung ausführen zu können.
- Das Animationssystem (dabei beziehe ich mich mal konkret auf die Thor-Library und die dort enthaltene Animator-Implementierung!) muss das zu animierende Objekt kennen (Sprite etc.).
- Das Rendersystem braucht diesen Sprite aber auch.
Dabei stellt sich dann die Frage: Wem "gehört" der Sprite: Animations- oder Rendersystem? Wem "gehören" Position, Richtung und "isMoving"-Flag: Kollisions- oder Bewegungssystem? Auch aus diesen Gründen habe ich die Systeme zusammengefasst.
Um das ganze abzurunden: Ich will dir die Aufteilung in kleinere Subsystem nicht zerreden
Sicherlich gibt es auch gute Gründe dafür, wie z.B. die aus der geringen Größe und Komplexität der Logik resultierende Übersichtlichkeit
LG Glocke
PS: An diesem Post habe ich jetzt ewig geschrieben
Irgendwie habe ich den gesamten Text mind. 2x komplett gelöscht, meine Gedanken umstrukturiert und neu formuliert... das ganze ist gar nicht so trivial wie es vllt. scheint