Dawidds Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 WSTĘP Nie drażniło Cię nigdy, że gdy Twoja gra spadnie poniżej 60 fps to automatycznie cała akcja dzieje się wolniej? Przecież w inne (nie gmowe raczej ;p) gry można spokojnie grać na mniejszym fpsie. Albo dobra, nie będzie ładnego wstępu. Po przeczytaniu tego tekstu powinieneś bez problemu oprzeć swoją grę o taki właśnie zmienny framerate - deltatime (delta to różnica (wymaga zweryfikowania ;p)) :) Więc zaczynajmy. MECHANIZM DZIAŁANIA Najpierw pomyślmy, jak to powinno działać. Im mniej fps wyrabia gra, tym wszystko powinno się dziać szybciej. Nie skorzystamy jednak z licznika fps (było by to za mało dokładne, na dodatek gm pokazuje taki, eee, specyficzny fps - nie taki jaki pokaże nam Fraps), a, jak już się pewnie domyśliłeś po nazwie, z różnicy czasu pomiędzy klatkami. Brzmi strasznie :D ? Po prostu będziemy liczyli czas, jaki upłynął pomiędzy dwoma klatkami, i na jego podstawie będziemy zmieniali prędkości wszystkich obiektów w grze. KOD Utwórz sobie obiekt, umieść go w roomie, i nazwij go jak Ci się podoba, powiedzmy obj_deltatime, wstaw do niego taki kod: GML //Create global._dt = 1/60; //W tej zmiennej globalnej bedziemy przechowywali roznice pomiedzy klatkami (w sekundach - czyli dla 60 klatek // uzyskmy watosc 0.016, i taka tez ustawiamy "na start") global._dt1 = date_current_time(); // Ta zmienna oznacza czas (normalny czas, pobrany przez date_current_time()) poprzedniej klatki global._dt2 = date_current_time(); // A ta aktualnej //Begin Step global._dt2 = date_current_time()*100000; // Ustawiamy zmienna _dt2 na aktualny czas (mnozymy przez 100000 aby uzyskac wynik w sekundach) global._dt = global._dt2-global._dt1; // Obliczamy roznice pomiedzy tymi wartosciami - odejmujemy czas z poprzedniej klatki od aktualnego czasu global._dt1 = global._dt2; // Ustawiamy aktualny czas jako ten z poprzedniej klatki</span></span></span></span></span> Jak widać, kod jest stosunkowo prosty, chyba nikt nie powinien mieć problemów z jego zrozumieniem. UŻYCIE Dobrze, mamy obliczoną różnicę w czasie pomiędzy klatkami, co dalej. Wyobraźmy sobie to. Mamy sobie platformówkę, w której gracz przy fpsie 60 powinien poruszać się z prędkością 1px/klatkę. Jak nietrudno się domyślić, przy 30 fpsach natomiast będzie przemieszczał się 2px/klatkę. Ale my nie dysponujemy fpsem, a różnicą czasu pomiędzy klatkami. Więc przebudujmy poprzedni przykład. 60 fps - 0.016ms - 1px/s 30 fps - 0.032ms - 2px/s 15 fps - 0.064ms - 4px/s Jak nietrudno zauważyć, prędkość gracza rośnie proporcjonalnie do różnicy czasu (różnica czasu zwiększa się dwa razy - prędkość gracza zwiększa się dwa razy). A więc uniwersalny wzór na prędkość gracza wygląda o tak: prędkość [px/kl] = prędkość [px/s] * global._dt; Ludek z naszego przykładu ma więc w Stepie kod: GML speed = 60 * global._dt; FUNKCJA Co by sobie wszystko maksymalnie ułatwić (aby nie dodawać do wszystkiego *global._dt) napiszemy sobie funkcję, która będzie po prostu mnożyła podaną w argumencie liczbę przez deltatime. Nazwijmy ją jakoś prosto - np. dt(). GML return argument0 * global._dt; Ruch gracza z poprzedniego przykładu będzie więc wyglądał tak: GML speed = dt(60); //60 to przypominam predkosc wyrazona w pikselach na sekunde</span></span></span></span></span> Teraz pora na omówienie różnych przykładowych ruchów. 1. STAŁA PRĘDKOŚĆ RUCHU Ten przykład już omówiliśmy, pokażę więc sam kod: GML speed = dt(prędkość w pikselach na sekundę); 2. RUCH Z OPÓŹNIENIEM (FRICTION) To nie będzie wiele bardziej skomplikowane od poprzedniego ruchu - pamiętajcie, aby wszystko, co ma jakiś związek z czasem wpakować w funkcję dt(). Do tego typu ruchu warto także używać już własnej zmiennej odpowiedzialnej za prędkość - nazwijmy ją spd. Będzie ona robiła dokładnie to samo, co gmowe speed, tyle, że będzie przechowywała prędkość wyrażoną w pikselach na sekundę. Wersja 1. GML if(keyboard_check(vk_right)) { spd += dt(100); } else { spd -= dt(100); if(spd<0){ spd=0; } } x += dt(spd); Wersja 2. (obie robią to samo, użyj tej, której będzie Ci wygodniej) GML if(keyboard_check(vk_right)) { speed += dt(20); } friction = dt(10); Jak widać, nic ciężkiego ;p 3. ALARMY Powiedzmy, że chcemy, aby co półtora sekundy wykonywała się jakaś akcja. Użycie gmowych alarmów oczywiście wchodzi w grę - musimy napisać własne. GML //Create timer = 1.5; //Czas do wykonania sie alarmu w sekudach //Step if(timer <= 0) { //akcja timer += 1.5 } else { timer -= dt(1); } Podobny (albo raczej identyczny) kod należy zastosować np. przy strzelaniu. To tyle z ruchów, teraz inna możliwość, którą daje nam użycie deltatime'u. A mianowicie... BULLET-TIME Gdy wszystkie prędkości oparliśmy o jedną funkcję, aż grzech nie dodać do tego możliwości spowolnienia akcji gry - o tak, dla zabawy. Do create obj_deltatime dodajemy: GML global.gamespeed = 1; //to jest wlasnie nasza predkosc gry - 1 to dnormalna wartosc, jesli chcemy spowolnic gre 5 razy ustawiamy ja na 0.2</span></span> Zadeklarowaliśmy jedną zmienną, która będzie odpowiadała za prędkość gry. funkcja dt(): GML return argument0 * global._dt * global.gamespeed; Prosty bullet-time do tego to chociażby: GML if(keyboard_check(vk_space)) { global.gamespeed = 0.15; } else { global.gamespeed = 1; } No i było by na tyle. DOWNLOAD Gotowy do zmerge'owania plik gmk Ed: Naprawione, tagi gml się zrypały. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PoxiPol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Whoa, czytasz mi w myslach ; ) Wlasnie sie zastanawialem nad takim czyms. Jednak jest to dosyc niewydajne, iz aby optymalizowac gre do tego kodu trzeba kazda najmniejsza rzecz zmieniac, nie myslisz? PS W punkcie 1 cos sie tobie jeszcze ztegowalo. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 Z tym najmniejszą rzecz zmieniać to nie przesadzaj, ja endurenca przepisałem pod to w efektywnie dwie godziny (nad pociskami się męczyłem 80% tego czasu, lol). W sumie to dodać "dt(" przed i ")" po każdej wartości to nie jakiś super problem. Chociaż to zależy o chcesz przerobić w sumie. A najlepiej to od początku na tym pisać, to nie będzie trzeba nic zmieniać ;d Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PoxiPol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Racja, jednak nie dalo by sie wymyslec czegos latwiejszego : (? Leniuch ze mnie. Sie okaze ze GM ma gdzies prosta zasade do tego ; ) Musze wybrobowac swoja metode. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 Łatwiejszego? Ciężko Ci podmienić wszystkie x na dt(x) :P ? Człowieku, lepiej nie zaczynaj się uczyć "normalnego" języka w takim razie ;d Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PoxiPol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Pisze w Delphi, wiec bez przesady. Ja tu mowie o GMLu a ten wyskakuje z "normalnimi jezykami". Rozumiem ze jest to spore ulatwienie, patrzac na te twoje "normalne jezyki", ale watpie aby Mark o tym nie pomyslal. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
lenin Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 ale watpie aby Mark o tym nie pomyslal. Nie pomyślał o wielu rzeczach, więc skąd takie zdziwienie? Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 No widzisz, nie bardzo jak miał o tym pomyśleć, bo prostszego rozwiązania nie ma, a gm miał być maksymalnie prosty :P No ewentualnie mógłby dać możliwość rozdzielenie stepów i drawów na osobne wątki gdzie step miałby większy priorytet priorytet, ale to też mogło by w końcu zmulić. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Uzjel Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Wydaje się być wszystko w porządku :) 5/5 Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Administratorzy gnysek Opublikowano 26 Grudnia 2009 Administratorzy Udostępnij Opublikowano 26 Grudnia 2009 Muszę to sprawdzić, bo jak ostatnio robiłem na podstawie FPSów, to dla np. 30/60 FPSów było ok, ale dla 2/60 FPS postać zasuwała tak szybko, że jednym kliknięciem wypadała 100 000px poza ekran :D Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Uzjel Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Ale na 2 FPS raczej nie pograsz. :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Cekol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Ja robiłem na current_timie() lecz zauważyłem dziwnego buga, w postaci regularnych chrupnięciach, np co 2 sec, lecz sama gra nie chrupała O.o Tylko ten current_time jakiś wadliwy był ... Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 current_time na takie coś i tak za mało precyzyjny ;p A że gm krzywo (no, nieregularnie) wskazuje czas to inna sprawa. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Uzjel Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Może jakiś prosty dll'ek naprawi sytuację ? Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 Jak próbowałem robić coś takiego na wątkach na gmthreads zwróciłem uwagę na te nieregularne pomiary czasu, to je zastąpiłem dllem i działało ładnie, tyle, że wątek z kodem wyrabiał 88 fps bodajże :P Ale to raczej wina użytego dlla była. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Snake Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 Dobry arcik. Taka technika powinna koniecznie być stosowana np. w takiej Almorze, gdzie gracz, któremu gra wyciąga mniej FPS ma znacznie utrudnioną rozgrywkę. Ale zamiast current_time lepiej użyć tego ;p Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Administratorzy gnysek Opublikowano 26 Grudnia 2009 Administratorzy Udostępnij Opublikowano 26 Grudnia 2009 Szkoda, że to GEX, bo mi się nie chce instalować. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PsichiX Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 gnysku, nie załamuj :C Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Snake Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 http://gm.ultimatepronoun.com/index.php?page=DLLs :F Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 26 Grudnia 2009 Autor Udostępnij Opublikowano 26 Grudnia 2009 To z tego korzystałem, i wątek na największym (nie realtime tylko o 1 niższy) priorytecie wyrabiał mi niecałe 90 fps. Ale nie kojarzę ile wyrabiało na gmowym czasie, więc może to nie wina dlla była ;p Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Cekol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 A jakby wgryźć się w gma przez GMAPI i tam podmienić funkcję na te "Prawidłowo działające" ? :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PsichiX Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 mozna tak zrobic. Tylko o jakie funkcje konkretnie Ci chodzi? Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Cekol Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 current_time() date_current_time() i wszystko związane z czasem i fpsami :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PsichiX Opublikowano 26 Grudnia 2009 Udostępnij Opublikowano 26 Grudnia 2009 current_time to nie funkcja a zmienna wbudowana do ktorej GMAPI na chwile obecna nie ma dostepu. Co innego date_current_time(), ale zeby to mialo sens trzeba zrobic to jako extension do gm7 i w niego wpakowac DLL. moge to zrobic wieczorkiem Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Rekomendowane odpowiedzi
Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto
Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.
Zarejestruj nowe konto
Załóż nowe konto. To bardzo proste!
Zarejestruj sięZaloguj się
Posiadasz już konto? Zaloguj się poniżej.
Zaloguj się