Autor Wątek: Java ME - poruszanie się obiektu klasy Sprite  (Przeczytany 1853 razy)

Offline LinK

  • Użytkownik

# Styczeń 18, 2011, 15:09:44
Witam wszystkich,
Mam pewnien problem z którym męcze się już kilka godzin. Mianowicie chodzi o poruszanie się obiektów wcześniej wymienionej klasy. Obiekty te są wstawiane w plansze za pomocą metody która odczytuje ich współrzędne a następnie ładuje do tablicy. Ale z tym jest wszystko w porządku. Mój problem polega na tym, że poruszanie się tych obiektów powinno wykonywać się w taki sposób, że gdy dojdzie do pewnego miejsca, wraca i porusza się do początku i tak w kółko. W jedną strone wszystko działa tak jak powinno natomiast w drugą strone jest już gorzej.
Poniżej umieszczam fragment kodu metody postep(int licznikGry) w której jest wykonywane przeskakiwanie klatek odpowiednich obiektów poprzez wywołanie odpowiedniej metody. To tak po krótce jeżeli chodzi o tą metode. Obiekt klasy Sprite ma się poruszyć o długość równą trzech kafelków, który ma rozmiar 24x24 i z powrotem. Proszę o wszelkie sugestie, jeżeli coś jest nie zrozumiałem to proszę pytać.
//Poruszanie się wszystkim przedmiotom
        public int postep(int licznikGry) {
                boolean poruszaj = true;
                int retWartosc = 0;
               
                for(int i = 0; i < monety.length; i++) {
                        monety[i].postep(licznikGry);
                }
                for(int j = 0; j < wrogowie.length; j++) {
                        wrogowie[j].postep(licznikGry);
                }
                while(poruszaj) {
                        if(wrogowie[1].getX() != (SZEROKOSC_KAFELKA*3)) {
                                wrogowie[1].setTransform(Sprite.TRANS_MIRROR);                               
                                wrogowie[1].move(-1, 0);
                               
                        } else if(wrogowie[1].getX() + SZEROKOSC_KAFELKA != SZEROKOSC_KAFELKA*6) {
                                wrogowie[1].setTransform(Sprite.TRANS_NONE);
                                while(wrogowie[1].getX() + SZEROKOSC_KAFELKA != SZEROKOSC_KAFELKA*6) {
                                        wrogowie[1].move(1, 0);                                       
                                }
                        }
                        poruszaj = false;
                }                       
                setPotrzebaRepaint();
                return(retWartosc);


W tym przypadku gdy próbuje poruszyć postać w prawą stronę to przeskakuje ona bardzo szybko bez wykonania przesunięcia w odpowiednim tępie. Zaznaczę że plansza składa się z tablicy 16x16 kafelków z czego każdy ma rozmiar 24x24.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Styczeń 18, 2011, 16:27:54
Jak masz while w środku, to czemu się dziwisz? :)

Offline Lerhes

  • Użytkownik

# Styczeń 18, 2011, 16:36:32
W twoim kodzie nie rozumiem nic. Miałeś wkleić kod odpowiedzialny za wyświetlanie klatek, a wkleiłeś coś, co wygląda za sprawdzanie kolizji. Jak już wklejasz kod, to opowiedz o nim coś, albo go bogato opatrz komentarzami. Kiedyś napisałem coś, co działało całkiem sprawnie, to Ci tutaj mogę wkleić:

_frames to tablica wektorów zawierająca SDL_Rect które określają kolejne klatki animacji z tileseta. Kod napisałem tak, że nieważne ile masz klatek do pokazania, ważne, że na wyświetlenie całej animacji masz _timeForOneJump = 1.0; czyli sekundę (tą wartością można manipulować). _frame to tablica wektorów, tablica jest cztero-elementowa  bo są cztery kierunki, a wektory mają tyle elementów ile jest klatek animacji w danym kierunku. Czyli jeszcze raz: Mamy we _frame klatki animacji dla każdego z kierunków upchane w wektorze. Teraz podajemy do funkcji deltaT z pętli głównej, a chcemy dostać prostokąt SDL_Rect który użyjemy do wyświetlenia obrazka z tilesetu. Prosto i przyjemnie, kod nie jest bardzo trudny, trochę matematyki:

SDL_Rect* zaba::getRectangle(double deltaT)
{
static double actualTime = 0.0;
if(frogMove)
{
actualTime += deltaT;
//Animacja zakonczona
if(actualTime >= _timeForOneJump)
{
frogMoveBetweenFields = false;
actualTime = 0.0;
//Frame zero to sprite pokazujacy stojaca zabe
return &_frames[_direction][0];
}
int framesCount = _frames[_direction].size() * 2 - 2;
int actualFrame = static_cast<int>( actualTime * framesCount / _timeForOneJump + 1.0 );
//Jezeli przez przypadek wybiegnie czasem poza liczbe klatek, to zwracamy juz stojaca zabe
if(actualFrame > framesCount ) return  &_frames[_direction][0];
if( static_cast<std::size_t>( actualFrame ) >= _frames[_direction].size() )
{
//Jezeli wyjdziemy poza tablice frame, to zaczynamy cofac sie z klatkami
actualFrame = (  _frames[_direction].size() - 2 ) - ( actualFrame % _frames[_direction].size() );
}
return &_frames[_direction][actualFrame];
}
return &_frames[_direction][0];
}

Mam nadzieję, że coś zrozumiesz. Ten kod był napisany do froggera, to też może być pomocne. Jeżeli masz stałą ilość klatek, to można go uprościć.

Ogólnie zrobione w inny sposób masz tutaj:
http://lazyfoo.net/SDL_tutorials/lesson20/index.php
Lerhes
« Ostatnia zmiana: Styczeń 18, 2011, 16:38:10 wysłana przez Lerhes »

Offline LinK

  • Użytkownik

# Styczeń 18, 2011, 16:56:37
Zacznę od tego, że nie mam problemu z wyświetlaniem poszczególnych klatek animacji, nie w tym jest problem. Metoda którą wrzuciłem odpowiada za odmalowanie całej planszy gry aby była widoczna zmiana klatek animacji. Czyli np. mam tablice obiektów klasy ZasobyGraficzne o nazwie monety[] i na jej rzecz wywołuje metodę o nazwie ZasobyGraficzne.postep(int) - wysietla animacje tego obiektu czyli animacja obracania się monety. Kolejno sytuacja wygląda podobnie z tablicą obiektów klasy Postac czyli wrogowie[], tu również nie ma problemów.
Natomiast problem jest w tym że  obiekt tablicy wrogowie[] nie porusza się w odpowiedni sposób, ponieważ cała ta metoda którą jest w klasie ManagerPoziomu.postep() i którą wrzuciłem dziedziczy po LayerManager jest wywoływana w tak jak by głównym wątku i wszystkie animacje nie licząć głównej postaci są uzależnione od tej właśnie metody.
Pytanie do was jest następujące: W jaki sposób mogę poruszyć obiekt tablicy wrogowie[] po planszy np. o 72piksele w lewo, a następnie 72 piksele w lewo? Animację w między czasie zapętlają się i wszystko fajnie wygląda.
POZDRAWIAM

Może teraz trochę prościej opisze mój problem. Poniżej wstawiłem kod który zmienia sposób poruszania, to znaczy powinnie się poruszać, ale tak nie jest.
//Poruszanie się wszystkim przedmiotom
public int postep(int licznikGry) {
boolean poruszaj = true;
int retWartosc = 0;
int kafelek2;

for(int i = 0; i < monety.length; i++) {
monety[i].postep(licznikGry);
}
for(int j = 0; j < wrogowie.length; j++) {
wrogowie[j].postep(licznikGry);
}

//????????????????????????????????????????????????????????
int kafelek = wrogowie[1].getX();
if(kafelek > (SZEROKOSC_KAFELKA*3)) {
wrogowie[1].setTransform(Sprite.TRANS_MIRROR);                               
                 wrogowie[1].move(-1, 0);
}

if(kafelek + 24 != (SZEROKOSC_KAFELKA*6)) {
wrogowie[1].setTransform(Sprite.TRANS_NONE);                               
            wrogowie[1].move(1, 0);

}
//???????????????????????????????????????????????????????
                 setPotrzebaRepaint();
return(retWartosc);
}


Obiekt nie porusza się tylko stoi w miejscu. Przyczyną tego jest, to że za każdym razem jak tylko metoda zostanie wykonana to za pierwszym razem poruszy się o ten jeden piksel w LEWO, a zaraz po tym sprawdzany jest warunek i porusza się o jeden w PRAWO więc postać stoi w miejscu. Teraz jak to zrobić aby przed sprawdzeniem drugiego if'a, który sprawdza położenie wroga wykonał się cały pierwszy if(kafelek > (SZEROKOSC_KAFELKA*3)).
POZDRAWIAM
« Ostatnia zmiana: Styczeń 18, 2011, 17:19:38 wysłana przez LinK »

Offline Lerhes

  • Użytkownik

# Styczeń 18, 2011, 17:42:50
Aha! Tobie chodzi o poruszanie sprita trzy klatki animacji w prawo, a nie przeskoczenie trzech klatek. Teraz rozumiem co chcesz uzyskać, trochę to źle opisałeś.

Dużo problemów widzę z tym kodem, odnosił się będę do tego co wkleiłeś na początku, bo potem to skombinowałeś jeszcze bardziej.

Czyli w tym kodzie na początku działało to tak : Wróg startował, szybko "biegł" do prawej strony i tam już utykał. No cóż, nic w tym dziwnego, w ogóle nie masz synchronizacji z licznikGry.
Ten licznikGry to jest po prostu liczba klatek obliczona przez timery? (Korzystaj z metody synchronizacji deltaT czy timery?)
Raczej ta druga opcja, więc na niej się skupmy.

Nie jestem pewien, bo całego kodu nie mam i nie do końca wiem jak on jest zbudowany, ale wydaje mi się, że powinieneś mieć raczej coś takiego (mówię o samym przesuwaniu):

//true w prawo false w lewo
static bool kierunekChodzenia = true;
static int ilePrzeszedl = 0;
for(int i = 0 ; i <  licznikGry; i++)
{
if(kierunekChodzenia)
{
//Tu trzeba sprawdzic czy nie doszedl do miesjca gdzie ma zawrocic. Rozumiem ze getX to pozycja na planszy?
//To wtedy SZEROKOSC_KAFELKA*3 = 24 * 3 = 72 No i jak to niby ma sparwdzic czy doszedl do konca?
//if( wrogowie[1].getX() == (SZEROKOSC_KAFELKA*3) )
//Ja bym zrobil raczej cos takiego
ilePrzeszedl++;
if(ilePrzeszedl ==  SZEROKOSC_KAFELKA*3) kierunekChodzenia = false;
wrogowie[1].setTransform(Sprite.TRANS_MIRROR);                               
        wrogowie[1].move(1, 0);
}
else
{
ilePrzeszedl--;
if(ilePrzeszedl ==  0) kierunekChodzenia = true;
wrogowie[1].setTransform(Sprite.TRANS_MIRROR);                               
        wrogowie[1].move(1, 0);
}
}
Jak coś jest niejasne to pytaj. Nie jest to może coś optymalnego, ale powinno działać (nie mam jak sprawdzić).
Jeżeli by chodził za szybko, to możesz przesuwać nie co każdą klatkę tylko na przykład co 20, albo uzależnić to od prędkości wrogów.
Lerhes
« Ostatnia zmiana: Styczeń 18, 2011, 17:44:46 wysłana przez Lerhes »

Offline LinK

  • Użytkownik

# Styczeń 18, 2011, 21:55:51
Wielkie dzięki o to właśnie mi chodziło. Musiałem dostosować to do moich potrzeb, ale właśnie tego mi brakował. Jak zaznaczyłeś licznikGry zastąpiłem wartością zwrotną z metody, która ma regulować wyświetlanie się 20 klatek na sekundę. Poniżej kod tej funkcji.
static long getWaitTime() {
long retWartosc = 1;
long roznica = System.currentTimeMillis() - ostatniCzasOdswierzania;
if(roznica < 50) {
retWartosc = 50 - roznica;
}
return(retWartosc);
}
Gdzie:
ostatniCzasOdswierzania = System.currentTimeMillis();

Metodę tą wziąłem z pewnego przykładu, ale nie bardzo wiem na jakiej zasadzie ona działa. Wiem jedynie że metoda System.currentTimeMillis() zwraca aktualny czas zegara w milisekundach. Udało mi się również spowolnić tempo animacji. Jeszcze raz wielkie dzięki!!!
« Ostatnia zmiana: Styczeń 18, 2011, 23:48:19 wysłana przez LinK »