Płynna animacja przy użyciu GAPI

  • 6 Odpowiedzi
  • 1547 Wyświetleń

0 użytkowników i 1 Gość przegląda ten wątek.

*

Offline Komame

  • 35
  • Płeć: Mężczyzna
  • Sprzęt: SE X1
Płynna animacja przy użyciu GAPI
« dnia: Kwiecień 02, 2006, 08:34:16 »
Witam. Od niedawna bawię się trochę eMbedded Visual C++. Dopóki była to zwykła zabawa przy użyciu funkcji GDI, nie było żadnego problemu. Schody pojawiły się dopiero w momencie, gdy chciałem zrobić port gry, którą parę lat temu napisałem na PC. W związaku z tym, że wystepują tam elementy animacji, z góry założyłem, że jedynym rozsądnym rozwiązaniem będzie wykorzystanie GAPI.
Wszystko OK - poza jedną sprawą - płynnością animacji. Nie mogę uzyskać idelanej płynności tak jak w innych grach na PPC, których dema można znaleźć wszędzie w sieci.
Zastosowałem najpierw timer, który w regularnych odstepach czasu (co 50 ms) miał odświeżać ekran. Jednak to okazało to się totalną klęską. Nawet, gdy sprzęt po prostu odtwarzał animacje (i nic poza tym - nawet nie był dodatykany) to i tak raz animacja chodziła szybciej, potem lekko zwalaniała potem znów przyspieszała - po prostu jej prędkość pływała.
W końcu zdecydowałem się na stworzenie osobnego wątku odpowiedzialnego za animacje, który to w petli w kółko (co nie jest porządnym programistycznie rozwiązaniem) co 50ms odświeżał ekran. Co prawada dało to już bardzo dobre rezultaty, gdyż animacja była rzeczywiście płynna, ale tylko do czasu wcisnięcia jakiegoś przycisku, np. w prawo. W tym momencie pojawiało się zachwianie na ok. 1 sekunde (lekkie lub większe drgania) po tym czasie animacja znów się stabilizowała. Jednak jeśli ma to być gra to nie może się przycisnać przy każdym naciśnięciu dowolnego przycisku.
W związku z powyższym moje pytanie brzmi - jak profesjonalnie rozwiązuje się ten problem?

Używam SPV M3000 - wiem, że sprzęt ten ma niezbyt szybki procesor, ale jednak są gry i programy, które chodzą na tym wręcz idelanie, więc istnieje jakiś sposób.

Z góry dziękuję za każdą, choćby trochę naprowadzającą odpowiedź.

Pozdrawiam
Piotr Kowalewski

*

Offline fp

  • **
  • 113
    • http://pdaclub.pl/forum/index.php?action=search
Płynna animacja przy użyciu GAPI
« Odpowiedź #1 dnia: Kwiecień 02, 2006, 21:44:03 »
założenie, że mozesz odświeżać ekran co 50 ms jest niepoprawne.

aby wszystko miało ręce i nogi musisz zrobić tak - wątek, który będzie odpowiedzialny za rysowanie powinien mieć pętle tego typu:

nTickPrev = GetTickCount();
while (..) {

   nTick = GetTickCount();
   Rysuj();

   while (nTick - nTickPrev >= 50) {
      Advance_Game_Logic( );
      nTickPrev += 50;
   }
}


funkcja GetTickCount() to funkcja WinAPI, która zwraca liczbę milisekund od uruchomienia systemu. chodzi o to, aby logika gry posuwała się do przodu średnio co 50 ms. nie możesz założyć, że będzie to dokładnie 50 ms, ponieważ system operacyjny może zawsze zostać spowolniony, np: przez operacje dyskowe. rysowanie natomiast na wolniejszych palmach może zająć więcej niż 50 ms, więc wtedy wszystko by chodziło wolniej. lepiej zrobić żeby chodziło mniej płynnie, a nie wolniej ;-).
można by w tę pętlę wrzucić jeszcze Sleep(10) - żeby wątek zasypiał na 10 ms po każdym rysowaniu, dzięki temu procesy systemowe dostaną chociaż jakiś ułamek czasu procesora i nie będą wisieć...

*

Offline Komame

  • 35
  • Płeć: Mężczyzna
  • Sprzęt: SE X1
Płynna animacja przy użyciu GAPI
« Odpowiedź #2 dnia: Kwiecień 02, 2006, 22:38:39 »
Własnie w ten sposób miałem to rozwiązane - pętla nieco inaczej skonstruowana, ale działanie identyczne. Mój problem z niestabilnością animacji leżał gdzie indziej - już go rozwiązałem. Jednak bardzo dziekuję za pomoc.

Z drugiej strony, chyba jeszcze lepiej byłoby (zamiast zakładać te 50ms w pętli) rozwiązać to tak:


while(Msg.message != WM_QUIT)
{
if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
else
{
Rysuj();
}
}


...a funkcję Rysuj() zacząć tak:


nTick=GetTickCount();
if(nTick-nTickPrev < 50)
return;
nTickPrev = nTick;
...


Dzięki temu funkcja rysująca nie będzie w ogóle zabierać czasu, gdy rysowanie jest nie wymagane w danym momencie i można to zrobić bez dodatkowych wątków (dodatkowy wątek okazał się zbędny, gdyż wcześniej popełniłem inny - głupi błąd).

*

Offline fp

  • **
  • 113
    • http://pdaclub.pl/forum/index.php?action=search
Płynna animacja przy użyciu GAPI
« Odpowiedź #3 dnia: Kwiecień 03, 2006, 07:00:00 »
Cytat: "Komame"

...a funkcję Rysuj() zacząć tak:


nTick=GetTickCount();
if(nTick-nTickPrev < 50)
return;
nTickPrev = nTick;
...




nie do końca zgodzę się. w Twoim rozwiązaniu jeśli gra w pewnym momencie zwolni i dojdzie do sytuacji gdy np: nTick-nTickPrev == 100, to logika gry posunie się o 1 krok do przodu, podczas gdy powinna o 2.
dlatego lepiej zamiast 'nTickPrev = nTick;' zrobić 'nTickPrev += 50;'.
zamiast 'if (..) return' który wychodzi z funkcji, zrobić 'while ' który mieli funkcję dopóki ma jeszcze "nieprzetworzone" klatki animacji/logikę gry.
no i wcisnąć tam gdzieś Sleep(5) albo Sleep(10), bo inaczej będziesz miał 100% wykorzystania CPU, co nie jest wskazane w urządzeniu mobilnym (bateria padnie szybko).

*

Offline Komame

  • 35
  • Płeć: Mężczyzna
  • Sprzęt: SE X1
Płynna animacja przy użyciu GAPI
« Odpowiedź #4 dnia: Kwiecień 04, 2006, 00:00:54 »
Cytat: "fp"
zamiast 'if (..) return' który wychodzi z funkcji, zrobić 'while ' który mieli funkcję dopóki ma jeszcze "nieprzetworzone" klatki animacji/logikę gry.
no i wcisnąć tam gdzieś Sleep(5) albo Sleep(10), bo inaczej będziesz miał 100% wykorzystania CPU, co nie jest wskazane w urządzeniu mobilnym (bateria padnie szybko).


Dlaczego Sleep() w funkcji rysującej i dlaczego brak tego ma spowodować obciążenie procka w 100%? Być może ja coś źle rozumiem, albo nie znam mechanizmu działania pętli komunikatów. Może mi wyjaśnisz: czy Twoim zdaniem ta pętla:

while(Msg.message != WM_QUIT)
{
   if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
   {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
   }
}

zajmuje 100% CPU?

Jeśli tak, to masz rację - w tej sytuacji muszę dodać owe Sleep() w funkcji rysującej (co z resztą już zrobiłem w moim programie, ale nie do końca jestem pewien czy tak MUSI być). Jesli natomiast powyższa pętla nie zajmuje 100% CPU to zauważ, że jedyna różnica pomiędzy tym, co widać powyżej, a tym co ja zrobiłem było tylko dodanie Rysuj() w momencie, gdy nie ma żadnego komunikatu do obsłużenia:

   ...
   if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
   {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
   }
   else
   {
      Rysuj();
   }
}

W dodatku Rysuj() wykonywane jest tylko raz (bez żadnego zapętlania się wewnątrz) i po tej jednej iteracji program ponownie powraca do obsługi komunikatów. W przypadkum, gdy nie minęło przynajmniej 50ms od ostatniego wywołania, funkcja Rysuj() wychodzi i powraca do pętli komunikatów natychmiast (co się zdaża w 90% przypadków wywołania tej funkcji), więc można by praktycznie stwierdzić, że prawie cały czas wykonuje się tylko pętla obsługi komunikatów. Dlatego właśnie nie dodałem tego Sleep() od razu, gdyż uważałem, że to tylko przedłuży czas wykonywania się funkcji Rysuj(). Ale być może jestem w błędzie.
Przepraszam, jeśli to jest jakiś podstawowy problem, którego nie rozumiem, ale w czystym WinAPI programuję dopiero od niedawna i prawdopodobnie jest jeszcze wiele mechanizmów, które źle rozumiem. Jeśli jestem w błędzie proszę o krótkie wyjaśnienie.

*

Offline fp

  • **
  • 113
    • http://pdaclub.pl/forum/index.php?action=search
Płynna animacja przy użyciu GAPI
« Odpowiedź #5 dnia: Kwiecień 04, 2006, 08:19:51 »
Cytat: "Komame"


while(Msg.message != WM_QUIT)
{
   if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
   {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
   }
}

zajmuje 100% CPU?



no tak - MSDN na temat funkcji PeekMessage mówi:
Unlike the GetMessage function, the PeekMessage function does not wait for a message to be placed in the queue before returning.

to tak jakbyś miał coś takiego:

a = 0;
b = 0;
while(a != 0)
{
   if(b != 0)
   {
      cośtamcośtam( );
   }
}



to będzie 100% CPU ;-)

*

Offline Komame

  • 35
  • Płeć: Mężczyzna
  • Sprzęt: SE X1
Płynna animacja przy użyciu GAPI
« Odpowiedź #6 dnia: Kwiecień 04, 2006, 21:32:48 »
Dziękuję za naukę. Moje wątpliwości zostały rozwiane ;)