Jak zrobić grę bez OOP? [Zamknięte]

10

Obecnie studiuję tworzenie gier i ćwiczę tworzenie gier.

W swoich grach używam dużo OOP. Na przykład każdy wystrzelony pocisk jest instancją Missileobiektu i dodany do listy Missileobiektów. Każdy czołg w grze jest Tankprzedmiotem. Itp.

Na tej podstawie opiera się cała konstrukcja programu. Na przykład posiadanie listy Missileobiektów pozwala mi przesuwać pociski, rysować je itd., A posiadanie instancji Tankobiektu dla każdego czołgu pozwala mi sprawdzić, czy każdy czołg koliduje z czymś itp.

Trudno mi sobie wyobrazić, jak można zaprogramować grę (która jest bardziej złożona niż Pac-Man) w języku innym niż OO. (Oczywiście bez lekceważenia programistów spoza OO). Nie tylko pod względem tego, jak długo to potrwa, ale przede wszystkim pod względem tego, jak można zaprojektować grę w ten sposób.

Nie wyobrażam sobie projektowania gry bez użycia programowania obiektowego, ponieważ całe moje rozumienie, jak projektować grę, opiera się na OOP.

Chciałbym zapytać: czy są jakieś gry, które nie są programowane przy użyciu OOP, w sposób podobny do tego, co opisałem powyżej? Czy są jakieś „profesjonalne” gry, w których OOP nie jest głównym czynnikiem w procesie rozwoju?

Jeśli tak, czy mógłbyś mi dać wyobrażenie o tym, jak na przykład można wdrożyć wykrywanie kolizji między czołgiem a liczbą pocisków N bez OOP?

użytkownik3150201
źródło
6
Czy to pytanie filozoficzne? Nawet jeśli nie nazwiesz swoich czołgów „przedmiotami”, prawdopodobnie będziesz chciał „bytów”, „aktorów”, „agentów”, „struktur” lub po prostu innej nazwy tego samego pomysłu, który jest zbiorem atrybuty i zachowania, które składają się na obracającą się prostopadłościan z wieżą, która może strzelać do różnych przedmiotów, zwaną czołgiem. Języki programowania będą miały różne sposoby sformalizowania tego samego pomysłu, ale ostatecznie będzie to czołg.
Anko
Wiele gier korzysta z systemu opartego na składnikach, ponieważ ta odpowiedź opisuje: gamedev.stackexchange.com/a/31491/9366
John McDonald
Jest to zarówno niezwykle szerokie (co można ewentualnie rozwiązać poprzez zawężenie zakresu), ale też nie jest specyficzne dla rozwoju gier (ponieważ tworzenie oprogramowania bez technik OO nie jest czymś, co twórca gier dałby lepszą odpowiedzią niż jakikolwiek inny programista), co sprawia, że ​​tutaj jest to temat, obawiam się.
Może być odpowiedni dla StackOverflow, lub możesz sprawdzić w Centrum pomocy, aby znaleźć wybrane strony, które są specyficzne dla rozwoju gry (takie jak GDNet), które pozwoliłyby na tego rodzaju szeroki, dyskusyjny temat. Powodzenia!

Odpowiedzi:

16

Nie wyobrażam sobie projektowania gry bez użycia programowania obiektowego, ponieważ całe moje rozumienie, jak projektować grę, opiera się na OOP.

Wtedy prawdopodobnie dobrze będzie, jeśli spróbujesz napisać jakieś programy w stylu innym niż OO. Nawet jeśli odkryjesz, że nie jest to dla ciebie pragmatyczne, prawdopodobnie nauczysz się wiele po drodze, co pomoże ci w przyszłości.

Styl OO jest całkiem odpowiedni dla gier, ponieważ prawie zawsze chodzi o manipulowanie stanowymi obiektami. Wiązka laserowa uderza robota, a stan robota zmienia się, a jego tożsamość pozostaje taka sama.

Możliwe jest jednak programowanie gier w funkcjonalnym stylu. W stylu funkcjonalnym stan sam się nie zmienia. Obiekty są niezmienne. Zamiast zmieniać przedmioty, zadajesz pytanie, jak wyglądałby wszechświat, gdybym to zmienił? a następnie stworzyć cały nowy wszechświat, który ma zmienioną właściwość. Oczywiście możesz ponownie wykorzystać wiele z wcześniej istniejącego wszechświata, ponieważ jest on niezmienny .

W programowaniu funkcjonalnym każda funkcja musi obliczyć swoją wartość zwrotną wyłącznie na podstawie przekazanych informacji; nie ma czytania ze „stanu globalnego”.

Jeśli to zrobisz, podstawowym problemem, który będziesz musiał rozwiązać, jest to, że każda aktualizacja nie jest destrukcyjna . Kiedy laser uderza w robota, nie zmieniasz jego stanu. Ostatecznie obliczasz zupełnie nowy wszechświat identyczny ze starym wszechświatem, z tym wyjątkiem, że robot ma inny stan; jeśli potrzebujesz tego starego wszechświata, nadal tam jest, bez zmian.

Ta seria artykułów na blogu zawiera więcej przemyśleń na temat pisania gier w funkcjonalnym stylu:

http://prog21.dadgum.com/23.html

Ten artykuł dotyczy w szczególności pytania „pocisk uderza w czołg”:

http://prog21.dadgum.com/189.html

W rzeczywistości po prostu przeczytaj cały blog. Tam są dobre rzeczy, a artykuły są krótkie.

Eric Lippert
źródło
12

Każdy program zorientowany obiektowo można przekształcić w program proceduralny, zastępując wszystkie klasy strukturami i przekształcając wszystkie funkcje składowe w samodzielne funkcje, które przyjmują obiekt thisza argument.

Więc

 missile.setVelocity(100);

staje się

 setMissileVelocity(missile, 100);

lub gdy ta funkcja jest trywialna, wystarczy

 missile.velocity = 100;

Główną różnicą między programowaniem obiektowym a programowaniem proceduralnym jest sposób traktowania danych. W OOP dane są inteligentne . Sam sobie zarządza i manipuluje. Ale w programowaniu proceduralnym dane są głupie . Sam nic nie robi i należy nim manipulować z zewnątrz.

Nawet jeśli rozważasz struktury zbyt obiektowe, możesz zastąpić jedną tablicę struktur wieloma tablicami, jedną dla wszystkiego, co byłoby zmienną pocisku. Więc

struct Missile {
     int x;
     int y;
     int velocity;
}

Missile missiles[256];

staje się

int missileX[256];
int missileY[256];
int missileVelocities[256];

W tym projekcie funkcja, która wykonuje operację obejmującą wiele atrybutów tego samego pocisku, pobierałaby teraz indeks tablicy zamiast odwołania do struktury. Jego implementacja wyglądałaby następująco:

function updateMissilePosition(int index) {
     missileX[index] += missileVelocity[index];
}
Philipp
źródło
1
Ale missilejest instancją obiektu. W trybie innym niż OOP nie ma żadnych wystąpień, czy mam rację? Jeśli tak, to jak możesz ustawić setMissileVelocity (pocisk, 100)?
user3150201
1
@ user3150201 Nie masz całkowitej racji. Większość języków innych niż OOP (i argumentowałbym, że każdy jest odpowiedni do poważnego tworzenia gier) obsługuje struktury. Struktura jest trochę jak klasa, tyle że nie zawiera nic oprócz zmiennych publicznych. Więc byłoby to pozwalają stworzyć typ Missile, który jest konstrukcją z kilku dziedzin, jak x, y, anglei velocity.
Philipp
@ user3150201 Zaktualizowano odpowiedź z sekcją o tym, jak to zrobić bez struktur.
Philipp
Dobra odpowiedź Filip, chociaż nie rozumiem, dlaczego nie miałoby się programować zorientowanego obiektowo. Naprawdę trudno jest czytać języki inne niż OOP i może to być frustrujące. Kod może szybko zmienić się w bałagan.
Zhafur
2
@Zhafur Zdajesz sobie sprawę, że twoje oświadczenie jest masywną przynętą, prawda?
Philipp
6

Robię to w następujący sposób:

  • Wszystkie klasy OOP / metody mają dostęp do this. Aby wykorzystać thisw podejściu innym niż OO, wystarczy przekazać w dowolnym przypadku (patrz następny punkt) thisjako pierwszy parametr.
  • Teraz, tak jak w przypadku instancji, możesz przekazać structs do swoich funkcji jako this, ale uważam, że najlepszym sposobem na osiągnięcie dobrej wydajności pamięci podręcznej dla obiektów, które są płodne, takich jak byty lub cząstki, jest po prostu przekazanie jednego indeksu do kilku tablic prymitywów lub małe structs. Tak więc ten indeks jest używany dla każdego pojedynczego elementu danych oryginalnej klasy. Na przykład, jeśli miałbyś

...

class Entity //let's say you had 100 instances of this
{
   int a;
   char b;
   function foo() 
   {
      .../*can access 'this' herein*/
   }
}

Zastąpiłbyś to

int a[100];
char b[100];
function foo(int index);

Aby przekazać funkcję do indeksu, aby uzyskać to, co zwykle byłoby this.

Pamiętaj, że możesz użyć macierzy pierwotnych jak wyżej lub macierzy structs, w zależności od tego, jak najlepiej przeplatać dane w celu zapewnienia dobrej lokalizacji pamięci podręcznej (lokalizacji odniesienia). Oczywiście, przyzwoita wydajność pamięci podręcznej zależy od znacznie więcej - szczególnie od tego, na jakim języku / platformie piszesz swój kod - ale nawet w opartych na maszynach wirtualnych, dynamicznie przydzielanych językach, takich jak Java, duże tablice liniowe prymitywów mają tendencję do wyświetlać lepszą charakterystykę wydajności niż instancje obiektów. Głównym powodem jest to, że do obiektów można uzyskać dostęp przez odniesienie, co oznacza, że ​​przeskakujesz całą pamięć, aby uzyskać dostęp do danych - nieefektywny w porównaniu do dostępu do operacji podstawowych z dużej tablicy.

Aby uzyskać więcej informacji na temat budowania jednostek itp. Jako tablicy struktur lub prymitywów, zobacz Mick West's Evolve your Hierarchy .

Inżynier
źródło
0

Oprócz istniejących odpowiedzi możesz chcieć wiedzieć, jak robić polimorfizm w języku proceduralnym.

Istnieją dwa podejścia:

Przechowywanie typu

W tym przypadku struct ma pole dla identyfikatora typu, prawdopodobnie wyliczenie, które jest sprawdzane za pomocą switchinstrukcji, kiedy należy wykonać akcję specyficzną dla typu.

Drugi sposób to:

Przechowywanie wskaźników funkcji

Nie wspominałeś, w jakim języku programowania masz doświadczenie, ale w różnych językach są one również nazywane wywołaniami zwrotnymi, delegatami, zdarzeniami lub funkcjami wyższego rzędu lub po prostu obiektami funkcji.

W takim przypadku nie zapiszesz typu, ale zapiszesz wskaźnik / odwołanie do funkcji wykonującej określoną akcję. Gdy trzeba wykonać akcję specyficzną dla typu, wystarczy wywołać tę funkcję. Ten wygląda bardzo podobnie do zwykłych metod wirtualnych.

Umożliwiając niezależne ustawianie każdej funkcji, otrzymujesz wzorzec strategii wolny.


Odnośnie ostatniego akapitu z wykrywaniem kolizji. Myślę, że prawdopodobnie masz wiele rodzajów czołgów i masz wiele rodzajów pocisków, a każda kombinacja może potencjalnie mieć inny wynik, gdy się zderzą. Jeśli tego właśnie szukasz, masz problem, który nie został jeszcze rozwiązany przez nawet języki OOP: wiele metod wysyłania i wiele metod, które mogą mieć wiele thisparametrów różnych typów. W przypadku tego problemu istnieją jeszcze dwie alternatywy:

  • Zagnieżdżone przełączniki dla każdej kombinacji czołgu i pocisku.
  • Dwuwymiarowe tablice wysyłkowe, które zawierają wskaźniki funkcji dla każdej kombinacji czołgu i pocisku.
Calmarius
źródło