Mam kilka starych programów, ściągnąłem komputer z systemem Windows z wczesnych lat 90. i próbowałem uruchomić je na stosunkowo nowoczesnym komputerze. Co ciekawe, biegali z niesamowitą szybkością - nie, nie z szybkością 60 klatek na sekundę, raczej, och, mój-boże-bohater-idzie-z-prędkością-dźwięku szybki. Naciskałem klawisz strzałki, a duszek postaci przesuwa się po ekranie znacznie szybciej niż zwykle. Postępy w grze przebiegały znacznie szybciej niż powinny. Istnieją nawet programy spowalniające procesor, dzięki czemu gry te są rzeczywiście dostępne.
Słyszałem, że jest to związane z grą w zależności od cykli procesora lub czegoś podobnego. Moje pytania to:
- Dlaczego starsze gry to robią i jak sobie z tym poradziły?
- Jak nowsze gry tego nie robią i działają niezależnie od częstotliwości procesora?
FOR F IN 0 TO 1000; NEXT F;
Odpowiedzi:
Uważam, że założyli, że zegar systemowy będzie działał z określoną częstotliwością, i powiązali wewnętrzne zegary z tą częstotliwością. Większość z tych gier prawdopodobnie działała w systemie DOS i działała w trybie rzeczywistym (z pełnym, bezpośrednim dostępem do sprzętu) i zakładała, że korzystasz z systemu iirc 4,77 MHz dla komputerów PC i jakiegokolwiek standardowego procesora, który model ten działał dla innych systemów, takich jak Amiga.
Przyjęli również sprytne skróty oparte na tych założeniach, w tym zaoszczędzili trochę zasobów, nie pisząc wewnętrznych pętli czasowych w programie. Zużyli również tyle mocy procesora, ile mogli - co było przyzwoitym pomysłem w czasach wolnych, często pasywnie chłodzonych układów!
Początkowo jednym ze sposobów na obejście różnych prędkości procesora był dobry stary przycisk Turbo (który spowolnił twój system). Nowoczesne aplikacje są w trybie chronionym, a system operacyjny ma tendencję do zarządzania zasobami - nie pozwala aplikacji DOS (która i tak działa w systemie NTVDM w systemie 32-bitowym) w wielu przypadkach zużywać cały procesor. Krótko mówiąc, systemy operacyjne stały się inteligentniejsze, podobnie jak interfejsy API.
Opierając się mocno na tym przewodniku na PC Oldskool, gdzie zawiodła mnie logika i pamięć - to świetna lektura i zapewne bardziej zagłębia się w „dlaczego”.
Rzeczy takie jak CPUkiller zużywają jak najwięcej zasobów, aby „spowolnić” system, co jest nieefektywne. Lepiej byłoby użyć DOSBoxa do zarządzania zegarem, który widzi twoja aplikacja.
źródło
game loop
. Istnieją w zasadzie 2 metody. 1) Biegnij tak szybko, jak to możliwe i skaluj prędkości ruchu itp. W oparciu o szybkość gry. 2) Jeśli jesteś zbyt szybki, poczekaj (sleep()
), aż będziemy gotowi na kolejne „tik”.Jako dodatek do odpowiedzi Journeyman Geek (ponieważ moja edycja została odrzucona) dla osób, które są zainteresowane częścią kodowania / perspektywą programisty:
Z punktu widzenia programistów, dla tych, którzy są zainteresowani, czasy DOS były czasami, w których każdy takt procesora był ważny, więc programiści zachowali kod tak szybko, jak to możliwe.
Typowy scenariusz, w którym dowolny program będzie działał z maksymalną szybkością procesora, jest taki prosty (pseudo C):
to będzie działać wiecznie, teraz zmieńmy ten fragment kodu w pseudo-grę DOS:
chyba że
DrawGameOnScreen
funkcje używają podwójnego buforowania / synchronizacji V (co było dość drogie w czasach, gdy powstawały gry DOS), gra będzie działać z maksymalną szybkością procesora. Na współczesnym telefonie komórkowym i7 działałoby to z prędkością od 1 000 000 do 5 000 000 razy na sekundę (w zależności od konfiguracji laptopa i bieżącego użycia procesora).Oznaczałoby to, że gdybym mógł uruchomić dowolną grę DOS działającą na moim nowoczesnym procesorze w 64-bitowych oknach, mógłbym uzyskać więcej niż tysiąc (1000!) Klatek na sekundę, co jest zbyt szybkie dla każdego człowieka, jeśli proces fizyki „zakłada”, że działa pomiędzy 50-60 fps.
Programiści (mogą) na dzień dzisiejszy to:
*** w zależności od konfiguracji karty graficznej / sterownika / systemu operacyjnego może być to możliwe.
Dla punktu 1 nie pokażę żadnego przykładu, ponieważ tak naprawdę nie jest to „programowanie”. Po prostu korzysta z funkcji graficznych.
Jeśli chodzi o pkt 2 i 3, pokażę odpowiednie fragmenty kodu i objaśnienia:
2:
Tutaj możesz zobaczyć, jak dane wejściowe użytkownika i fizyka uwzględniają różnicę czasu, ale wciąż możesz uzyskać ponad 1000 FPS na ekranie, ponieważ pętla działa tak szybko, jak to możliwe. Ponieważ silnik fizyki wie, ile czasu minęło, nie musi on zależeć od „żadnych założeń” lub „pewnej liczby klatek na sekundę”, więc gra będzie działać z tą samą prędkością na dowolnym procesorze.
3:
Co programiści mogą zrobić, aby ograniczyć liczbę klatek na sekundę, na przykład do 30 klatek na sekundę, jest w rzeczywistości niczym trudniejszym, wystarczy spojrzeć:
To, co się tutaj dzieje, polega na tym, że program liczy, ile milisekund minęło, jeśli określona ilość zostanie osiągnięta (33 ms), wówczas przerysowuje ekran gry, skutecznie stosując częstotliwość klatek bliską ~ 30.
Ponadto, w zależności od programisty, może on ograniczyć przetwarzanie WSZYSTKIE do 30 fps z powyższym kodem nieco zmodyfikowanym do tego:
Jest kilka innych metod, a niektórych z nich naprawdę nienawidzę.
Na przykład za pomocą
sleep(<amount of milliseconds>)
.Wiem, że jest to jedna z metod ograniczania liczby klatek na sekundę, ale co się dzieje, gdy przetwarzanie gry trwa 3 milisekundy lub dłużej? A potem wykonujesz sen ...
spowoduje to niższą liczbę klatek na sekundę niż ta, która
sleep()
powinna tylko powodować.Weźmy na przykład czas snu 16 ms. spowoduje to, że program będzie działał przy 60 Hz. teraz przetwarzanie danych, danych wejściowych, rysowania i wszystkich innych zajmuje 5 milisekund. mamy teraz 21 milisekund na jedną pętlę, co powoduje nieco mniej niż 50 Hz, podczas gdy możesz łatwo być przy 60 Hz, ale z powodu snu jest to niemożliwe.
Jednym z rozwiązań byłoby wykonanie adaptacyjnego snu w formie pomiaru czasu przetwarzania i odliczenia czasu przetwarzania od pożądanego snu, co skutkuje naprawieniem naszego „błędu”:
źródło
Jedną z głównych przyczyn jest użycie pętli opóźniającej, która jest kalibrowana podczas uruchamiania programu. Liczą, ile razy pętla wykonuje się w znanym czasie i dzielą ją, aby wygenerować mniejsze opóźnienia. Można to następnie wykorzystać do zaimplementowania funkcji sleep () w celu przyspieszenia wykonania gry. Problemy pojawiają się, gdy ten licznik jest maksymalny, ponieważ procesory są tak szybsze w pętli, że małe opóźnienie jest zbyt małe. Ponadto nowoczesne procesory zmieniają prędkość w zależności od obciążenia, czasem nawet w przeliczeniu na rdzeń, co sprawia, że opóźnienie jest jeszcze większe.
W przypadku naprawdę starych gier komputerowych działały tak szybko, jak to możliwe, bez względu na próbę nadania tempa grze. Tak było raczej w dniach IBM PC XT, jednak istniał przycisk turbo, który spowolnił system do dopasowania procesora 4,77 MHz z tego powodu.
Nowoczesne gry i biblioteki, takie jak DirectX, mają dostęp do liczników czasu wysokiej precesji, więc nie trzeba używać skalibrowanych pętli opóźnień opartych na kodzie.
źródło
Wszystkie pierwsze komputery działały z tą samą prędkością na początku, więc nie trzeba było uwzględniać różnicy prędkości.
Ponadto wiele gier na początku miało dość stałe obciążenie procesora, więc nie było prawdopodobne, aby niektóre ramki działały szybciej niż inne.
W dzisiejszych czasach, gdy masz dzieci i swoich fantazyjnych strzelanek FPS, możesz spojrzeć na podłogę w jednej sekundzie, a w wielkim kanionie w następnej kolejności zmiany obciążenia zdarzają się częściej. :)
(I niewiele konsol sprzętowych jest wystarczająco szybkich, aby stale uruchamiać gry przy 60 fps. Wynika to głównie z faktu, że twórcy konsol wybierają 30 Hz i sprawiają, że piksele są dwukrotnie błyszczące ...)
źródło