Jak zaprojektować silnik gry w języku obiektowym? [Zamknięte]

26

Ilekroć próbuję napisać grę w jakimkolwiek języku zorientowanym obiektowo, pierwszym problemem, z którym zawsze się spotykam (po zastanowieniu się, jaką grę napisać) jest zaprojektowanie silnika. Nawet jeśli używam istniejących bibliotek lub frameworków, takich jak SDL, wciąż muszę podejmować pewne decyzje dla każdej gry, np. Czy używać maszyny stanów do zarządzania menu, jakiej klasy używać do ładowania zasobów itp.

Co to jest dobry projekt i jak mógłby zostać wdrożony? Jakie są kompromisy, jakie należy podjąć, i ich zalety / wady?

silnik zewnętrzny
źródło
12
Co jest złego w podążaniu za impulsem i stamtąd refaktoryzacji zamiast cierpieniu z powodu paraliżu analizy?
Kaczka komunistyczna
7
@TheCommunistDuck Ponieważ impuls jest podejściem, które podjąłem we wszystkich moich poprzednich projektach - i każdy uderza w ścianę po kilku miesiącach, kiedy stwierdzam, że każda nowa funkcja wymaga monumentalnego wysiłku i złożoności, aby dodać. Obecnie spędzam więcej czasu na przepisywaniu silników niż na pisaniu samej gry, więc mam nadzieję, że dzięki odrobinie przemyślenia i planowania zaoszczędzę sobie czasu na dłuższą metę.
silnik pozasilnikowy
3
@chuzzum, dobra uwaga. Jedną rzeczą, którą poleciłbym wtedy, jest sprawdzenie architektury silnika C4, to znaczy; terathon.com/c4engine/images/architecture.png To może być o wiele wyższy poziom niż potrzebujesz, ale może dać ci kilka pomysłów ;-)
Kaczka komunistyczna
1
i.imgur.com/81zIY.png
Kaczka komunistyczna
3
Również to pytanie jest niejasne. Być może weź jeden ze swoich przykładów i zadaj to głębsze pytanie.
Tetrad

Odpowiedzi:

24

Wątpię, czy ktoś będzie w stanie powiedzieć: „Musisz to zrobić, to i tamto, i te automaty z tym wzorem X”.

Jednak przydatne zasoby:
Enginuity - seria artykułów na temat budowy silników na Gamedev.net.
Game Coding Complete - Jestem właścicielem tej książki, która dobrze opisuje każdy (dobrze, prawie) aspekt programowania gier. Ma również silnik zbudowany w całej książce.
Architektura silnika gry - to kolejna świetna książka do projektowania silników.
Układ silnika C4 - wzięty z mojego komentarza, ale pokazuje ogólny sposób montażu każdej części silnika razem.

Może to być trochę za dużo na to, czego potrzebujesz, ale nie możesz o czymś wiedzieć zbyt wiele i jestem pewien, że dostaniesz od nich dobry plan.

EDYCJA: Zapomniałem, że artykuły Gamedev zostały zarchiwizowane od czasu nowej strony, naprawiono :)

Kaczka komunistyczna
źródło
Link Enginuity został zerwany, a przeglądanie artykułów zdawało się wskazywać, że nie ma ich już w sieci. (?) Książki wyglądają jednak na dobre zasoby i zawsze szukam więcej książek o programowaniu;)
silnik z przekładnią zewnętrzną
Ponadto, jeśli chodzi o twój pierwszy komentarz, nie oczekuję, że ktokolwiek wymyśli ogólny plan, który pasuje do każdej gry. Właśnie zauważyłem, w trakcie opracowywania kilku gier, że często pojawiają się wspólne wzorce, więc zastanawiałem się, co inni ludzie używają w swoich grach.
silnik pozasilnikowy
1
Naprawiono link.
Kaczka komunistyczna
+1 za ciągłość. @chuzzum Po prostu sprawdź kilka silników gier, pozwól im się zainspirować i opracuj dla siebie optymalną architekturę. Plus: Często lepiej jest, aby komponent silnika gry był oparty na hierarchicznym, patrz cowboyprogramming.com/2007/01/05/evolve-your-heirachy
Dave O.
1
Nie powiedziałbym, że to silnik, który wymaga agregacji, a bardziej część frameworku encji.
Kaczka komunistyczna
7

Jako przykład, oto jak wygląda mój obecny projekt roguelike (w Javie). Korzysta z silnika grafiki 2D, więc dużo kodu renderującego już się mną zajęto. Krytyka jest mile widziana.

class Game
Ta klasa konfiguruje maszynę stanu zarządzającą bieżącym stanem gry. (w menu vs. rozpoczęcie nowej gry vs. gra zapisana)

interface State
Każda klasa stanu zawiera dwie pętle: pętlę do aktualizacji logiki i pętlę do renderowania. Zawierają również kod do wywoływania Gameklasy i żądania zmiany na inny stan.

class ResourceManager
Singleton zainicjowany przez Gameklasę, która ładuje wszystkie potrzebne zasoby i umożliwia dostęp do nich. Nie podoba mi się ten projekt, ponieważ utrudnia na przykład ładowanie / zwalnianie zasobów na różnych poziomach. Prawdopodobnie zaprojektowałbym to inaczej, gdybym zaczynał od nowa.

class Map
Mapa zawiera tablicę kafelków oraz listę wszystkich stworzeń i przedmiotów na mapie. To dość podstawowa klasa.

class Creature
Stworzenia zawierają informacje o sobie, w tym obliczenia ruchu (wymagające od nich, aby wiedziały, w której Mapie się znajdują, i aby móc je zapytać, aby dowiedzieć się o przeszkodach). Walczę z decyzją, czy to zrobić, czy też zająć się tym jakaś klasa menedżerska dla wszystkich stworzeń.

interface AITask
Stworzenia mogą mieć listę AITasks, które są wykonywane przy każdym uruchomieniu pętli logicznej stworzenia. AITask ma własną pętlę logiczną, która wydaje komendy stworowi, oraz warunek zakończenia, który określa, czy zadanie zostało wykonane pomyślnie, czy nie.

interface UIElement
Wdrożyłem własny interfejs użytkownika dla tego silnika. Każde UIElement ma pętlę renderowania i pętlę logiczną. Mają także pętlę do przetwarzania danych z klawiatury / myszy. Wszystkie elementy mogą mieć wiele elementów potomnych, które są renderowane po rodzicach i przejmują dane z klawiatury / myszy. Pozwala to na przykład mieć menu z podmenu.

silnik zewnętrzny
źródło
Co dokładnie się z tym dzieje? Wydaje mi się to zupełnie w porządku.
Kaczka komunistyczna
@TheCommunistDuck Tak naprawdę nie pojawia się w wybranych przeze mnie przykładach, ale mam wiele problemów z tym kodem. Klasa ResourceManager jest jedną z nich, ale mam też kłopoty ze stanami - kończę na ich ogromnym mnożeniu i kopiuję dużo kodu. Zwłaszcza w grze RPG, w której gracz ma jednocześnie wiele możliwości wyboru, możesz uzyskać naprawdę złożone wykresy stanów. Przykład: rzucanie zaklęcia. Przechodzi z NormalState -> SelectSpellState -> SelectTargetState -> InvalidTargetState (jeśli nie powiodło się) -> DoSpellAnimationState -> NormalState. A to tylko jedna akcja.
silnik pozasilnikowy
1
Nie? Nie. NO . NIE. Proszę nie. Och, czekaj, powiedziałeś, że ci się nie podoba.
Bartek Banachewicz,
6

Pierwszą ważną kwestią, na którą należy zwrócić uwagę, jest to, że nie ma jednej „dobrej” odpowiedzi na to pytanie.

Prawidłową odpowiedzią byłoby coś takiego: To bardzo zależy od rodzaju gry, platformy docelowej, ograniczeń (czasu) itp.

To powiedziawszy, istnieje kilka naprawdę dobrych artykułów, które pokażą, jak inni ludzie próbowali rozwiązać ten problem (ponieważ próbowałem znaleźć informacje na ten temat w przeszłości).
Jak wspomniano w komunistycznej kaczce, artykuł na temat twórczości Game Dev pomógł mi zrozumieć niektóre elementy architektury gry.

Mój obecny projekt to hybryda Quake3 / Doom3 i trochę biblioteki klas .NET :)

Mam dwie biblioteki (statyczne lub dynamiczne zależą od tego, jak chcesz zbudować / dostarczyć) Frameworki Library.

Biblioteka zawiera wszystkie klasy pomocników, które są pomocne w produkcji oprogramowania do gier, ale nie są ograniczone do tego rodzaju produktu. tzn. ma implementację połączonej listy, która jest zoptymalizowana pod kątem kodu gry, ale może być używana przez wszystko, co wymaga obsługi połączonej listy.

Framework jest wnętrznością „silnika”, jeśli chcesz to tak nazwać. Wiele z nich wynika z filozofii projektowania Quake3 (tylko w sposób bardziej obiektowy). Zawiera CLI , zarządzanie taktowaniem, kod specyficzny dla systemu operacyjnego i ewentualnie warstwy sieciowe itp.

Te dwa elementy są następnie łączone z produkowaną aplikacją. GameJeśli chcesz, który zawiera kod gra konkretnego. W podobny sposób Quake3 ładuje biblioteki DLL w zależności od tego, który „mod” jest odtwarzany.

Aby dać ci wyobrażenie o strukturze, tutaj jest szybki podział folderów i zawartości dla każdej biblioteki:


  • Struktura
    • IO (klasy zarządzania plikami specjalistycznymi, klasy drukowania tekstu (np. Do CLI), logowanie itp.)
    • Sieć
      • Klient (klasy reprezentujące to, co Framework uważa za „osobę grającą / połączoną z grą”)
      • Serwer (klasy do zarządzania połączeniami w ramach i zarządzania odtwarzaczem (graczami))
    • Platforma (klasy obsługi klawiatury / myszy / kontrolerów, procedury specyficzne dla systemu operacyjnego, takie jak getTime ())
    • System (klasy bardzo niskiego poziomu, takie jak klasa błędów ułatwiająca drukowanie komunikatów o błędach, klasy czasu i sam interfejs CLI).
    • Renderer (nie wymaga wyjaśnień)
    • itp.

  • Biblioteka
    • Kolekcje (klasy reprezentujące kolekcje danych, powiązane listy / tabele skrótów itp.)
    • Matematyka (podstawowe klasy pomocnicze matematyki, takie jak wektory i macierze)
    • itp.

HTH! Powinien dać ci kilka wskazówek ...

Adam Naylor
źródło
Alternatywny link do serii
Enginuity
-3

Rzeczy do rozważenia

  • Języki zorientowane obiektowo mają problemy, ponieważ zwykle nie mają funkcji pierwszej klasy lub nie wszystkie dane są obiektowe (takie jak liczba całkowita lub liczba zmiennoprzecinkowa w Javie). Wzory projektowe rozwiązują te problemy za pomocą kilku wzorów. Zasadniczo kodowanie jest szybsze i łatwiejsze w użyciu język, który potrafi to zrobić (obiekty pierwszej klasy); na przykład Python (który pozwala również na projektowanie obiektowe), będziesz miał wolniejszą prędkość.
  • Rachunek zdarzeń, przynajmniej do AI
  • Hoare logika, użyj warunków wstępnych i dodatkowych, aby przynajmniej przetestować swój kod
  • Agenci, spójrz na podmioty Quake
  • Relacyjne bazy danych, potężny sposób przechowywania danych

Dobry projekt

  • zrobić diagram ER
  • popraw to
  • utwórz z niej bazę danych, obiekty lub struktury danych

Dane są kluczem do programowania. Jeśli dobrze projektujesz swoje dane, zwykle wyłania się z nich algorytm (jeśli nie liczysz niektórych algorytmów numerycznych, takich jak wyznacznik obliczeniowy).

użytkownik712092
źródło
-1, ponieważ ta odpowiedź jest bardzo niejasna i myląca. Relacyjne bazy danych nie mają absolutnie nic wspólnego z silnikiem OO. Rozumiem, że angielski nie jest twoim pierwszym językiem, ale czy mógłbyś wyjaśnić, co masz na myśli w pierwszym akapicie? Wydaje się to sprzeczne (języki OO mają problemy, ale łatwiej jest programować w językach z wzorami projektowymi ... chociaż wzorce projektowe prawie zawsze są strukturami OO).
Kaczka komunistyczna
@duck Contradictory? OO mają problemy, które nie istnieją w innych językach, DP je rozwiązuje, patrz c2.com/cgi/wiki?DesignPatternsInDynamicProgramming .
user712092,
@ Kaczka 1) Możesz używać SQL w C ++ 2) Możesz wywnioskować atrybuty obiektów z ER (chociaż nie jest to zalecana praktyka) 3) Możesz strukturyzować dane z relacji (jest to lista, ponieważ muszę zmienić kolejność elementów według woli to jest skrót, ponieważ potrzebuję szybkiego wyszukiwania)
user712092,
@Duck przepraszam, popełniłem błąd, zmieniając kolejność. Nie chciałem twierdzić, że „DP są łatwiejsze do XY”, ale „językami, które potrafią robić pierwszą klasę są ...”. :)
user712092
Tak, funkcjonalne języki mogą mieć zalety. Jednak nawet bezstronnie uważam, że podejście OO ma logiczny sens z perspektywy gamedev. Ponadto nie ma powodu, dla którego potrzebujesz relacyjnej bazy danych w dowolnym miejscu. Jasne, mogą się przydać. Nigdzie jednak nie staje się niezbędnym komponentem.
Kaczka komunistyczna