Jak koncepcyjnie działa powtórka w grze?

145

Byłem trochę ciekawy, jak można zaimplementować powtórkę w grze.

Początkowo myślałem, że będzie tylko lista poleceń wszystkich działań gracza / ai, które zostały podjęte w grze, a następnie „gra” ponownie i pozwala silnikowi renderować się jak zwykle. Mam jednak spojrzał na powtórkach w FPS / RTS gry, a po wnikliwej kontroli nawet rzeczy takie jak cząstki i graficznych / słyszalnych trzasków są spójne (i te usterki są na ogół w spójne).

Więc jak to się dzieje. W grach ze stałym kątem kamery pomyślałem, że może po prostu zapisać każdą klatkę całej sceny w strumieniu, który zostanie zapisany, a następnie po prostu powtórzy strumień z powrotem, ale to nie wydaje się wystarczające dla gier, które pozwalają na wstrzymywanie i poruszanie kamerą na około. Musiałbyś przechowywać lokalizacje wszystkiego na scenie w każdym momencie (nie?). Tak więc w przypadku cząsteczek jest to dużo danych do przekazania, co wydaje się znaczącym obniżeniem wydajności gry podczas grania.

Steven Evers
źródło
10
Oryginalne powtórki Star Craft w rzeczywistości NIE były spójne. Możesz dwukrotnie obejrzeć tę samą grę i zobaczyć nieco inne wyniki.
Andres
1
@Andres: Interesujące, nie zauważyłem. W szczególności, jeśli chodzi o gatunek RTS, myślałem o Company Of Heroes.
Steven Evers
4
Aby wyjaśnić, o co moim zdaniem SnOrfus pyta: Niektóre gry (Uncharted 2, Halo 3, nawet Battlefield 2) pozwalają na nagranie całej gry. Po zakończeniu gry możesz ją odtwarzać z wyznaczoną prędkością i latać przez poziom w trakcie akcji, oglądając ją z dowolnego miejsca na mapie. Zakładam więc, że chodzi o nagrywanie ruchów wszystkich graczy / obiektów, a nie o coś związanego z buforem wideo.
Sean
1
@Sean O'Hollaren: Tak, zgadza się.
Steven Evers
1
Następnie dodam również dla gier wyścigowych, w których powtórki są prawie domyślne. Jestem prawie pewien, że lokalizacja modeli jest zapisywana, a następnie wszystko jest uruchamiane przez silnik.
d -_- b

Odpowiedzi:

61

Myślę, że twoja początkowa myśl była poprawna. Aby utworzyć powtórkę, przechowujesz wszystkie dane wejściowe otrzymane od użytkownika (wraz z numerem klatki, w której zostało odebrane) wraz z początkowymi początkami dowolnych generatorów liczb losowych. Aby odtworzyć grę, resetujesz swoje PRNG za pomocą zapisanych seedów i zasilasz silnik gry tą samą sekwencją danych wejściowych (zsynchronizowanych z numerami klatek). Ponieważ wiele gier aktualizuje stan gry na podstawie czasu, jaki upływa między klatkami, może być konieczne zapisanie długości każdej klatki.

Peter Ruderman
źródło
Numery klatek mogą nie być dobrym odniesieniem, ponieważ powtórka może przebiegać z inną liczbą klatek na sekundę niż gra na żywo.
Ben S
5
@Ben: Szybkość klatek nie ma znaczenia, ponieważ numery klatek będą nadal takie same. To jest poprawna odpowiedź.
BlueRaja - Danny Pflughoeft
14
Ramki graficzne i „ramki” silnika (lub iteracje) niekoniecznie są takie same. W wielu starszych grach silnik aktualizował się w tym samym tempie co grafika, w jednej pętli głównej. W nowoczesnych silnikach grafika często aktualizuje się tak szybko, jak pozwala na to GPU, a silnik pracuje na poziomie wymaganym dla dobrej, spójnej rozdzielczości dynamiki gry (często silnik fizyki).
Dan Bryant
3
@iamgopal: jeśli znasz stan generatora liczb pseudolosowych, to ten problem został już rozwiązany. Inną metodą może być traktowanie liczb losowych jako innej formy danych wejściowych i zapisywanie ich obok naciśnięć klawiszy i tym podobnych.
Kylotan
1
Chciałbym dodać, że takie podejście wymaga, aby silnik gry był deterministyczny i działał w ustalonym przedziale czasowym. Wierzę, że wszystkie gry RTS Blizzarda zostały zbudowane w ten sposób. Gry niedeterministyczne obejmowałyby dodatkowe dane synchronizacyjne, aby zapewnić spójność w dłuższej perspektywie.
John Leidegren
28

Starcraft i Starcraft: Brood War miały funkcję powtórki. Po zakończeniu meczu możesz zapisać powtórkę, aby wyświetlić ją później. Podczas powtórki można było przewijać mapę i klikać jednostki i budynki, ale nie zmieniać ich zachowania.

Pamiętam, jak oglądałem powtórkę meczu, który był rozegrany w oryginalnej grze, ale powtórka była oglądana w Brood War. Dla tych, którzy nie są zaznajomieni, Brood War zawiera wszystkie oryginalne jednostki i budynki, a także wiele nowych. W oryginalnej grze gracz pokonał komputer, tworząc jednostki, którym komputer nie mógł łatwo przeciwdziałać. Kiedy grałem w powtórkę w Brood War, komputer miał dostęp do różnych jednostek, które tworzył i wykorzystywał do pokonania gracza. Tak więc ten sam plik powtórki skutkował innym zwycięzcą w zależności od tego, która wersja Starcraft odtwarzała plik.

Zawsze fascynowała mnie ta koncepcja. Wydawałoby się, że funkcja powtórki działała poprzez rejestrowanie wszystkich danych wejściowych odtwarzacza i zakładała, że ​​komputer za każdym razem będzie reagował na te bodźce w dokładnie taki sam sposób. Kiedy dane wejściowe gracza zostały wprowadzone do oryginalnego odtwarzacza Starcraft, gra przebiegała dokładnie tak, jak w pierwotnym meczu. Gdy te same dane wprowadzono do odtwarzacza Brood War, komputer zareagował inaczej, stworzył silniejsze jednostki i wygrał grę.

Coś, o czym należy pamiętać, pisząc silnik powtórek.

Bobwise
źródło
6
+1: Bardzo interesujące. Nigdy o tym nie słyszałem. Zapewnia dobry wgląd w to, jak go opracowali.
Steven Evers
18

Istnieją dwie główne metody:

  1. Przechowywanie wydarzeń (takich jak akcje gracza / AI) - tak, jak mówisz.
  2. Stan przechowywania (pełny stan gry, np. Lokalizacje obiektów, w kolejnych momentach).

To zależy od tego, co chcesz zrobić. Czasami przechowywanie zdarzeń jest lepsze, ponieważ zwykle zajmuje to znacznie mniej pamięci. Z drugiej strony, jeśli chcesz zapewnić powtórki, które można odtwarzać z różnymi prędkościami iz różnych punktów początkowych, lepiej jest przechowywać stany. Podczas zapisywania stanów możesz również zdecydować, czy zapisywać je po każdym wydarzeniu, czy np. Tylko 12 lub 25 razy na sekundę - może to zmniejszyć rozmiar powtórki i ułatwić przewijanie ich do tyłu / do przodu.

Zauważ, że „stan” nie oznacza stanu graficznego. Bardziej jak pozycje jednostek, stan zasobów i tak dalej. Rzeczy takie jak grafika, układy cząstek itp. Są zwykle deterministyczne i mogą być przechowywane jako „animacja X, czas Y: Z”.

Czasami powtórki są używane jako schemat anticheating. Zatem przechowywanie wydarzeń jest tutaj prawdopodobnie najlepsze.

liori
źródło
10

Technicznie rzecz biorąc, powinieneś napisać swój silnik tak, aby był deterministyczny, czyli nie ma przypadkowości. Zakładając, że postać w grze celuje w ramię przeciwnika i strzela z broni, wówczas we wszystkich przypadkach należy zadać przeciwnikowi taką samą ilość obrażeń.

Zakładając, że bomba zdetonuje się w miejscu X, cząstki wytwarzane przez tę eksplozję powinny zawsze dawać ten sam efekt wizualny. Jeśli potrzebujesz losowości, utwórz zestaw liczb losowych, wybierz wartość początkową podczas gry i zapisz tę wartość początkową w powtórce.

Ogólnie rzecz biorąc, losowość w grze to zły pomysł. Nawet w przypadku gier wieloosobowych nie możesz mieć połowy graczy w stanie widzieć wokół eksplozji, podczas gdy inni nie mogą po prostu dlatego, że nie otrzymali odpowiedniej losowej wartości.

Niech wszystko będzie deterministyczne, a powinno być dobrze.

Timothy Baldridge
źródło
1
A co z AI? Czy sztuczna inteligencja nie jest przypadkowa?
Jesse Jashinsky
18
To naprawdę nie jest konieczne. Użyj rozstawionych liczb pseudolosowych dla wszystkich zdarzeń losowych i zapisz ziarno w pliku powtórki. W ten sposób podczas powtórki będą generowane te same „losowe” liczby.
Ben S,
13
-1 za wyraźne niezrozumienie działania „losowości” w komputerach
BlueRaja - Danny Pflughoeft
10
hm… nie… Jestem doskonale świadomy, że nie ma czegoś takiego jak „prawdziwa” przypadkowość. Jednak większość ludzi próbuje to obejść, ustawiając swoje losowe ziarno na coś podobnego do czasu systemowego. Jednak mówię, że czegoś takiego nie należy robić. Nie obchodzi mnie, czy korzysta z systemowego API, czy z predefiniowanej tabeli liczb losowych. To, co pierwotnie powiedziałem, było poprawne. Każda funkcja jego silnika powinna dawać ten sam wynik na podstawie swoich danych wejściowych. Czas nigdy nie powinien być czynnikiem.
Timothy Baldridge
2
Jeśli cząsteczki nie mają żadnego znaczącego wpływu na mechanikę gry, nie ma znaczenia, czy RNG są dla nich inne. Pomogłoby to w przypadku symulacji zsynchronizowanej z siecią (jak ma to miejsce w większości gier RTS i wielu innych gatunkach gier), ponieważ jest to trochę mniej symulacji, która musi synchronizować każdą klatkę (efekty cząstek są po prostu aktualizowane indywidualnie).
RCIX
10

Biorąc pod uwagę stan początkowy i serię działań z sygnaturami czasowymi , po prostu przejdź przez sekwencję, ponieważ nagrane działania mają się wydarzyć i odtworzyć.

Aby przypadkowe zdarzenia wystąpiły ponownie dokładnie to samo, użyj zapoczątkowanych liczb pseudolosowych i zapisz ziarno w pliku powtórki.

Dopóki używasz tego samego algorytmu do generowania liczb losowych z materiału siewnego, możesz odtworzyć wszystkie zdarzenia tak, jak wystąpiły w grze na żywo, bez konieczności wykonywania pełnych migawek stanu gry.

Będzie to wymagało oglądania powtórek po kolei , ale jest to całkiem normalne w przypadku powtórek z gry (patrz Starcraft 2). Jeśli chcesz zezwolić na swobodny dostęp do osi czasu, możesz wykonywać pełne migawki stanu w ustalonych odstępach czasu (powiedzmy co minutę), aby przeskakiwać po osi czasu z określoną szczegółowością.

Ben S.
źródło
Jeśli ponownie wysiewasz co określoną liczbę sekund (powiedzmy 5 lub 10), łatwo byłoby nagrać w strumieniu powtórek, a także pozwolić na przeskakiwanie do przodu lub do tyłu (zasadniczo do "klatek kluczowych" PRNG).
Wedge
7

NVidia PhysX (silnik symulacji fizyki, który jest często używany w grach) jest w stanie rejestrować pełny stan fizycznej sceny w czasie. Obejmuje to wszelkie dane wejściowe z silnika gry, co oznacza, że ​​nie musisz śledzić losowych nasion, jak sugerowali inni. Jeśli weźmiesz ten zrzut sceny, możesz go odtworzyć w zewnętrznym narzędziu (dostarczonym przez firmę NVidia), które jest bardzo przydatne do śledzenia problemów z modelami fizycznymi. Jednak możesz również użyć tego samego strumienia fizyki do sterowania silnikiem graficznym, który pozwoliłby ci wtedy na normalne sterowanie kamerą, ponieważ zapisywana była tylko fizyka sterująca grafiką. W wielu grach dotyczy to również efektów cząsteczkowych (PhysX zawiera kilka bardzo wyrafinowanych systemów cząsteczek). Jeśli chodzi o dźwięk, zgaduję, że jest on nagrany dosłownie (jako strumień dźwiękowy), ale ja '

Dan Bryant
źródło
4

Twój oryginalny pomysł jest słuszny, a dla naprawdę złożonych efektów nie są one pamiętane wyłącznie. Na przykład, system powtórek Warcraft 3 nie przechowuje stanu animacji, ani efektów cząsteczkowych w przypadku efektów losowych itp. Poza tym WIĘKSZOŚĆ rzeczy można obliczyć obliczeniowo od punktu początkowego w sposób deterministyczny, więc dla większości systemów używających zmiennych losowych (na przykład eksplozji cząstek, która daje losowe przesunięcie), wszystko, czego potrzebujesz, to czas efektu i losowe ziarno. Możesz wtedy ponownie wygenerować efekt, nie wiedząc tak naprawdę, jak będzie wyglądał ... wiedząc, że przechodzi przez deterministyczną ścieżkę kodu.

Myśląc o tym czysto koncepcyjnie, aby odtworzyć oś czasu zdarzeń, potrzebujesz tylko działań użytkownika. Program zareaguje dokładnie w ten sam sposób, poza przypadkami zmiennych losowych. W tym scenariuszu możesz albo zignorować losowość (czy NAPRAWDĘ ma to znaczenie, jeśli efekty wyglądają DOKŁADNIE tak samo, czy też mogą zostać losowo wygenerowane ponownie) lub zapisać wartość ziarna i sfałszować losowość.

Stefan Valianu
źródło
3

Wrzuć moje dwa pensy.

W zależności od tego, czego chcesz, powtórka może zostać wykonana przez

  1. Nagrywanie bufora wideo i odtwarzanie później,
  2. Przechwytywanie stanu obiektu z każdej klatki i odtwarzanie później,

W większości przypadków ludzie chcą interaktywnej powtórki, więc 2. jest najlepszym rozwiązaniem. Następnie, w zależności od twoich ograniczeń, istnieje wiele sposobów optymalizacji tego procesu

  • Upewnij się, że system jest deterministyczną symulacją *, tak aby każde wejście generowało spójny i oczekiwany wynik
  • jeśli wymagana jest losowość, upewnij się, że liczby losowe mogą zostać odtworzone dokładnie w późniejszym czasie [spójrz na rozstawianie za pomocą generatorów pseudolosowych PRNG lub użyj gotowych zestawów losowych]
  • podziel elementy gry na elementy „mechaniczne” i „estetyczne”. elementy mechaniczne wpływają na wynik (np. przewrócenie się kolumny i blokowanie ścieżki), elementy estetyczne są do pokazania i nie wpływają na żaden proces decyzyjny w systemie [np. wizualne efekty cząsteczkowe, takie jak iskry].

To naprawdę fascynujący temat. Pamiętam, że jeden tytuł premierowy oryginalnego Xbox Wreckless miał dobrą funkcję odtwarzania. Niestety, niejednokrotnie powtórka się zepsuła;)

o tak, jak ktokolwiek mógł zapomnieć o Blinx Time Sweeper ! świetna interaktywna powtórka, która została włączona do rzeczywistej mechaniki gry!


* = wydaje się, że są pewne uwagi dotyczące kroków w czasie. Używam tutaj „symulacji”, aby uchwycić tę funkcję. w istocie Twój silnik musi być w stanie wytwarzać dyskretne ramy czasu. nawet jeśli przetwarzanie klatki powtórki trwa dłużej lub krócej niż oryginał, system musi dostrzec, że minęła ta sama delta czasu. oznacza to rejestrowanie przedziału czasowego klatki z każdym zarejestrowanym wejściem i dostarczanie tej delty do zegara silnika.

johnny g
źródło
2

Być może mógłbyś po prostu zapisać stos poleceń wysyłanych przez każdego gracza. Zamiast więc oszczędzać, że bomba wybuchnie w określonym momencie lub w określonym czasie, lub że jakiś samochód zostanie zniszczony, po prostu zapisujesz naciśnięcia klawiszy wysyłane przez każdego gracza. Następnie w powtórce po prostu symulujesz grę, tak jak by się to stało w przypadku tych pras. Wydaje mi się, że może to zająć mniej miejsca, ale nigdy nie pracowałem nad takim systemem powtórek.

Ciekawe pytanie. Byłbym zainteresowany, jak to się robi w profesjonalnych grach.

ThirdD3gree
źródło
2

Dan Bryant

Ponadto rejestrowanie losowych zarodków nie byłoby wystarczające do obsługi przewijania do tyłu, ponieważ losowy postęp nie jest odwracalną procedurą bez specjalnego wsparcia w całej logice opartej na losowości. Bardziej elastyczne jest rejestrowanie wyników operacji losowych jako części strumienia zdarzeń.

Dokładnie to pomyślałem na początku, kiedy próbowałem dowiedzieć się, jak to zrobili, aby gra była odtwarzana zawsze tak samo za każdym razem. Z Doomem pomyślałem, jak przypadkowe poszły zdjęcia: D. Przechowuj dowolną liczbę losową, odkryłem, że może to być rozwiązanie. To było zanim natknąłem się na dokument pdf o technologii Crysis. Niektóre tekstury są tam szumiące i usposobienie trawy lub drzewa, wydawało się, że używają pseudorandomizacji z ustalonymi odwracalnymi nasionami, aby zrobić to, abyś nie widział zmienionego rozmieszczenia hałasu, drzew i trawy za każdym razem, gdy spojrzysz!

Unikanie w tym samym czasie przechowywania milionów drzew i pozycji trzonów trawiastych. Pozornie pseudolosowa sekwencja może powtórzyć to samo w dowolnym momencie, ponieważ logika jest ustalona, ​​aby po prostu utworzyć fałszywą statystycznie losową sekwencję liczb.

Antonimowy
źródło
Jeśli chcesz zwrócić na to uwagę Dana, dodaj komentarz pod jego wkładem - w przeciwnym razie prawdopodobnie go nie zobaczy.
halfer
Czy może to być spowodowane tym, że jestem tylko gościem, ale nie widziałem żadnej funkcji „dodaj komentarz” w poście nadrzędnym, odpowiedział Dan, nie mówiąc już o odpowiedzi Dana. Widziałem, że istnieje funkcja poprawiania edycji, nawet dla postów, które nie są moje, ale jak to działa?
Antonymous
Ach, dobre pytanie! To wydaje się tutaj , że trzeba 50 punktów rep komentować pytań ani odpowiedzi innych niż własne - moje przeprosiny. 50 jest jednak bardzo łatwe do zdobycia - wystarczy kilka przydatnych wkładów, aby to osiągnąć. Tak, możesz edytować pytania i odpowiedzi innych osób, ale Twoje zmiany będą sprawdzane przez innych, dopóki nie osiągniesz 2000. Zobacz tabelę swoich uprawnień tutaj .
halfer
1

Problem spójnej powtórki jest taki sam (no cóż, łatwiejszy), jak w przypadku spójnej gry wieloosobowej.

Jak inni wspomnieli wcześniej, powtórki w grach RTS są zapisywane poprzez nagrywanie wszystkich danych wejściowych (ma to wpływ. Przewijanie nie ma żadnego efektu). Tryb wieloosobowy również przesyła wszystkie dane wejściowe

Nagrywanie wszystkich danych wejściowych, a nie tylko przypuszczenie - jest biblioteka do czytania powtórek Warcraft3, która to ujawnia.

input zawiera sygnatury czasowe dla tej odpowiedzi.

Franky
źródło
Nie, to nie to samo (lub łatwiejsze), co spójna gra wieloosobowa. Kiedy grasz w MP, gry zwykle wymagają, aby każdy miał tę samą wersję gry, co niekoniecznie ma miejsce w przypadku zapisanych sesji (ponieważ mogła być przechowywana w starszej wersji gry). Jest to szczególnie ważne, jeśli jeden z graczy jest przeciwnikiem AI. Wyobraź sobie, że grasz ponownie w grę, w której jednostka ma tylko jeden punkt ataku więcej w nowszej wersji niż w wersji, w której została nagrana. Może to doprowadzić do zupełnie innego wyniku.
drakon
-1

Uważam, że przy pewnych przyrostach gra zrobi migawkę stanu wszystkiego (WSZYSTKIEGO). Wtedy, gdy ma miejsce powtórka, do wypełnienia "dziur" można użyć prostego użycia interpolacji liniowej. Przynajmniej tak mi się wydaje.

Masz rację, że rejestrowanie wejść byłoby niewiarygodne / nie gwarantuje takiego samego wyjścia. Gra zdecydowanie musi na bieżąco śledzić stan wszystkich obiektów (lub przynajmniej tych ważnych)

Bob Fincheimer
źródło
2
Nie, podanie tych samych danych wejściowych da dokładnie taki sam wynik, jak za pierwszym razem. Musisz tylko upewnić się, że otrzymałeś prawidłowe taktowanie, wprowadzając dane wejściowe między tymi samymi klatkami, w których zostało pierwotnie odebrane. Okresowe zapisywanie całego stanu gry może wymagać ogromnej ilości pamięci i powodować niespójne wyniki.
Peter Ruderman
@Peter, „podanie tych samych danych wejściowych da dokładnie ten sam wynik”: nie. W wielu grach występuje element losowy, który może być inny za każdym razem, gdy odtwarzana jest powtórka. Musisz śledzić więcej niż tylko wejścia.
houbysoft
To prawda. Musisz także przechowywać nasiona swoich PRNG (zobacz moją odpowiedź na to pytanie).
Peter Ruderman
1
Wiem, że to pochłania wydajność i pamięć, ale jeśli przegapisz 1 małą rzecz, jeśli chodzi o dane wejściowe lub losowe generatory ... lub naprawdę cokolwiek, powtórka zadziała okropnie!
Bob Fincheimer
@BlueRaja, pomysł Boba na migawkę pamięci niekoniecznie jest tak naciągany, chociaż dobry silnik może rejestrować „delty” stanu, zamiast kodować całą pamięć dla każdej iteracji. Prawdopodobnie jest to łatwiejsze do obsługi na poziomie silnika. Ponadto rejestrowanie losowych zarodków nie byłoby wystarczające do obsługi przewijania do tyłu, ponieważ losowy postęp nie jest odwracalną procedurą bez specjalnego wsparcia w całej logice opartej na losowości. Bardziej elastyczne jest rejestrowanie wyników operacji losowych jako części strumienia zdarzeń.
Dan Bryant