Problem mit zirkulärer Abhängigkeit

  • C++

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von ichduersie.

    Problem mit zirkulärer Abhängigkeit

    Servus,

    ich hab eine Architekturfrage in C++. Ich arbeite an einer Game Engine und möchte die State Machine überarbeiten. Bisher ist es so, dass neue Szenen manuell in eine Enum eingetragen werden müssen und beim Szenenwechsel einfach die changeScene(...) aus dem Manager in der Szene durch ein gebindetes std::function-Objekt aufgerufen wird. Ich möchte das ganze dahingehend umgestalten, dass zum einen die Szenen dynamisch hinzugefügt werden können und dass der Szenenwechsel über ihren Namen / Tag funktioniert. Weitergehend möchte ich gerne ohne das Binding arbeiten, ich persönlich finde das echt nicht schön.

    Ich stoße dabei aber auf das Problem, dass ich ohne Binding eine zirkuläre Abhängigkeit aufbaue (siehe vereinfachtes Klassendiagramm). Im Internet werde ich leider nicht besonders schlau, denn die Wege, diese zirkuläre Abhängigkeit zu umgehen (bspw. mit einer anonymen Klasse und friend-Funktionen) finde ich genauso unschön. Da kann ich dann ja auch direkt funktional programmieren. Und andere Beispiele für eine State Machine ohne Enum finde ich nicht, aber es muss ja einen Weg geben.

    Von daher hoffe ich, dass mir hier jemand helfen kann.
    Liebe Grüße

    Spoiler anzeigen

    C-Quellcode

    1. class Scene {
    2. public:
    3. virtual void load() = 0;
    4. virtual void unload() = 0;
    5. virtual void render(...) = 0;
    6. virtual void update(const std::chrono::high_resolution_clock::duration& deltaTime) = 0;
    7. public:
    8. enum class SceneType : int8_t {
    9. INVALID = -1,
    10. MAIN = 0,
    11. GAME = 1,
    12. OPTIONS = 2,
    13. CREDITS = 3
    14. };
    15. std::function<void(SceneType)> changeScene;
    16. std::function<void()> requestQuit;
    17. };

    C-Quellcode

    1. void Manager::updateScene(const std::chrono::high_resolution_clock::duration& deltaTime) {
    2. switch (currentScene) {
    3. case scenes::Scene::SceneType::MAIN: mainScene.update(deltaTime); break;
    4. case scenes::Scene::SceneType::GAME: gameScene.update(deltaTime); break;
    5. case scenes::Scene::SceneType::OPTIONS: optionsScene.update(deltaTime); break;
    6. case scenes::Scene::SceneType::CREDITS: creditsScene.update(deltaTime); break;
    7. }
    8. }
    9. void Manager::changeScene(scenes::Scene::SceneType scene) {
    10. // Game state machine
    11. // We consciously don't check to see if a scene is trying to reload itself.
    12. // This way a scene can reset itself.
    13. // Unload the current scene
    14. if (currentScene != scenes::Scene::SceneType::INVALID) {
    15. switch (currentScene) {
    16. case scenes::Scene::SceneType::MAIN: mainScene.unload(); break;
    17. case scenes::Scene::SceneType::GAME: gameScene.unload(); break;
    18. case scenes::Scene::SceneType::OPTIONS: optionsScene.unload(); break;
    19. case scenes::Scene::SceneType::CREDITS: creditsScene.unload(); break;
    20. }
    21. }
    22. // Load the new scene
    23. switch (scene) {
    24. case scenes::Scene::SceneType::MAIN:
    25. mainScene.changeScene = std::bind(&Manager::changeScene, this, std::placeholders::_1);
    26. mainScene.requestQuit = std::bind(&Game::exit, game);
    27. mainScene.load();
    28. break;
    29. case scenes::Scene::SceneType::GAME:
    30. gameScene.changeScene = std::bind(&Manager::changeScene, this, std::placeholders::_1);
    31. gameScene.requestQuit = std::bind(&Game::exit, game);
    32. gameScene.load();
    33. break;
    34. case scenes::Scene::SceneType::OPTIONS:
    35. optionsScene.changeScene = std::bind(&Manager::changeScene, this, std::placeholders::_1);
    36. optionsScene.requestQuit = std::bind(&Game::exit, game);
    37. optionsScene.load();
    38. break;
    39. case scenes::Scene::SceneType::CREDITS:
    40. creditsScene.changeScene = std::bind(&Manager::changeScene, this, std::placeholders::_1);
    41. creditsScene.requestQuit = std::bind(&Game::exit, game);
    42. creditsScene.load();
    43. break;
    44. }
    45. currentScene = scene;
    46. }


    // Edit: Die eigentliche State Machine ergänzt. Ist schon echt hart widerlich gemacht <X
    Bilder
    • cd.png

      9,98 kB, 546×368, 87 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ichduersie“ ()

    Ok, das ist mir jetzt echt unangenehm. Ist sitze no joke seit mehreren Wochen an diesem Problem und komme just nachdem ich hier geschrieben habe auf die Lösung :S
    Ist eigentlich echt easy, hab ein einfaches Demoprojekt angehängt, sodass das jeder nachvollziehen kann, wenn ersie möchte. Der Witz war einfach, die Scene für den Manager im Header anonym zu deklarieren und den Header der Scene erst im Source des Managers und nicht im Header reinzunehmen. Ich hoffe, dass solche Krämpfe mit den neuen Modulen nicht mehr auftreten.

    Natürlich werde ich in der Engine keine statischen Klassen sondern Singletons einsetzen. Ist mMn deutlich schöner.
    Dateien
    • Project2.zip

      (4,95 kB, 71 mal heruntergeladen, zuletzt: )
    Mir ist klar, dass du diesen Beitrag schon als gelöst markiert hast, allerdings habe ich da noch eine Frage bezüglich deines dynamischen Szenen Problems (falls du dies noch nicht gelöst hast):
    Wieso verwendest du dafür nicht einfach einen Vector-container der auf dynamisch allozierte Szenen Objekte zurückgreift?
    Oder villeicht auch eine unordered_map wenn du auf die Szenen via ID zugreifen möchtest!
    So kannst du dir dann auch die ganzen switch-statements sparen und mit einem reinen null check überprüfen.

    ~ Elanda
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------
    Genau das hab ich vor. Ich will das switch schmeißen und die Szenen mit ihrer ID in eine map packen. Ich kann das switch zur Laufzeit schließlich nicht verändern, aber es sollen zur Laufzeit Szenen dynamisch hinzugefügt werden können.

    Aber trotzdem danke für deine Anmerkung :)