ďťż

Ładny brzuch

Witam wszystkich!

Mam pytanie odnośnie Gry Eksperta. Otóż piłka porusza się trochę skokowo, a już na pewno nie jest to płynne. Czy da się to jakoś zoptymalizować?
Czytałem kiedyś, że w Builderze timer nie przybiera interwału mniejszego jak 10 ms. Może to dlatego?

Czy ma ktoś jakieś pomysły?



Z tym Timerem to ogólnie jest jakoś dziwnie. Jeśli da się odpowiednio małą liczbę oznaczającą częstotoliwość powtarzania kodu, to nijak ma się do rzeczywistaj częstotliwości. Dlaczego:?

Każdy dzień jest pierwszym dniem reszty twojego życia.

Dlatego że pewne operacje co w tym przypadku chodzi chyba konkretnie o rysowanie sceny (chociaz nie jestem pewnien jak to sie mialo do atrukulow w KSE bo wstyd sie przyznac :oops: nie czytalem tego :oops: ) ale tak domniemam ze własnie chodzi o rysowanie, tak wiec pewne operacje wymagala czasu i skoro zegar ma tykac co powiedzmy 10ms a pomiedzy przerwami odbywa sie przerysowywanie obrazu to to nie jest miarodajne bo przeciez kiedy jest rysopwane dluzej niz te 10ms to chociaz tykniecie zegara jest na czas to mimo tego rysowane blokuje wyslanie komunikatu (WM_TIMER), m.w. o to chodzi, wsystkie te operacje w jakims stopniu spowalniaja prace zegara, tykniecie zegara odbywa sie w watku glownym (w kazdym badz razie watek glowny ten komunikat przechwytuje). A w Windows to wyglada tak, ze jak jakis komunikat jest wysylany to trafia do tzw. kolejki komunikatow i w odpowiednim czasie zostaje obsluzony.

Generalnie znaczna poprawa wydajnosci (ten konkretny przypadek czyli gra jest pierwszorzednym materiałem na taka przerobkę) nastepoje po przenieiseniu czynnosci zwiazanych z obsluga sceny do oddzielnego wątku. Najlepiej wyrysowac sobie cala powierzchnie w oddzielnym watku i w odpowiednim momencie wrzucic to na scene. Jak to sie robi technicznie. Jest kilka sposobow. rowniez przy "wspoludziale" :) Timera:

1. Watek rysuje wszystko (oczywiscie konieczna nieskonczona petla while) potem przerzuca na scene i "usypia" na okreslony czas ( Sleep(10) ), po "samo"wybudzeniu powtarza tączynnosc od nowa;

2. Watek przygotowuje scene czyli rysuje wszystko na jakiejs bitmapie i kiedy przygotuje cala bitmape zasypia zupelnie (suspend) - wybudza go timer ktory co okreslony czas "tyka" (WM_TIMER) i kiedy wykryje ze watek rysujacy spi to go po prostu budzi wczasniej wyswietlajac bitmape na scenie (scena=okno).

3. wazne jest wykorzystanie poszczegolnych komunikatow. w przypadku pracy wielowatkowej lepszym wyjsciem jest modyfikaowanie jest wylacznie poszczegolnych zmiennych reprezentujacych wspolrzedne obiektow anizeli rzeczywiste umiejscowienie tych obiektow czy nawet rysowanie ich

4. no i oczywiscie jezeli juz koniecznie ma byc timer to najlepiej tzw. multimedialny ktory dziala niejako poza petla obslugi komunikatow i ogolnie bardzo fajnie sie z nim pracuje, wygodniej niz ze zwyklym timerem. Po prostu co okreslony czas wywolywana jest wskazana funkcja i tyle i dzieje sie to z bardzo duza precyzja.

5. a jesli chodzi o watki to w C++ uruchamiamy je za pomaca _begnithread(NazwaFunkcji,0,NULL) i koniecznie doalaczys naglowek <process.h> , w przypadku Buildera mozna sie pokusic od wyprowadzenie klasy z TThread, taka kalsa dziedziczaca to bardzo fajna rzecz, łatwa w uzyciu i bardzo czytelna. Obiekt TThread reprezentuje watek.

KoDo

Nie jestem pewny (correct me, if I am wrong), ale wydaje mi się, że dziwne zachowanie timera jest związane z architekturą samego Windowsa. Innymi słowy - małe przypomnienie z WinApi :) :

System Windows opiera się na komunikatach. Informują one konkretne okno (każde okno ma do siebie przypisaną procedurę, którą Winda wywołuje [tkzw. callback] ) o tym, że np. użytkownik kliknął przycisk "zamknij", chce zmienić rozmiar okna, czy nacisnął coś na klawiaturze. Te komunikaty ustawiane są w pewnego rodzaju kolejkę. Procedura okna je pobiera, przetwarza i usuwa po kolei, czyli pierwsze, które przyszły są też pierwszymi przetworzonymi.

Sęk w tym, że zegar także wysyła komunikaty do okna, i co gorsza, te komunikaty mają dość niski priorytet, czyli jeśli jakiś inny program otrzymuje komunikaty, lub użytkownik zacznie desperacko tłuc w klawiaturę :), to komunikat zegara ustawiony na interwał jednej sekundy może dojść nawet po dziesięciu. Jeszcze gorzej jest z niskimi interwałami, ie. 100 ms., bo w tedy nie ma praktycznie szans na dokładne otrzymywanie czasu.

Dobrym środkiem zaradczym było by zaimplementowanie własnego zegara wysokiej częstotliwości za pomocą Windowsowych funkcji QueryPerformanceCounter i QueryPerformanceFrequency. Jeśli kogoś to zainteresuje, to mogę podzrucić przykładowy kod.

Pozdrawiam.

//================================================

KĄESACP: Komputer Ąwiat - Expert Society Against Crap Posting
To join: Put these lines in your signature and don't post crap!



-> KoDo:
Ale po co komplikować sprawę wątkami?? Nie prościej uzywać zegara wysokiej częstotliwości??

Ja proponuje tenże zegar. Do jego używania potrzebne są następujące modyfikacje:

1) Hi - Res Timer :)
2) Współrzędne przechowujesz w zmiennych typu float.
3) Kod przetwarzający, ie. ruch piłki ładujesz do (kilku) funkcji o postaci typ_zwracany Funkcja ( float deltaTime). Zmieniamy kod zależny od czasu na zasadzie:

zamiast:
ball.x += ball.xvel;
jest:
ball.x += (ball.xvel * deltaTime);

itp.
6) Oczywiście kod przetwarzania i rysowania wstawiamy do maina, czy jak się zwie w Borlandzie Entry Point. Po prostu:

//gdzies tam
float time;
//w petli
time = CTimer::GetTime(); //czy jak to tam zaimplementujesz
//....
ProcessInput(); //klawiatura & stuff
UpdatePhysics(time); //fizyke uaktualniamy zgodnie z czasem
Draw(); //rysujemy

Całość wydaje mi się prostsza od wątków, tylko wymaga zmiany podejścia do pisania gry (pętla główna). I dzięki temu fizyka jest uniezależniona od częstotliwości wykonywania programu.

Aaa, no - bo zszedłem z tematu - funkcja rysująca MUSI być niezależna od czasu; program w ogóle nie powinien zatrzymywać się czekając na komunikaty zegara, czy jakieś inne.

//================================================

KĄESACP: Komputer Ąwiat - Expert Society Against Crap Posting
To join: Put these lines in your signature and don't post crap!

W zasadzie to niby racja, ale zapomniales o jednym istotnym szczegole.W tym konkretnym przypadku masz doczynienia wylacznie z funkcjami interfejsu GDI. Osobiscie uwazam ze podsystem ten jest dosc wydajny... ale w przetwarzaniu obrazów statycznych i wolnozmiennych. W zupelnosci nie nadaje sie do przetwarzania obrazow szybkozniennych a model aplikacji do takich zastosowań w największym uproszczeniu jest zgodny z twoim "wyobrażeniem", powtarzam "w ujęciu najprostszym" - w pewnych kwestiach nastepuje jakas tam zgodność - tutaj polecam odwołać sie do "Wprowadzenia do grafiki komputerowej" Foley'a ktory model aplikacji doskonale opisuje i model ten jest zgodny i stosowany do dzis w grach czy symujacjach komputerowych - oczywiscie rozszerzono go o pewne elementy m.in. własnie wątki;).
Ale nie odbiegam od tematu:) Twój punkt widzenia oczywiscie jest zupełnie poprawny, ale irracjonalny w zastosowanich GDI. Odzczyt io oraz uaktualnianie zmiennych blokuja w dalszym ciągu proces rysowania ktory i tak z racji programowych mozliwości podsystemu graficznego nie przebiega płynnie.Poza tym rysowanie odbywa się w wątku głównym, w tym samym który przetwarza komunikaty, w tym samym w krótym obsługa tych komunikatów jest realizowana. Oczywiscie mozna zrezygnować z generownia jakichkoliwek komunikatów ale czy to ma sens, aplikacja traci znacznie na funkcjonalności a system i tak będzie rozsyłał do twojej aplikacji wszystkie komunikaty tak jak byc powinno, to że ty w procedurze okna nie zamieścisz ich obsługi nie znaczy że pętla nie będzie przywoływana - wprost przeciwnie, każdorazowo odebranie komunikatu przez program powoduje wywołanie procedury okna. Oczywiście mozna posunąc się dalej i stworzyć takiego mutanta co to w ogóle komunikatów nie chce odbierać (mówię o czystym APi Windows w tym momencie) i zdaje mi sie że takie coś własnie proponujesz, OK. To mam tego zmutowanego mutanta ;) no i teraz widzisz musze sobie sam zadbać zeby odczytać klawiaturę , żeby odczytać mysz, zeby odczytac stan okna, jego rozmiar, położeni, obsługę przynajmniej podstawowych polecen jakie mógłbym wydać oknu aplikacji, i wiele wiele innych zeczy w zalezności od potrzeb. I co teraz? Ano okazuje się że ten cały karkołomny wysiłej jest niepotrzebny bo wszystko co miało chodzic szybciej z racji ogromnych nakładów związanych z obsługa io i dodatkowo z obsługą sceny chodzi o wiele wolniej.
Ale... jakas tam część prawdy w tym co mówisz jest. W ten m/w sposób realizowane są aplikacje OpenGL, Direct3D czy DierctDraw (czy jeszcze ktoś tego uzywa??) - dokładnie to jest tak, obsługa komunikatów wystepuje swoja drogą, ew. aby jeszcze było jeszcze wydajniej obsługiwane sa tylko wybrane komunikaty, a do rysowania wykorzystywany jest tzw. stan jałowy aplikacji - jest to taki stan kiedy program nic nie robi (ścislej nie przetwarza żadnych komunikaótw) oczywiście aby mozna był uzyskac satysfakcjonujące winiki konieczna jest zmiana mechanizmu przetwarzania komunikatów i Windows daje taka mozliwość - skutkiem tego jest w pierwszej kolejności rysowanie sceny, drugorzędne staje się przetwarzanie komunikatów - w praktyce jednak ich obsługa jest tak mało absorbująca że staje się niezauważalna a jednoczesnie wystepuje prawie zawsze kiedy tego oczekuje uzytkownik dzieki czemu animacja nabiera niesamowitej płynności.
to teraz tak: czym rózni sie ten model opisany przeze mnie od tego co przedstawiłes ty? W moim przypadku wykorzystałem możliwości jakie daje mi system. Czy tego chcesz czy nie on i tak będzie do twojego okna rozsyłał komunikaty, mało tego razem z komunikatem podeśle ci gotowy zestaw danych, zmiennych (np. przy WM_MOUSEMOVE dostajesz pozycje myszy która w twoim przypadku kazdorazowo musi zostac wyliczona) - system i tak będzie dla ciebie liczył te dane, nie rozumiem jak mozna rezygnowac z takiego prezentu i robić to samemu znacznie wolniej drugi raz .
Ponadto tak jak powiedziałes funkcja rysująca musi być niezalezna od czasu i komunikatów, a pokazujesz przykład zupełnie odwrotny. Ta niezalezność w wątku głównym daje jedynie obsługa stanu jałowego.
a wracając do technicznej implementacji wątku to nie wydaje mi się żeby było to szczególnie skąplikowane:

void mythread(PVOID) {
while(true) {
//moze byc rysowanie itp
}
}

int WinMain(......){
.
.
.
//utworzenie okna
_beginthread(mythread, 0, NULL); //<--tu powstaje watek
}
LRESULT CALLBACK WndMainProc(....) {
//procedura obslugi komunikatow
}
Rownie dobrze mozna tworzyc wątek w WM_CREATE okna lub po kliknieciu np. w Buttona. Oczywiscie przed utworzeniem wątku rysującego trzeba mu dostarczyc pewnych danych tj. kontekst urządzenia, rozmiary etc, etc. I co najwazniejsze procedura rysujaca jest fizycznie odseparowana od procedury obliczeniowej i odczytu wejscia.
A poza tym co to znaczy używac High-Resolution Timer - i tak trzeba zorganizowac jakis sposob odliczania i wlwolywania funkcji zwrotni (pętla) to że urzyjesz zegara wysokiej precyzji wcale nie przyspieszy działania programu i nie ograniczy nieprzyjemnych dla oka efektów migotania obrazu, nie poprawi płynności - to tylko jakis tam sposób odliczania czasu i tyle i nijak sie to ma do Timera Windows - ten drugi to po prostu jeden z mechanizmów windows badz co badz bazujący na High-Resolution Timer. High-Resolution Timer to sprawa sprzetowa, to ze jest inkrementowana jakas tam liczba to fajnie ale nic poza tym, jest dobre w przypadku amatorskich pomiarow wydajnosci(do profesionalnych zastosowań mam trzy linijki assemblera ktore dają mi dokładność wyzszą o kilka rzędów a które bez problemu mozna wykuc na blache:))):
asm
{
dw 310Fh
mov tl, eax
mov th, edx
}
Jesli juz jestesmy przy zegarach timerach itp to ciekawy moim zdaniem jest zegar multimedialny dostepny w Window, działa niejako poza pętlą komunikatów (patrz. mmsystem.h - timeSetEvent i timeKillEvent) zezwala na bardzo małe interwały a co najwazniejsze jest bardzo precyzyjny. Jezeli ktos bedzie chcial mage wrzucic jakis przykladzik.

Jesli chodzi o Buildera to w moim przekonaniu metody dostepne m.in. w klasie Canvas nie uwazam za udana ,choc bardzo przystepne i wygodne to jednak mało efektywne, przynajmniej nie tak bardzo jak tradycyjne wywolania GDI i najgorsze ze to spowolnienie widać. Polecam wiec korzystanie z tego drugiego choc znacznie trudniejszego i zastawiającego szereg (oj.. duzo duzo) pułapek na programistę:)

A tak juz zupelnie na sam koniec to oczywiscie kazdy bedzie realizowal tego typu rzeczy wg swojego uznania i wg wlasnych doswiadczen czy przekonan. Oczywiscie kazdy sposob jest w jakims tam stopniu dobry i kazdy ma swoje wady i zalety - nic nie jest do wszystkiego,a wszystko zalezy od potrzeb i stawianych wymagan :). Na pewno bagaz własnych doswiadczen pozwolą każdemu programiscie niezaleznie wypracowac sobie wsoj własny model implementacji tego czy innego problemu.

Ale nudze :o

KoDo

Hmn, tak was czytam i nie wiem czy dobrze trafiłem (chodzi mi o poziom :))
Zaczynacie tu mówić o rzeczach o których troszkę trudnych. Chciałem się nauczyć przy pomocy Eksperta mechanizmów tworzenia takich aplikacji od podstaw, i liczyłem na to, że ktoś mi to w prosty sposób wytłumaczy.
Powiedzcie mi czy mam rację.
Albo Timer nie może dawać sygnału w interwale mniejszym niż 10 ms, albo (co wg mnie bardziej prawdopodobne) generowanie obrazu trwa więcej niż 10ms. Ten drugi przypadek wydaje mi się bardziej realny, bo w sekundzie interwał co 10ms dawałby 100 impulsów, czyli 100 klatek na sekundę. Takiej częstotliwości odświeżania ta piłeczka na pewno nie ma. Z tego wnioskuję, że generowanie obrazka trwa znacznie dłużej.

W związku z tym pytanie: nie można tego zrobić szybciej???
Pisaliście coś o wątkach itp. czy moglibyście dać jakieś linki gdzie użycie takich technik jest w przystępny sposób opisane?
A może redaktorzy Eksperta spróbowaliby napisać artykuł o optymalizacji, połączonej z innymi technikami programowania. Mam cichą nadzieję, że artykułem o debugowaniu błędów nie zakończyli wątku Buildera i nie spoczęli na laurach, ale będą temat dalej kontynuować :)

Oczywiscie 100 klatek to na pewno nie wyciagniesz w GDI nawet jak bys tylko przerysowywal puste powierzchnie. Po prostu ograniczenia interfejsu i nakład czasu jaki pochłania przywołanie konkretnych metod na to nie pozwalają a poza tym jak słusznie TeMPOraL zauwazył komunikat WM_TIMER ma dość niski priorytet i stąd takie rozbierzności w częstotliwościach.
Ja zasugerowałem juz uzycie bardziej precyzyjnego timera tzw. multimedialnego. Jesli chcesz mogę zucic kodem, ale zasadnicze pytanie jak to ma wyglądać. Czy miałby to byc kod w czystym API czy typowo pod Buildera? Poza tym moge jesli chcesz podac inny przykład typowo pod gierki DirectDraw i 3D ale w GDI tez uzyteczny tylko że w czystym API.

KoDo

To się nazywa wyczerpująca krytyka mojej wypowiedzi :) Oczywiście, KoDo ma 100 % racji; przedstawione prze ze mnie podejście jest charakterystyczne raczej dla gier jako gier; o tym, że wyświetlanie w Api jest takie wolne wiedziałem, ale jakoś przy pisaniu zapomniełem ( wiem, :lame: ). A wątki to rzeczywiście bardzo dobre rozwiązanie przy pisaniu gier/programów w Poor - WinApi, ale jako że z nich niewiele korzystam, to się na ich temat nie wypowiadam.

Co do pętli komunikatów, to nigdy nie byłem zwolennikiem jej masowego używania; osobiście korzystam tylko z WM_ACTIVATE, KEYDOWN/KEYUP, MOUSEMOVE, CLICK (czy jak to się nazywa :) ), SYSCOMAND, CLOSE i DESTROY . Ewentualnie jeszcze WM_COMMAND jak jest jakieś menu. U mnie po prostu działanie pętli komunikatów sprowadza się do poustawiania zmiennych na swoje miejsca; resztą zajmie się część update w pętli głównej.

Dalej, uważam także, że to teoretyzowanie w Expercie na temat gier w Borlandzie powinno szybko przejść w kierunku Direct Draw - tam można pisać gry, które nie skaczą :)

A co do profilingu, to wydaje mi się, że gdzieś już ten kod z ASM widziałem....

DWORD dwLow, dwHigh;
__asm{
rdtsc
mov dwLow, eax
mov dwHigh, edx
}
//-----

(Perełki Programowania Gier, Tom II)

A na zakończenie, mówiąc o niezależności rysowania od czasu i komunikatów, miałem na myśli to, że program rysuje scenę co każdy obieg pętli, nie czekając na żadne komunikaty (sprawdzając, ale nie czekając), i że nie należy łączyć kodu czasu z rysowaniem.

A tak w ogóle, to:

PUKI PISZECIE W GDI TO KoDo NIE MNIE SŁUCHAJCIE I Z WŚTKÓW KORZYSTAJCIE

Pozdrawiam.

//================================================

KĄESACP: Komputer Ąwiat - Expert Society Against Crap Posting
To join: Put these lines in your signature and don't post crap!

KoDo jak byś mógł to rzuć kodem pod Buildera. Za przykłady gier też byłbym wdzięczny.
Na dzień dobry chciałbym zrobić płynne(!) skrolowanie tekstu, tak jak na filmach, gdzie pod koniec jest wyświetlana obsada. Oczywiście w okienku. Próbowałem zmieniać po prostu Top komponentu Label ale dobrze to nie wychodzi...

  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • zsf.htw.pl
  •