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 GameScreen
obiektó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 .
ScreenManager
Zawiera odniesienie do mojego Game
obiektu. W Game
inicjuje 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 Game
klasie.
Jednak OptionsScreen obecnie nie ma dostępu do klasy gry. Zobacz poniższy schemat:
GameScreen może sygnalizować trzy zdarzenia onFinished
, onTransitionStart
oraz onTransitionEnd
. Nie ma, onOptionsChanged
bo robi to tylko jeden ekran. ScreenManager nie może skonfigurować obsługi zdarzeń w tym celu, ponieważ obsługuje wszystkie ekrany jako GameScreen
s.
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 Game
obiekt o aktualizację po kliknięciu przycisku Zastosuj.
Odpowiedzi:
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
apply
lubok
są 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.
źródło
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ń.
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.
źródło
Bez popsuwania obecnej architektury widzę dwa sposoby. Po pierwsze, możesz przechowywać wskaźnik do
Game
instancji wOptionsScreen
klasie. Po drugie, możeszGame
pobrać klasę bieżących ustawień w danym przedziale, powiedzmy co sekundę.Aby faktycznie dostosować się do nowych ustawień,
Game
klasa 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ń
OptionsScreen
może po prostu globalnie uruchomić zdarzenie przerysowane, któreGame
zarejestrował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_map
jako mapy mieszania istd::function
do 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żyjeszstd::string
jako 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.źródło
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 przypadkuOptionsScreen
tego zmodyfikuje niektóre wartości samej wartości ustawień. W przypadku tego poGameScreen
prostu 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
SettingsStore
klasa będzie miała listęnotifiables
. Są to klasy, które implementują określonyISettingChanged
interfejs. Interfejs byłby prosty, który zawiera następującą metodę: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ć zSettingsStore
.Teraz można to dodatkowo uzupełnić następującymi pomysłami:
SettingsStore
ciągami const, aby zapobiec wklejaniu ciągów.źródło
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.
źródło