Jak mogę zaktualizować Ustawienia ekranu z ekranu Opcje bez ponownego uruchamiania?

9

Obecnie tworzę 2D RPG w C ++ 11 z Allegro 5 i boost.

Moim celem jest jakoś zaktualizować ustawienia gry, gdy opcja zostanie zmieniona w menu opcji. Nie chcę zmuszać użytkownika do ponownego uruchomienia gry. Inne gry nie wymagają ponownego uruchomienia podczas zmiany rozdzielczości lub przejścia z trybu pełnoekranowego do okienkowego, więc moja gra też nie powinna. Zobacz uproszczony widok systemu poniżej.

Pamiętaj, że niekoniecznie chcę bezpośrednio wywoływać mój obiekt Game z ekranu OptionsScreen. Linia przerywana ma jedynie zilustrować efekt, który próbuję osiągnąć; w jakiś sposób spowodować aktualizację gry, gdy opcja zostanie zmieniona w innej części systemu.

Szczegółowe wyjaśnienie

ScreenManager zawiera listę wszystkich istniejących GameScreenobiektów. Będą to różne ekrany w grze, w tym wyskakujące okienka. Ten projekt jest mniej więcej zgodny z próbką Game State Management w C # / XNA .

ScreenManagerZawiera odniesienie do mojego Gameobiektu. W Gameinicjuje obiektów i modyfikuje ustawienia gry. Jeśli chcę zmienić rozdzielczość, przejść do trybu pełnoekranowego lub wyciszyć głośność, zrobiłbym to w Gameklasie.

Jednak OptionsScreen obecnie nie ma dostępu do klasy gry. Zobacz poniższy schemat:

GameScreen może sygnalizować trzy zdarzenia onFinished, onTransitionStartoraz onTransitionEnd. Nie ma, onOptionsChangedbo robi to tylko jeden ekran. ScreenManager nie może skonfigurować obsługi zdarzeń w tym celu, ponieważ obsługuje wszystkie ekrany jako GameScreens.

Moje pytanie brzmi: jak mogę zmienić projekt, aby zmiana w menu Opcje nie wymagała ponownego uruchomienia, ale została natychmiast zmieniona? Wolę poprosić mój Gameobiekt o aktualizację po kliknięciu przycisku Zastosuj.

IAE
źródło
To pytanie (nawet jeśli jest związane z grą) wygląda na idealne dopasowanie do programmers.stackexchange.com bo to bardziej o ogólnym projektu OO niż o rzeczywistej grze
bughi
Skąd masz tak niesamowicie wyglądające wykresy?
joltmode
@psycketom: Pierwszy pochodzi z Visio 2013, a drugi jest generowany z kodu przez VS2012. Mam nadzieję, że to pomaga!
IAE

Odpowiedzi:

1

Z tego, co widziałem, najłatwiejszym podejściem jest odczytanie pliku opcji podczas uruchamiania, aby określić bieżące ustawienia wyświetlania; następnie po wyświetleniu ekranu opcji załaduj wszystkie bieżące opcje z pliku.

Po sfinalizowaniu zmian za pomocą przycisku applylub oksą one zapisywane z powrotem w pliku. Jeśli jakiekolwiek zmiany wpłyną na wyświetlanie, powiadom użytkownika, że ​​gra musi zostać ponownie uruchomiona, aby zaczęły obowiązywać.

Po ponownym uruchomieniu gry (teraz nowe) ustawienia wyświetlania są ponownie odczytywane z pliku.

--EDYTOWAĆ--

Annd ... pomógłbym, gdybym zauważył to ostatnie zdanie. Nie chcesz ponownie uruchamiać. Sprawia, że ​​wszystko staje się nieco trudniejsze w zależności od implementacji i biblioteki graficznej zaplecza.

IIRC, Allegro ma wywołanie funkcji, które pozwala na zmianę ustawień wyświetlania w locie. Nie jestem jeszcze na Allegro 5, ale wiem, że możesz to zrobić w 4.

Casey
źródło
Nie sądzę, że problem dotyczy Allegro i jego możliwości. „Moje pytanie brzmi: jak mogę zmienić projekt, aby zmiana w menu opcji nie wymagała ponownego uruchomienia, ale została natychmiast zmieniona?” Ponowne uruchomienie jest spowodowane tym, że „OptionsScreen obecnie nie ma dostępu do klasy gry” i jeśli poprawnie rozumiem, musi je załadować z pliku ustawień.
ClassicThunder
@Casey: Hello Casey! :) Tak, używam pliku konfiguracyjnego do przechowywania odpowiednich danych wyświetlanych, ale chcę mieć możliwość uniknięcia ponownego uruchomienia. To mała gra, a inne gry mogą zmienić rozdzielczość bez konieczności ponownego uruchamiania, więc nie rozumiem, dlaczego mam zmuszać użytkownika do ponownego uruchomienia z moją. To kwestia użyteczności. Chociaż mogłem po prostu wywołać funkcje wysyłania allegro, staram się pozostać OOP i nie mieszać moich połączeń graficznych tylko dlatego, że mogę; Nie uważam tego za dobry projekt.
IAE
Podkreśliłem fragment „bez restartu”, aby inni nie potknęli się o to samo ostatnie zdanie. Przepraszam, że tak ważny fakt nie powinien był nastąpić:
IAE
@SoulBeaver Kto mówi, że musisz to zrobić, aby się nie restartować? Wymaganie tego jest opłacalne, w rzeczywistości staje się coraz bardziej powszechne z dnia na dzień. Niektóre najnowsze wydania (XCOM: Enemy Unknown, Skyrim itp.) Gier o dużym budżecie wymagają ponownego uruchomienia. (fakt, że są one na Steamie, może być winowajcą ze względu na synchronizację opcji po stronie serwera, ale nie idźmy tam ...)
Casey
1
@Casey: Prawda i dla niektórych opcji widzę, dlaczego jest to nawet konieczne, ale w przypadku takich rzeczy, jak pełny ekran / okno czy rozdzielczość ekranu? Nigdy nie widziałem gry, która wymagałaby ponownego uruchomienia tych ustawień. Moje podstawowe pytanie brzmi: dlaczego powinienem zmusić mojego użytkownika do ponownego uruchomienia, gdy gra może automatycznie zmienić ustawienia?
IAE
1

To właśnie robię dla mojej gry. Mam 2 oddzielne funkcje do inicjowania rzeczy, „init” i „reset”. Init jest wywoływany tylko raz podczas uruchamiania i robi rzeczy, które nie zależą od żadnych ustawień, takie jak ładowanie głównych zasobów. Resetowanie wykonuje takie czynności, jak układanie interfejsu użytkownika na podstawie rozdzielczości ekranu, dlatego jest wywoływany przy każdej zmianie ustawień.

init();
bool quit = false;
while( !quit )
{
    createWindow();
    reset();

    bool settings_changed = false;
    while( !quit && !settings_changed )
    {
        ... main loop
        // set quit=true if user clicks 'quit' in main menu
        // set settings_changed=true if user clicks 'apply' in options
    }

    destroyCurrentWindow();
}

Nie jestem zaznajomiony z Allegro, ale moja odpowiedź jest dość ogólna, więc mam nadzieję, że pomoże tobie lub komukolwiek innemu z podobnym problemem.

DaleyPaley
źródło
To ciekawe rozwiązanie. Próbowałem mieć ścisłą kontrolę nad każdym wywołaniem resetu, ale robisz to niezależnie od zmiany. To zdecydowanie coś, co mógłbym wdrożyć i przyjrzę się temu, dzięki!
IAE
1

Bez popsuwania obecnej architektury widzę dwa sposoby. Po pierwsze, możesz przechowywać wskaźnik do Gameinstancji w OptionsScreenklasie. Po drugie, możesz Gamepobrać klasę bieżących ustawień w danym przedziale, powiedzmy co sekundę.

Aby faktycznie dostosować się do nowych ustawień, Gameklasa musi zaimplementować pewien rodzaj funkcji resetowania, która pobiera bieżące ustawienia i ponownie inicjuje na ich podstawie.

Aby uzyskać czyste rozwiązanie, potrzebujesz jakiegoś globalnego menedżera, dlatego wdrożenie go wymaga większego wysiłku. Na przykład system zdarzeń lub system przesyłania komunikatów. Bardzo przydatne jest pozwalanie klasom komunikować się bez tak silnych powiązań, jak agregacja lub kompozycja itd.

Dzięki globalnemu menedżerowi zdarzeń OptionsScreenmoże po prostu globalnie uruchomić zdarzenie przerysowane, które Gamezarejestrowało się do słuchania wcześniej.

Ogólnie można zaimplementować klasę menedżera przechowującą zdarzenia i wywołania zwrotne nasłuchujące na mapie skrótowej. Następnie możesz utworzyć pojedyncze wystąpienie tego menedżera i przekazać do niego wskaźniki do swoich komponentów. Korzystanie z nowszej wersji C ++ jest dość łatwe, ponieważ można używać go std::unordered_mapjako mapy mieszania i std::functiondo przechowywania połączeń zwrotnych. Istnieją różne podejścia, których można użyć jako klucza. Na przykład możesz ustawić łańcuch menedżera zdarzeń, co czyni komponenty jeszcze bardziej niezależnymi. W takim przypadku użyjesz std::stringjako klucza na mapie mieszania. Ja osobiście to lubię i to zdecydowanie nie jest problem z wydajnością, ale większość tradycyjnych systemów zdarzeń działa ze zdarzeniami jako klasami.

danijar
źródło
Witaj Danijar. Już używam boost :: sygnałów do skonfigurowania podstawowego schematu obsługi zdarzeń. Mocną linią od GameScreen do ScreenManager jest w rzeczywistości obsługa zdarzeń. Czy masz jakieś zasoby opisujące architekturę globalnego menedżera wydarzeń? Więcej pracy czy nie, wydaje się, że może to doprowadzić do czystej implementacji.
IAE
Chociaż nie czytałem o tym badań, zaimplementowałem globalnego menedżera wydarzeń dla mojego małego silnika. Jeśli jesteś zainteresowany wdrożeniem, wyślę Ci link. Zaktualizowałem swoją odpowiedź, aby uwzględnić więcej szczegółów.
danijar
1

To jest konkretny przypadek wzorca Observer.

Istnieje rozwiązanie polegające na wywołaniach zwrotnych. Jest to najlepszy sposób na zrobienie tego, jeśli chcesz luźne sprzężenie i myślę, że jest to również najczystsze. Nie będzie to obejmowało żadnych globalnych menedżerów ani singletonów.

Zasadniczo musisz mieć jakiś rodzaj SettingsStore. Tam przechowujesz ustawienia. Gdy utworzysz nowy ekran, będą oni potrzebować wskaźnika do sklepu. W przypadku OptionsScreentego zmodyfikuje niektóre wartości samej wartości ustawień. W przypadku tego po GameScreenprostu je przeczyta. Tak więc w swojej grze stworzyłbyś tylko jedną instancję, która będzie przekazywana na wszystkich ekranach, które tego wymagają.

Teraz ta SettingsStoreklasa będzie miała listę notifiables. Są to klasy, które implementują określony ISettingChangedinterfejs. Interfejs byłby prosty, który zawiera następującą metodę:

void settingChanged(std::string setting);

Następnie na ekranie zaimplementujesz logikę dla każdego ustawienia, na którym Ci zależy. Następnie dodać się do sklepu, aby być zgłaszane: store->notifyOnChange(this);. Po zmianie ustawienia wywołanie zwrotne jest wywoływane z nazwą ustawienia. Nową wartość ustawienia można następnie pobrać z SettingsStore.

Teraz można to dodatkowo uzupełnić następującymi pomysłami:

  • Użyj klasy, która przechowuje ciągi ustawień - mogą być nawet SettingsStoreciągami const, aby zapobiec wklejaniu ciągów.
  • Nawet lepiej, zamiast ciągu, użyj wyliczenia, aby określić, jakie masz ustawienia
  • Możesz nawet podać stare i nowe wartości jako argumenty w wywołaniu zwrotnym, ale byłoby to bardziej skomplikowane, ponieważ możesz mieć wartości łańcuchowe lub int lub cokolwiek. To zależy od Ciebie.
Timotei
źródło
0

Przeczytaj swoje ustawienia z pliku na zmienne. Poproś menedżera ekranu, aby śledził, czy ekran, z którego właśnie przyszedł, był ekranem Opcje, a jeśli tak, to ponownie załaduj ustawienia ze zmiennych. Gdy użytkownik wychodzi z gry, zapisz ustawienia w zmiennych z powrotem do pliku.

131nary
źródło