Co to jest „kod logiki gry?”

50

Używam C # / XNA i kilkakrotnie powiedziano mi, aby nie mieszać kodu aktualizacji z rysowanym kodem - i jestem pewien, że nie! Ale czy ktoś mógłby opisać, czym dokładnie jest „kod logiczny”?

Jak widać tutaj: http://blogs.msdn.com/b/shawnhar/archive/2007/07/25/understanding-gametime.aspx

[...] upewnij się, że umieściłeś całą logikę gry w metodzie aktualizacji (nie w Draw!), a wszystko będzie działało z niezłą stałą prędkością.

Pytam o to, ponieważ szybkość mojej gry zmienia się w stosunku do FPS. Wolne FPS to wolno poruszające się obiekty i odwrotnie. I tak, dołączam oczekiwany position += speed * (float)gt.ElapsedGameTime.TotalSeconds;kod.

To jest prawdopodobnie duże pytanie dla początkujących, ale po prostu chcę mieć absolutną jasność co do definicji tego.

Nieśmiały facet
źródło
Myślę, że miałeś na myśli position = speed * ...TotalSeconds. Zauważ, że to =nie jest +=. Gdyby było +=tak, jak piszesz, twoja pozycja spadłaby z ekranu niemal natychmiast.
DrZ214,
Mam kod, który wygląda jak „pozycja + = kierunek * prędkość * ... TotalSeconds” i działa bardzo dobrze. Mogłem coś pomylić, ale pozycja = prędkość przypisywałaby to każdej aktualizacji. Twój sposób może działać, ale mój kod działa w ten sposób. (Uwaga: kierunek jest znormalizowany)
Nieśmiały facet
Myślałem, że gt.ElapsedGameTime.TotalSecondsupłynęła liczba sekund, które upłynęły od uruchomienia programu (gry). Jeśli pomnożysz przez to prędkość, to po 5 sekundach gry prędkość będzie 5 razy większa (z wyjątkiem specjalnego przypadku, w którym prędkość jest ustawiona na 0). Nie jestem pewien, co jeszcze mogłoby to spowodować, że to nieprawda, ale jestem zaintrygowany.
DrZ214,
1
Aha, to od ostatniej aktualizacji. gamedev.stackexchange.com/questions/67968/…
Shyy Guy
2
Fascynujące, nigdy bym tego nie zgadł. Nigdy nie potrzebowałem takiej rzeczy w kilka sekund, ponieważ osobiście używam własnej zmiennej o nazwie, iiiże ręcznie zwiększam każdą aktualizację, ponieważ nie chcę tego w kilka sekund, chcę kroki lub ramki. Widzę jednak, że twój sposób jest prawidłowym sposobem kodowania miękkiego.
DrZ214,

Odpowiedzi:

102

Czy to zmienia stan twojego świata gry? To kod logiczny.

Czy pokazuje stan świata gry? To renderuje kod.

Nils Ole Timm
źródło
Przejrzę mój kod, aby być w 100% pewnym. Dobre wyjaśnienie, dzięki.
Nieśmiały facet,
38
Czy obsługuje dane wejściowe? Jego kod kontrolera. Oddziel kontrolę od logiki, abyś mógł używać tej samej logiki z różnymi typami kontroli. Np. FPSInputController + CharacterMotor vs. AIInputController + CharacterMotor vs NetworkInputController (np. Inni gracze w instancji wieloosobowej) + CharacterMotor. Silnik (kod logiczny) nie dba o to, w jaki sposób otrzymuje instrukcje, tylko że są one dostarczane (ten rodzaj oddzielenia pozwala na łatwe przejście z odtwarzacza do bota AI (pomyśl Left 4 Dead i bezczynności graczy) iz powrotem, między innymi).
Draco18s,
10
Draco porusza dobrą rację. Istnieje wiele różnych podziałów, które można wprowadzić do kodu. Podział Nilsa jest tym, co uważam za archetypowy podział na logikę i rendering, ale istnieje wiele innych warstw, które zobaczysz. Prawdziwym powodem rozdzielenia ich na warstwy jest to, że każda warstwa składa się z podobnych działań, które wymagają podobnego stylu kodu o podobnych wymaganiach. Na przykład w wielu grach można uniknąć logiki gry, która zajmuje 2 lub 3 klatki, ale jeśli nie wykonasz renderowania każdej klatki, oko użytkownika szybko to zauważy.
Cort Ammon
Ich rozróżnienie pomoże ci zarządzać wymaganiami, gdy zaczną się one stresować.
Cort Ammon
6
I pamiętaj, że jeśli rzucisz na nią zbyt dużo MVC, może to spowolnić do pełzania, więc zawsze staraj się zachować równowagę między konserwacją a optymalizacją. Ale nie optymalizuj zbyt wcześnie, chyba że masz absolutną pewność co robisz. I pamiętaj, że zawsze możesz refaktoryzować. I ... Uh, stwórz wiele gier i ucz się na własnych błędach.
Maurycy
24

Twoja separacja jest prawidłowa, jeśli:

  • Wielokrotne wywoływanie Draw () bez przerywanych wywołań Update () nigdy nie spowodowałoby widocznych zmian między wywołaniami.
  • Wielokrotne wywoływanie aktualizacji () bez przerywanego wywołania Draw () byłoby jak gra z wyłączonym ekranem: wszystko przebiega idealnie i konsekwentnie, po prostu nie widać.
Tomasz
źródło
5
Prawidłowo napisane Draw()mogą rysować różne obrazy w miarę upływu czasu. Na przykład ramki animowanych duszków mogą się zmieniać. Ponadto obiekty mogą nadal poruszać się wizualnie do przodu, jeśli kod renderujący używa wspólnej sztuczki i zwiększa velocity * time since last update / period of updatewidoczną pozycję obiektów (podczas gdy ich rzeczywista pozycja pozostaje niezmieniona).
HolyBlackCat
2
iffczyli jeśli i tylko jeśli?
BalinKingOfMoria
3
@HolyBlackCat: to dyskusyjne. Co jeśli chcę zatrzymać grafikę? Co jeśli ramki animacji wpływają na grę (animacje ataku odpowiadające hitboxom itp.)? Interpolacja wizualna nadal sugeruje, że „czas” (jak w delcie gry) mija, ale jeśli czas mija, a ty nie grasz, Updateco jeszcze nie jest zsynchronizowane? Brak danych wejściowych odtwarzacza, zdarzenia sieciowe nie są przetwarzane itp.? Gra powinna być wyparta z jednego zegara, ze stałymi „tyknięciami” dla logiki gry lub fizyki pochodzącej z tego zegara, a stan grafiki pochodnej również sterowany przez ten sam zegar.
Sean Middleditch
@SeanMiddleditch Zgadzam się, prawie zawsze możesz pisać Draw()w taki sposób, aby zawsze rysował ten sam obraz, gdy wywoływany kilka razy z rzędu. Należy to zrobić, jeśli to możliwe. Ale zdarzają się przypadki, gdy nie wiesz, z jaką częstotliwością Draw()będzie się nazywać. Na przykład, jeśli chcesz mieć pełne wsparcie (rzeczywiste 120 FPS) dla nowych monitorów 120Hz i włączysz vsync. What if I want to pause graphics?Następnie podajesz 0 zamiast faktycznego czasu delta do Draw().
HolyBlackCat
2
@HolyBlackCat: Nic, o co mi chodziło, nie wykluczałoby użycia renderowania 120 Hz. Absolutnie nie zalecałem stałej stawki; to tylko amatorskie. Powinien istnieć jeden globalny zegar gry, którego delta jest mierzona w kategoriach ramek renderujących, które zasilają wartość akumulacji dla logiki gry o stałej szybkości. Ten globalny zegar steruje grafiką, w tym interpolacją. Możesz wstrzymać grafikę, ustawiając skalę zegara na 0. Możesz mieć zegary hierarchiczne, więc np. Interfejs użytkownika nadal działa i animuje, a interpolacja znaków również zatrzymuje się bardzo, bardzo łatwo.
Sean Middleditch
7

Chodzi tutaj o rozdzielenie rzeczy, które nie są modelem.

Logika gry to model, o którym mowa w

Są to wszystkie różne, powiązane wzorce architektury oprogramowania. Ale w każdym przypadku Model jest tym samym, jest prawdziwą logiką i rzeczywistym stanem.

Podczas tworzenia oprogramowania biznesowego nazywa się go czasem logiką biznesową i koduje niektóre zasady biznesowe. Na przykład, jeśli kodujesz coś dla banku, aby obliczyć rachunki z karty kredytowej, to funkcja powodująca, że ​​ktoś nie musi płacić odsetek, jeśli spłaci dług w ciągu mniej niż 30 dni, jest częścią logiki biznesowej, żyje w Model. Nie działa na przykład na jednej z wyświetlanych warstw. Kod do drukowania rachunku nie służy na przykład do edycji tekstu na podstawie ich działań. W tym przykładzie być może podkreślono, dlaczego warto zorganizować swój kod w ten sposób.

Podobnie jest z logiką gry.

Wyobraź sobie, że w pewnym momencie twoja gra została przeniesiona na inną konsolę. Może pomóc wyobrazić sobie coś naprawdę innego niż twój obecny cel. Np. Jeśli celujesz w coś za pomocą gamepada / kontrolera, wyobraź sobie, że gra jest przeniesiona na tablet z ekranem dotykowym. Logika gry jest częścią kodu, która nie zmienia się po przeniesieniu.

Jeśli twoja gra była czymś w rodzaju strategii wojskowej, wyobraź sobie, że jest przekształcana w najbardziej złożoną grę planszową na świecie. Logika gry to sekcje kodu, które bezpośrednio odpowiadają wierszom w zbiorze reguł. (Nie wszystkie wiersze w zbiorze reguł, nie te dotyczące ruchomych elementów, ale niektóre.).

Logika gry nigdy się nie zmienia, bez względu na formę.

Lyndon White
źródło