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.


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! =)


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!
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.