Oddzielanie fizyki i logiki gry od kodu interfejsu użytkownika

12

Pracuję nad prostą grą logiczną opartą na blokach.

Rozgrywka polega głównie na przesuwaniu bloków w obszarze gry, więc jest to trywialna symulacja fizyki. Moje wdrożenie jest jednak moim zdaniem dalekie od ideału i zastanawiam się, czy możesz dać mi jakieś wskazówki, jak to zrobić lepiej.

Podzieliłem kod na dwa obszary: logikę gry i interfejs użytkownika, podobnie jak w przypadku wielu gier logicznych:

  • Logika gry odpowiada za ogólne zasady gry (np. Formalny system reguł w szachach)
  • Interfejs użytkownika wyświetla obszar gry i pionki (np. Szachownicę i pionki) i odpowiada za animacje (np. Animowany ruch szachów)

Logika gry reprezentuje stan gry jako logiczną siatkę, gdzie każda jednostka ma szerokość / wysokość jednej komórki na siatce. Tak więc dla siatki o szerokości 6 możesz przesunąć blok o szerokości 2 cztery razy, aż zderzy się z granicą.

Interfejs użytkownika pobiera tę siatkę i rysuje ją, konwertując rozmiary logiczne na rozmiary pikseli (to znaczy, pomnaża ją przez stałą). Ponieważ jednak gra nie ma prawie żadnej logiki, moja warstwa logiki gry [1] nie ma wiele do roboty poza wykrywaniem kolizji. Oto jak to działa:

  1. Gracz zaczyna przeciągać kawałek
  2. Interfejs użytkownika pyta logikę gry o legalny obszar ruchu tego elementu i pozwala graczowi przeciągnąć go w tym obszarze
  3. Gracz puszcza kawałek
  4. Interfejs użytkownika przyciąga element do siatki (aby znalazł się w prawidłowej pozycji logicznej)
  5. Interfejs użytkownika informuje logikę gry o nowej pozycji logicznej (metodami mutatora, których wolałbym unikać)

Nie jestem z tego zadowolony:

  • Piszę testy jednostkowe dla mojej warstwy logiki gry, ale nie dla interfejsu użytkownika, i okazało się, że cały trudny kod znajduje się w interfejsie: powstrzymywanie elementu przed kolizją z innymi lub granicą i przyciąganie go do siatki.
  • Nie podoba mi się fakt, że interfejs użytkownika mówi logice gry o nowym stanie, wolę, aby wywoływała movePieceLeft()metodę lub coś takiego, jak w moich innych grach, ale nie posunęłam się za daleko z tym podejściem, ponieważ logika gry nie wie nic na temat przeciągania i przyciągania, które są możliwe w interfejsie użytkownika.

Myślę, że najlepiej byłoby pozbyć się mojej warstwy logiki gry i zamiast tego wdrożyć warstwę fizyki. Mam kilka pytań na ten temat:

  1. Czy taka warstwa fizyki jest powszechna, czy może bardziej typowa jest warstwa logiki gry?
  2. Czy przyciąganie do siatki i kodu do przeciągania elementów należałoby do interfejsu użytkownika lub warstwy fizyki?
  3. Czy taka warstwa fizyki zazwyczaj działałaby z rozmiarami pikseli lub z jakąś jednostką logiczną, taką jak moja warstwa logiki gry?
  4. Widziałem wykrywanie kolizji oparte na zdarzeniach w bazie kodu gry raz, to znaczy, gracz po prostu przeciągał element, interfejs użytkownika renderowałby to posłusznie i powiadamiał system fizyki, a system fizyki wywoływałby metodę onCollision () na kawałku po wykryciu kolizji. Co jest bardziej powszechne? To podejście lub najpierw prośba o legalny obszar przemieszczania się?

[1] warstwa prawdopodobnie nie jest właściwym słowem na to, co mam na myśli, ale podsystem wydaje się przesadzony, a klasa wprowadza w błąd, ponieważ każda warstwa może składać się z kilku klas.


źródło
Czy moja odpowiedź pomogła, potrzebujesz dalszych informacji?
Will Marcouiller,
Proszę wyrazić głos i zaakceptować odpowiedź, jeśli pomogło to lub opowiedzieć o problemach związanych z wdrażaniem takiej architektury lub czegoś podobnego, ale informuj nas o swojej sytuacji, abyśmy mogli lepiej pomóc. =)
Czy Marcouiller

Odpowiedzi:

3

Spróbuję odpowiedzieć na to pytanie, ponieważ rozumiem, o co pytasz, chociaż nie mam dużego doświadczenia w tworzeniu gier, ponieważ wciąż się uczę.

Widzę, że należy oddzielić kod GUI od logiki gry i obiektów domeny, czyli elementów układanki. Są to rzeczywiście trzy osobne warstwy - i tak, layermoim zdaniem , jest to właściwe określenie. Jest często używany do wyjaśnienia pojęć rozdzielania poziomu każdego obiektu w systemie na niezależne od siebie podsystemy.

W odniesieniu do programowania obiektowego każdy obiekt powinien być klasą. Tak więc każdy element układanki powinien składać się z jednej klasy, a więc z planszy. Plansza powinna zawierać X elementów układanki w zależności od jej wielkości i zdolności ruchowej, którą chcesz dać graczowi.

Oto moje przemyślenia na ten temat - mam nadzieję, że to pomoże:

  1. Warstwa GUI: ta warstwa zawiera tylko metody wyświetlania elementów na planszy, aby umożliwić interakcję między samą grą a graczem;
  2. Warstwa kontrolera gier: odpowiada za wkłady gracza. To jest warstwa, która powinna kazać pionkowi poruszać się w różnych kierunkach i pytać planszę, czy będą kolizje podczas ruchu i tak dalej;
  3. Warstwa biznesowa: ta warstwa zawiera wszystkie klasy biznesowe, czyli elementy gry i obiekt planszy zawierający elementy układanki.

W tej architekturze warstwa GUI pokazywałaby graczowi stan gry, położenie poszczególnych elementów układanki. Warstwa GUI byłaby wówczas odpowiedzialna za uzyskiwanie danych wejściowych gracza i przekazywanie jej do podstawowej warstwy kontrolera gier, która byłaby wówczas odpowiedzialna za wykrywanie kolizji. Jeśli go nie ma, element można nakazać przesunąć w tym kierunku wejściowym. Aby to zrobić, wystarczy nazwać ten utwór MoveLeft,MoveRightitp. metody poruszania elementu. Równie dobrze możesz poinformować planszę, który element chcesz przenieść, a następnie sam zarządza ruchem elementu, a następnie element porusza się w żądanym kierunku. Ta architektura ułatwia testowanie każdego fragmentu kodu na różnych warstwach, a następnie umożliwia testowanie jednostkowe, testy integracji i testy funkcjonalne.

Wiem, że może to wydawać się nieco mylące z powodu widoku i dzięki Bogu, jeśli tak nie jest! Jeśli potrzebujesz dodatkowych informacji i pomocy, nie wahaj się zapytać, chętnie pomogę najlepiej jak potrafię, choć jestem początkującym w tworzeniu gier.

Dziękuje za przeczytanie! =)

Will Marcouiller
źródło
2
Który brzmi bardzo podobnie do kontrolera widoku modelu.
tenpn
Szczerze mówiąc, każdy opis interfejsu abstrakcji z podstawowej implementacji będzie brzmiał jak kontroler widoku modelu. Ale to dlatego, że stosowana terminologia i techniki są takie same (abstrahując od szczegółów implementacji i oddzielając interfejs użytkownika od implementacji). Ale zakłada bardzo proste środowisko. Jest tu wiele warstw, a każda warstwa zawiera kontrolery. Oddzielenie widoku od modelu nie wystarczy tutaj, musisz także oddzielić kontrolę interakcji użytkownika od kontroli gry.
MrCranky,
Zastanawiam się, co jest tutaj tak skomplikowane, ponieważ przenosimy elementy układanki na ograniczoną planszę. Kontrolki są pobierane z GUI, co jest dobrym sposobem, i przechodzą do warstwy abstrakcji, czyli kontrolera gier. Kontroler gry sprawdza kolizje podczas ruchu, a następnie informuje element, aby się poruszył, jeśli nie wykryto kolizji. Kawałek układanki następnie przesuwa się prawidłowo w żądanym kierunku i odsłania swoją nową pozycję dzięki Positionfunkcji pobierania właściwości, dzięki czemu kontroler gier wymaga od GUI wyświetlenia tego przesuniętego elementu w tej nowej pozycji.
Will Marcouiller,
heh, kilka miesięcy temu blogowałem o „MVC” w tworzeniu gier. Tak, jest to dobra koncepcja bramy dla osób niewtajemniczonych w tworzeniu gier: codecube.net/2010/08/xna-for-the-evoodday-developer
Joel Martinez
1
Przepraszam za spóźnione przyjęcie, ale niestety przez jakiś czas byłem odciągnięty od projektu. Bardzo podoba mi się podejście kontrolera, zastosuję je!