Jak gra może obsłużyć wszystkie postacie jednocześnie?


31

To pytanie ma tylko na celu zdobycie wiedzy o tym, jak gra może poradzić sobie z tak wieloma postaciami na raz. Jestem nowy w grach, dlatego z góry proszę o wybaczenie.

Przykład

Tworzę grę obrony wieży, w której jest 15 miejsc na wieże, w których buduje się wieże, a każda z nich wyrzuca pocisk w określonym tempie; powiedzmy, że co sekundę każda z wież tworzy 2 pociski, a na polu bitwy maszerują wrogowie, powiedzmy 70 (każdy z 10 typami atrybutów, takimi jak HP, mana itp.), które będą się zmieniać w miarę przemieszczania się po pole bitwy).

Podsumowanie

Liczba wież = 15
pocisków utworzonych przez każdą wieżę na sekundę = 2
Całkowita liczba pocisków wytworzonych na sekundę = 30
jednostek w polu bitwy = 70

Czy gra radzi sobie z tymi 30 pociskami i 70 jednostkami , obsługując je na 100 różnych wątkach (co jest zbyt duże na PC) lub 1 wątku, który porusza nimi wszystkimi, zmniejsza ich wartość itp. (Co będzie rodzajem powolności , Myślę)?

Nie mam pojęcia na ten temat, więc czy ktoś może poprowadzić mnie przez to, jak to się ułoży?


Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
MichaelHouse

Dodając do innych odpowiedzi ... przykład niektórych masywnych gier. Skyrim miał większość aktualizacji logiki gry w jednym wątku. Sposób, w jaki radzi sobie z tym tak dobrze, polega na tym, że odległe postacie niezależne (postacie oddalone o milę) są przybliżane zgodnie z ich harmonogramem. Większość MMO aktualizuje logikę gry w jednym wątku, ALE każda część mapy istnieje na innym wątku lub stojaku na serwerze.
bimber

Odpowiedzi:


77

Jak gra radzi sobie z tymi 30 pociskami i 70 jednostkami, posługując się nimi na 100 różnych wątkach

Nie, nigdy tego nie rób. Nigdy nie twórz nowego wątku dla zasobu, to nie skaluje się w sieci, ani w aktualizowaniu jednostek. (Czy ktoś pamięta czasy, kiedy miałeś jeden wątek do odczytu na gniazdo w Javie?)

1 wątek, który przesuwa je wszystkie, zmniejsza ich wartość itp.?

Tak, na początek, taka jest droga. „Duże silniki” dzielą część pracy na wątki, ale nie jest to potrzebne do rozpoczęcia prostej gry, takiej jak gra w obronę wieży. Prawdopodobnie jest jeszcze więcej pracy do wykonania każdego tyknięcia, które również wykonasz w tym jednym wątku. O tak i oczywiście rendering.

(myślę, że będzie to trochę powolne)

Cóż ... Jaka jest twoja definicja wolnego ? Dla 100 jednostek nie powinno to zająć więcej niż pół milisekundy, prawdopodobnie nawet mniej, w zależności od jakości kodu i języka, z którym pracujesz. I nawet jeśli zajmuje to dwie pełne milisekundy, nadal wystarczy, aby trafić do 60 tps (tyknięć na sekundę, nie mówiąc o klatkach w tym przypadku).


34
Bardziej niż pół mikrosekundy, chyba że robisz coś dziwnego. Ale co najważniejsze, podział pracy na wiele wątków pogorszy wszystko , a nie poprawi. Nie wspominając o tym, że wielowątkowość jest niezwykle trudna.
Luaan

13
+1 Większość współczesnych silników gier renderuje tysiące, a nawet dziesiątki tysięcy wielokątów w czasie rzeczywistym, co jest znacznie bardziej intensywne niż śledzenie ruchu zaledwie 100 obiektów w pamięci.
phyrfox 15.04.2016

1
„Prosta gra, taka jak gra w obronę wieży”. Hmm ... czy kiedykolwiek grałeś w Defense Grid: The Awakening lub w jego kontynuację?
Mason Wheeler,

4
„Nigdy nie tworzyć nowego wątku za zasobu, to nie skaluje w sieci ...” hm niektóre bardzo skalowalnych architektur zrobić dokładnie to!
NPSF3000 16.04.16

2
@BarafuAlbino: To dziwna rzecz do powiedzenia. Istnieje wiele ważnych powodów, aby utworzyć więcej wątków niż dostępnych rdzeni. Jest to kompromis złożoności / wydajności / itp., Jak każda inna decyzja projektowa.
Dietrich Epp

39

Zasada numeryczna wielowątkowości to: Nie używaj jej, chyba że musisz równolegle pracować na wielu rdzeniach procesora w celu uzyskania wydajności lub szybkości reakcji. Wymaganie „x i y powinny zdarzyć się jednocześnie z punktu widzenia użytkownika” nie jest jeszcze wystarczającym powodem do korzystania z wielowątkowości.

Czemu?

Wielowątkowość jest trudna. Nie masz kontroli nad tym, kiedy każdy wątek jest wykonywany, co może skutkować wszelkiego rodzaju niemożliwymi do odtworzenia problemami („warunki wyścigu”). Istnieją metody, aby tego uniknąć (blokady synchronizacji, sekcje krytyczne), ale wiążą się one z własnym zestawem problemów („zakleszczenia”).

Zwykle gry, które radzą sobie z tak małą liczbą obiektów, jak zaledwie kilkaset (tak, nie jest to tak dużo w rozwoju gier), zazwyczaj przetwarzają je w sposób szeregowy, każdy logiczny tik za pomocą wspólnej forpętli.

Nawet stosunkowo słabsze procesory smartfonów mogą wykonywać miliardy instrukcji na sekundę. Oznacza to, że nawet gdy logika aktualizacji twoich obiektów jest złożona i zajmuje około 1000 instrukcji na obiekt i tyknięcie, a ty dążysz do hojnego 100 tyknięć na sekundę, masz wystarczającą moc procesora dla dziesiątek tysięcy obiektów. Tak, jest to bardzo uproszczone obliczenie z tyłu koperty, ale daje pomysł.

Powszechną mądrością przy tworzeniu gier jest to, że logika gry bardzo rzadko stanowi wąskie gardło gry. Najważniejszą częścią wydajności jest prawie zawsze grafika. Tak, nawet w grach 2d.


1
„Zasada numeryczna wielowątkowości jest następująca: nie używaj jej, chyba że musisz równolegle pracować na wielu rdzeniach procesora, aby uzyskać wydajność lub czas reakcji”. Może to być prawda w przypadku tworzenia gier (ale nawet w to wątpię). Podczas pracy z systemami czasu rzeczywistego głównym powodem dodawania wątków jest dotrzymywanie terminów i logiczna prostota.
Sam

6
@Sam Terminy dotrzymywania terminów w systemach czasu rzeczywistego to przypadek, w którym potrzebujesz wielowątkowości, aby móc reagować. Ale nawet tam logiczna prostota, do której pozornie dochodzisz poprzez wątki, jest często zdradliwa, ponieważ powoduje ukrytą złożoność w postaci impasu, warunków wyścigu i głodu zasobów.
Philipp

Niestety wiele razy widziałem logikę gry w całej grze, jeśli występują problemy z wyszukiwaniem ścieżek.
Loren Pechtel

1
@LorenPechtel Też to widziałem. Ale zwykle można było to rozwiązać, nie wykonując niepotrzebnych obliczeń ścieżek (takich jak ponowne obliczenie każdej ścieżki na każdym tik), buforując często ścieżki, buforując wielopoziomowe wyszukiwanie ścieżek i stosując bardziej odpowiednie algorytmy wyszukiwania ścieżek. Jest to coś, w czym wykwalifikowany programista zwykle może znaleźć duży potencjał optymalizacji.
Philipp

1
@LorenPechtel Na przykład w grze typu tower defense możesz wykorzystać fakt, że zazwyczaj jest tylko kilka punktów docelowych. Możesz więc uruchomić algorytm Dijkstry dla każdego miejsca docelowego, aby obliczyć mapę kierunkową, która poprowadzi wszystkie jednostki. Nawet w dynamicznym środowisku, w którym trzeba ponownie obliczać te mapy co klatkę, powinno to być nadal dostępne.
CodesInChaos 18.04.16

26

Inne odpowiedzi dotyczyły wątków i mocy współczesnych komputerów. Aby odpowiedzieć na większe pytanie, starasz się tutaj unikać sytuacji „n-kwadrat”.

Na przykład, jeśli masz 1000 pocisków i 1000 wrogów, naiwnym rozwiązaniem jest sprawdzenie ich wszystkich względem siebie.

Oznacza to, że otrzymujesz p * e = 1.000 * 1.000 = 1.000.000 różnych czeków! To jest O (n ^ 2).

Z drugiej strony, jeśli lepiej uporządkujesz swoje dane, możesz tego uniknąć.

Na przykład, jeśli wymieniasz na każdym kwadracie siatki, którzy wrogowie są na tym kwadracie, możesz przerzucić 1000 pocisków i po prostu sprawdzić kwadrat na siatce. Teraz musisz tylko sprawdzić każdy pocisk względem kwadratu, to jest O (n). Zamiast miliona czeków na każdą klatkę potrzebujesz tylko tysiąc.

Myślenie o uporządkowaniu danych i ich wydajnym przetwarzaniu ze względu na tę organizację jest największą pojedynczą optymalizacją, jaką możesz kiedykolwiek zrobić.


1
Alternatywnie do przechowywania całej siatki w pamięci tylko w celu śledzenia kilku elementów, możesz również użyć drzewek b, po jednym dla każdej osi, aby szybko wyszukać potencjalnych kandydatów na kolizje itp. Niektóre silniki nawet to robią dla Ciebie „automatycznie „; określasz obszary trafienia i prosisz o listę kolizji, a biblioteka ci ją podaje. Jest to jeden z wielu powodów, dla których programiści powinni używać silnika zamiast pisać od zera (oczywiście jeśli to możliwe).
phyrfox 15.04.16

@ phyrfox Z pewnością istnieje wiele różnych sposobów, aby to zrobić - w zależności od przypadku użycia, który jest lepszy, będzie się znacznie różnić.
Tim B

17

Nie twórz wątków dla zasobu / obiektu, ale dla sekcji logiki programu. Na przykład:

  1. Wątek do aktualizacji jednostek i pocisków - wątek logiczny
  2. Wątek do renderowania ekranu - wątek GUI
  3. Wątek dla sieci (np. Multiplayer) - Wątek we / wy

Zaletą tego jest to, że GUI (np. Przyciski) niekoniecznie utknie, jeśli logika jest wolna. Użytkownik może nadal wstrzymywać i zapisywać grę. Jest również dobry do przygotowania gry do gry wieloosobowej, gdy oddzielisz grafikę od logiki.


1
Dla początkujących nie polecam używania osobnych wątków graficznych i logicznych, ponieważ jeśli nie skopiujesz wymaganych danych, renderowanie stanu gry wymaga dostępu do odczytu stanu gry, więc nie możesz modyfikować stanu gry podczas rysowania.
CodesInChaos 18.04.16

1
Niezbyt częste rysowanie (np. Więcej niż 50 razy na sekundę) jest dość ważne i to pytanie dotyczyło wydajności. Program dzielenia jest najprostszą rzeczą do osiągnięcia prawdziwej korzyści w zakresie wydajności. To prawda, że ​​wymaga to pewnej wiedzy na temat wątków, ale warto ją zdobyć.
Tomáš Zato - Przywróć Monikę

Podział programu na wiele wątków jest dla programisty najtrudniejszą rzeczą. Większość naprawdę irytujące błędy wynikają z wielowątkowości i jest to olbrzymia ilość kłopotów i większość czasu po prostu nie warto - Pierwsza zasada: Sprawdź, czy masz problem z wydajnością, WTEDY zoptymalizować. I optymalizuj dokładnie tam, gdzie jest wąskie gardło. Może pojedynczy wątek zewnętrzny dla pewnego złożonego algorytmu. Ale nawet wtedy musisz pomyśleć, jak rozwinie się Twoja logika gry, gdy ten algorytm zajmie 3 sekundy, aby zakończyć ...
Falco,

@Falco Nadzorujesz długoterminowe zalety tego modelu - zarówno dla projektu, jak i doświadczenia programisty. Twoje twierdzenie, że najtrudniej jest myśleć, że tak naprawdę nie można się odnieść, to tylko opinia. Dla mnie projektowanie GUI jest znacznie bardziej przerażające. Wszystkie rozwinięte języki (C ++, Java) mają dość wyraźne modele wielowątkowości. A jeśli naprawdę nie jesteś pewien, możesz użyć modelu aktora, który nie cierpi z powodu błędów wielowątkowości dla początkujących. Wiesz, że istnieje powód, dla którego większość aplikacji jest zaprojektowana zgodnie z moją propozycją, ale możesz się o to kłócić.
Tomáš Zato - Przywróć Monikę

4

Nawet Space Invaders zarządzały dziesiątkami interaktywnych obiektów. Natomiast dekodowanie jednej klatki wideo HD H264 wymaga setek milionów operacji arytmetycznych. Dostępna jest duża moc obliczeniowa.

To powiedziawszy, nadal możesz zwolnić, jeśli go zmarnujesz. Problemem jest nie tyle liczba obiektów, ile liczba przeprowadzonych testów zderzeniowych; podejście prosty sprawdzenia każdego obiektu przed siebie obiektów kwadratów liczby obliczeń wymagane. Testowanie 1001 obiektów pod kątem kolizji wymagałoby miliona porównań. Często rozwiązuje się to poprzez np. Nie sprawdzanie pocisków pod kątem kolizji.


2
Nie jestem pewien, czy Space Invaders to najlepsze porównanie. Powodem, dla którego zaczyna się powoli i przyspiesza, gdy zabijasz wrogów, nie jest to, że został zaprojektowany w ten sposób, ale dlatego, że sprzęt nie był w stanie obsłużyć renderowania tylu wrogów jednocześnie. en.wikipedia.org/wiki/Space_Invaders#Hardware
Mike Kellogg

Co z tego, że każdy obiekt utrzymuje listę wszystkich obiektów, które są wystarczająco blisko, aby w następnej sekundzie mogły się z nimi zderzyć, aktualizowane co sekundę lub za każdym razem, gdy zmieniają kierunek?
Random832

Zależy od tego, co modelujesz. Rozwiązania do partycjonowania przestrzeni są kolejnym powszechnym podejściem: podziel świat na regiony (np. BSP, które i tak możesz zrobić w celu renderowania, lub quadtree), wtedy możesz kolidować tylko z obiektami w tym samym regionie.
pjc50 15.04.16

3

Nie zgadzam się z niektórymi innymi odpowiedziami tutaj. Oddzielne wątki logiczne są nie tylko dobrym pomysłem, ale są niezwykle korzystne dla szybkości przetwarzania - jeśli logika jest łatwa do rozdzielenia .

Twoje pytanie jest dobrym przykładem logiki, którą prawdopodobnie można oddzielić, jeśli możesz dodać na niej dodatkową logikę. Na przykład, możesz uruchomić kilka wątków wykrywających uderzenia, blokując wątki do określonych obszarów przestrzeni lub muteksując zaangażowane obiekty.

Prawdopodobnie NIE chcesz jednego wątku dla każdej możliwej kolizji, tylko dlatego, że prawdopodobnie spowoduje to utratę harmonogramu; istnieje również koszt związany z tworzeniem i niszczeniem wątków. Lepiej zrobić pewną liczbę wątków wokół rdzeni systemu (lub użyć metryki takiej jak stara #cores * 2 + 4), a następnie użyć ich ponownie po zakończeniu procesu.

Jednak nie wszystkie logiki można łatwo oddzielić. Czasami twoje operacje mogą obejmować wszystkie dane gry naraz, co sprawia, że ​​wątki są bezużyteczne (w rzeczywistości szkodliwe, ponieważ musisz dodać kontrole, aby uniknąć problemów z wątkami). Ponadto, jeśli wiele etapów logiki jest w dużym stopniu zależnych od siebie występujących w określonych zamówieniach, będziesz musiał kontrolować wykonywanie wątków w taki sposób, aby zapewnić, że nie będą dawały wyników zależnych od zamówienia. Jednak ten problem nie został wyeliminowany przez nieużywanie wątków, wątki tylko go zaostrzają.

Większość gier nie robi tego po prostu dlatego, że jest bardziej złożona niż przeciętny twórca gier jest w stanie poradzić sobie z tym, co zwykle nie jest wąskim gardłem. Zdecydowana większość gier jest ograniczona GPU, a nie CPU. Chociaż poprawa szybkości procesora może ogólnie pomóc, zwykle nie jest to najważniejsze.

To powiedziawszy, silniki fizyki często wykorzystują wiele wątków i mogę wymienić kilka gier, które moim zdaniem skorzystałyby z wielu wątków logicznych (na przykład gry Paradox RTS, takie jak HOI3).

Zgadzam się z innymi postami, że prawdopodobnie nie będziesz musiał używać wątków w tym konkretnym przykładzie, nawet jeśli może to być korzystne. Wątek powinien być zarezerwowany dla przypadków, w których występuje nadmierne obciążenie procesora, którego nie można zoptymalizować innymi metodami. To ogromne przedsięwzięcie, które wpłynie na podstawową strukturę silnika; nie jest to coś, na co można się skupić po fakcie.


2

Myślę, że w innych odpowiedziach brakuje ważnej części pytania, ponieważ zbytnio koncentruję się na wątku pytania.

Komputer nie obsługuje jednocześnie wszystkich obiektów w grze. Obsługuje je kolejno.

Gra komputerowa rozwija się w dyskretnych odstępach czasu. W zależności od gry i prędkości komputera kroki te wynoszą zwykle 30 lub 60 kroków na sekundę lub tyle / kilka kroków, ile komputer może obliczyć.

W jednym takim kroku komputer oblicza, co zrobią poszczególne obiekty w grze, i odpowiednio je aktualizuje, jeden po drugim. Mógłby to zrobić nawet równolegle, używając wątków, aby być szybszymi, ale jak wkrótce zobaczymy, prędkość nie jest wcale problemem.

Przeciętna procesora powinna wynosić 2 lub GHz, to znaczy 10 9 taktów na sekundę. Jeśli obliczenia 60 timesteps na sekundę, że liście 10 9 cykli / 60 cykli zegara = 16666666 zegara za czas kroku. Przy 70 jednostkach nadal mamy około 2400000 cykli zegara na jednostkę. Gdybyśmy musieli zoptymalizować, moglibyśmy zaktualizować każdą jednostkę w zaledwie 240 cyklach, w zależności od złożoności logiki gry. Jak widać, nasz komputer jest około 10 000 razy szybszy niż to konieczne.


0

Oświadczenie: Mój ulubiony rodzaj gier jest oparty na tekście i piszę to jako długoletni programista starego MUD.

Myślę, że ważne pytanie, które musisz sobie zadać, brzmi: czy potrzebujesz w ogóle nici? Rozumiem, że gra graficzna prawdopodobnie ma większe zastosowanie MT, ale myślę, że zależy to również od mechaniki gry. (Warto również wziąć pod uwagę, że dzięki procesorom graficznym, procesorom i wszystkim innym zasobom, które mamy dzisiaj, są znacznie potężniejsze, co sprawia, że ​​obawy o zasoby są tak problematyczne, jak mogłoby się wydawać; w rzeczywistości 100 obiektów jest praktycznie zerowych). Zależy to również od tego, jak zdefiniujesz „wszystkie postacie jednocześnie”. Masz na myśli dokładnie w tym samym czasie? Nie będziesz tego miał, jak słusznie zauważa Peter, więc wszystko to jest bez znaczenia w dosłownym tego słowa znaczeniu; wydaje się tylko w ten sposób.

Zakładając, że przejdziesz do wątków: zdecydowanie nie powinieneś brać pod uwagę 100 wątków (a ja nawet nie zamierzam się zastanawiać, czy to za dużo dla twojego procesora, czy nie; odnoszę się tylko do komplikacji i praktyczności).

Pamiętaj jednak: wielowątkowość nie jest łatwa (jak podkreśla Philipp) i ma wiele problemów. Inni mają znacznie większe doświadczenie (o wiele) niż ja, ale mówię, że oni też sugerowaliby to samo (nawet gdyby byli bardziej zdolni niż ja - szczególnie bez mojej praktyki).

Niektórzy twierdzą, że nie zgadzają się, że wątki nie są korzystne, a niektórzy twierdzą, że każdy obiekt powinien mieć wątek. Ale (i znowu jest to cały tekst, ale nawet jeśli weźmiesz pod uwagę więcej niż jeden wątek, nie musisz - i nie powinien - rozważać go dla każdego obiektu), jak wskazuje Philipp, gry mają tendencję do iteracji po listach. Ale to nie tylko (jak sugeruje, chociaż zdaję sobie sprawę, że on reaguje tylko na twoje parametry tak niewielu obiektów) dla tak niewielu obiektów. W MUD jestem programistą, ponieważ mamy następujące (i to nie wszystko, co dzieje się w czasie rzeczywistym, więc miej to na uwadze):

(Liczba wystąpień jest oczywiście różna - wyższa i niższa)

Telefony komórkowe (NPC, tj. Postać inna niż gracz): 2614; prototypy: 1360 Obiekty: 4457; prototypy: 2281 Pokoje: 7983; prototypy: 7983. Każdy pokój ma zwykle swoją własną instancję, ale mamy też pokoje dynamiczne, to znaczy pokoje w pokoju; lub pokoje wewnątrz telefonu komórkowego, np. żołądek smoka; lub pokoje w obiektach, np. wchodzisz do magicznego obiektu). Należy pamiętać, że te dynamiczne pokoje istnieją na obiekt / pokój / telefon komórkowy, który faktycznie je zdefiniował. Tak, to bardzo przypomina pomysł World of Warcraft (nie gram, ale znajomy kazał mi grać, gdy miałem komputer z systemem Windows) przez pewien czas, z wyjątkiem tego, że mieliśmy go na długo przed stworzeniem World of Warcraft.

Skrypty: 868 (obecnie) (co dziwne, nasze polecenie statystyki nie pokazuje, ile mamy prototypów, więc dodam to). Wszystkie odbywają się w obszarach / strefach, a mamy ich 103. Mamy również specjalne procedury, które działają w różnych momentach. Mamy też inne wydarzenia. Następnie mamy również podłączone gniazda. Telefony poruszają się, wykonują różne czynności (oprócz walki), mają interakcje z graczami i tak dalej. (Podobnie jak inne rodzaje bytów).

Jak sobie z tym wszystkim poradzić bez opóźnień?

  • gniazda: select (), kolejki (wejście, wyjście, zdarzenia, inne rzeczy), bufory (wejście, wyjście, inne rzeczy) itp. Są one odpytywane 10 razy na sekundę.

  • postacie, przedmioty, pokoje, walka, wszystko: wszystko w centralnej pętli na różnych pulsach.

My (moja implementacja oparta na dyskusji między założycielem / innym programistą a mną) przeprowadziliśmy obszerne śledzenie połączonych list i testowanie poprawności wskaźnika i mamy więcej niż wystarczającą ilość wolnych zasobów, gdybyśmy rzeczywiście tego potrzebowali. Wszystko to (poza tym, że rozszerzyliśmy świat) istniało lata temu, kiedy było mniej pamięci RAM, mocy procesora, miejsca na dysku twardym itp. I nawet wtedy nie mieliśmy problemów. W opisanych pętlach (skrypty powodują to, podobnie jak resety obszarów / zaludnienia, podobnie jak inne rzeczy), potwory, obiekty (przedmioty) i inne rzeczy są tworzone, uwalniane i tak dalej. Połączenia są również akceptowane, odpytywane i wszystko, czego można oczekiwać.

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.