Witamy na forum PC Format Zapraszamy do REJESTRACJI


Użytkownicy przeglądający ten wątek: 1 gości

Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.

#1
Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
A więc napisałem cały kod za radą użytkownika, który radził mi stworzyć cały menadżer do aplikacji tj.


Cytat:No to inaczej: wydziel część odpowiedzialną za animację do osobnej klasy abstrakcyjnej z metodami reset i nextFrame oraz wskaźnikiem na postać + zrób podklasę JumpAnimation, wtedy będziesz miał wszystko ładnie opakowane.


Mniej-więcej tak:


class Animation {
private:
Player* player;
 
protected:
unsigned int frameId; // klatka animacji
 
public:
virtual bool nextFrame();
 
void reset(); // ustawia `frameId` na `0`
 
void setPlayer(Player* player);
Player* getPlayer();
}




class JumpAnimation: public Animation {
 
bool nextFrame() {
player->position->x = frameId * jakieś tam równanie okręgu;
player->position->y = frameId * jakieś tam równanie okręgu;
frameId++;
 
return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
}
}



A wszystko wygląda tak:

Animation.h: http://wklej.org/id/1727705/
Animation.cpp: http://wklej.org/id/1727709/

W Engine.h jest pole:

Kod:
public: AnimationManager* getAnimationManager();

, a także metoda do wywoływania funkcji.

Kod:
AnimationManager* Engine :: getAnimationManager()

{
    return &anim;
}

A więc mam w pliku 'Engine.cpp' w if'ie na event jakim jest wciśnięcie klawisza 'Space' takie wywoływanie animacji (tak to robię):

Kod:
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Space)

            {

                   

                Player* gr_1 = &player;
                Animation *jump =  new JumpAnimation;
                jump->setPlayer(gr_1);
                 engine->getAnimationManager()->addAnimation(jump);
                    }

I problem tkwi w tym, że logi w konsoli pojawiają się zaraz po naciśnięciu spacji, a 'przesunięcie jakie jest ustalone w animacji' nie jest płynne klatka po klatce, tylko po prostu jeśli wcisnę jakiś inny klawisz, postać przesuwa się natychmiastowo o tą ilość jednostek, które ustaliłem.

I ktoś mi mówił, że mam dodać timer do processAll, ale ja nie wiem gdzie konkretnie, moglibyście pomóc. I chciałem spytać o dźwięki, czy też będzie potrzebny podobny menadżer, czy mogę je dodawać właśnie w deklaracjach klas poszczególnych animacji?
 System operacyjny: windows_seven Przeglądarka: firefox
#2
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
Wspominałem o tym kiedyś - powinieneś być w stanie sprawdzić, czy gracz aktualnie nie wykonuje skoku (czy jednokrotne wciśnięcie spacji różni się od przytrzymania jej?). Poza tym masz wycieki pamięci w swoim kodzie - trzymanie wskaźników w vectorze nie jest dobrym pomysłem, jeśli nie pamięta się o zwolnieniu pamięci.
Metodę processAll chyba możesz wywoływać w pętli głównej (ew. odmierzać czas, żeby program działał podobnie na różnych maszynach) - przed pętlą główną powinieneś stworzyć zegar (obiekt klasy sf::Clock) i w środku odmierzać czas (wywołując metodę getElapsedTime) i w momencie przekroczenia czasu o daną długość powinieneś wywołać metodę processAll i zresetować zegar.
 System operacyjny: linux_ubuntu Przeglądarka: firefox
#3
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
(02.06.2015, 20:16)Szachista napisał(a): Wspominałem o tym kiedyś - powinieneś być w stanie sprawdzić, czy gracz aktualnie nie wykonuje skoku (czy jednokrotne wciśnięcie spacji różni się od przytrzymania jej?). Poza tym masz wycieki pamięci w swoim kodzie - trzymanie wskaźników w vectorze nie jest dobrym pomysłem, jeśli nie pamięta się o zwolnieniu pamięci.
Metodę processAll chyba możesz wywoływać w pętli głównej (ew. odmierzać czas, żeby program działał podobnie na różnych maszynach) - przed pętlą główną powinieneś stworzyć zegar (obiekt klasy sf::Clock) i w środku odmierzać czas (wywołując metodę getElapsedTime) i w momencie przekroczenia czasu o daną długość powinieneś wywołać metodę processAll i zresetować zegar.

Przecież po wykonaniu się danej animacji, z wektora zaraz jest usuwana. Nie rozumiem reszty gdzie konkretnie w jakim pliku mam stworzyć zegar i odmierzać czas? Gdzie dodać ten warunek, przy wywoływaniu funkcji? Czy może w deklaracji processAll, a może jeszcze gdzieś?
 System operacyjny: windows_seven Przeglądarka: firefox
#4
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
Zauważ, że usuwasz wskaźnik, a nie to, na co on pokazuje, czyli tracisz dostęp do obszaru pamięci.
W pętli głównej powinno się to znaleźć (na pewno masz gdzieś tę konstrukcję):
Kod:
while (window.isOpen())
{
    //
}

Masz tu przykład kwadratu obracającego się co ok. sekundę:
Kod:
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
    sf::RectangleShape rectangle(sf::Vector2f(20, 20));
    sf::Clock clock;
    sf::Event event;

    rectangle.setPosition(sf::Vector2f(window.getSize() / 2u));
    rectangle.setOrigin(rectangle.getSize() / 2.f);
    rectangle.setFillColor(sf::Color::Green);
    while (window.isOpen())
    {
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }
        window.clear();
        if (clock.getElapsedTime().asSeconds() >= 1)
        {
            rectangle.rotate(30);
            clock.restart();
        }
        window.draw(rectangle);
        window.display();
    }
    return 0;
}
 System operacyjny: linux_ubuntu Przeglądarka: firefox
#5
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
(02.06.2015, 20:58)Szachista napisał(a): Zauważ, że usuwasz wskaźnik, a nie to, na co on pokazuje, czyli tracisz dostęp do obszaru pamięci.
W pętli głównej powinno się to znaleźć (na pewno masz gdzieś tę konstrukcję):

Animation.h: http://wklej.org/id/1728075/

Engine.h: http://wklej.org/id/1728076/
Player.h: http://wklej.org/id/1728074/
Game.h : http://wklej.org/id/1728073/

Animation.cpp: http://wklej.org/id/1728078/
Engine.cpp: http://wklej.org/id/1728079/
Player.cpp: http://wklej.org/id/1728083/
Game.cpp: http://wklej.org/id/1728080/
Main.cpp: http://wklej.org/id/1728081/

Zajrzyj do game.cpp i engine.cpp

W game.cpp mam taką pętlę, ale tyczy się ona tylko gdy jest odpalone 'menu, no i z game.cpp nie mam dostępu do wywołania tej funkcji. Ja ją właśnie wywołuję w engine.cpp - tam możesz też zobaczyć jak się to odbywa.

Nadal nie wiem czy timer ma się tyczyć po prostu wywołania funkcji processAll, czy jej deklaracji czy czegoś innego - ponadto w podobny sposób co podałeś użyłem timera do animacji chodu, ale to coś innego.

Kod:
/*Player.cpp*/

void Player::update(sf::Vector2f mysz)
{

    sf::Time t2 = anim_clock.getElapsedTime();
    if (anim_clock.getElapsedTime() > sf::seconds(0.09f))
    {
        //std::cout << t2.asSeconds() << std::endl;
        if (status == STOJ) return;
        if (frame < 5 /*liczba klatek animacji*/)
            frame++;
        else
            frame = 0; //zapetlanie animacji

        sprite.setTextureRect(IntRect(frame * 32, 0, 32, 34));
        //x, y, szerokosc, wysokosc
        anim_clock.restart();
    }
}

I dokładnie o co Tobie chodzi z tą pamięcią? Przecież ja tworzę tylko raz obiekt danej klasy animacji, a nie niewiadomo ile razy, a dodaję właśnie tylko wskaźniki do wektora, a nie same obiekty, więc chyba jest jeden obiekt?
 System operacyjny: windows_seven Przeglądarka: firefox
#6
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
Po co instrukcja
Kod:
Engine *engine = new Engine();
? Nawiasem mówiąc masz kolejny wyciek pamięci, bo nigdzie nie niszczysz obiektu (EDIT: formalnie to to nie jest wyciek, ponieważ do końca działania programu jest dostęp do pamięci).

Za każdym razem, kiedy warunek
Kod:
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Space)
jest spełniony, tworzysz nowe obiekty
Kod:
Animation *walk = new Animation();
//
Animation * chod = new JumpAnimation;
które żyją do końca programu (nigdzie nie ma jawnego zniszczenia ich przy pomocy operatora delete).
Wywołanie metody processAll (nie rozumiem, dlaczego robisz to na rzecz obiektu *engine) wyciągnąłbym z tego warunku i umieścił poniżej instrukcji
Kod:
window.clear()
(w pliku Engine.cpp).
 System operacyjny: linux_ubuntu Przeglądarka: firefox
#7
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
(03.06.2015, 17:39)Szachista napisał(a): Po co instrukcja

Kod:
Engine *engine = new Engine();
? Nawiasem mówiąc masz kolejny wyciek pamięci, bo nigdzie nie niszczysz obiektu (EDIT: formalnie to to nie jest wyciek, ponieważ do końca działania programu jest dostęp do pamięci).

Za każdym razem, kiedy warunek

Kod:
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Space)
jest spełniony, tworzysz nowe obiekty

Kod:
Animation *walk = new Animation();
//
Animation * jump = new JumpAnimation;
które żyją do końca programu (nigdzie nie ma jawnego zniszczenia ich przy pomocy operatora delete).
Wywołanie metody processAll (nie rozumiem, dlaczego robisz to na rzecz obiektu *engine) wyciągnąłbym z tego warunku i umieścił poniżej instrukcji

Kod:
window.clear()
(w pliku Engine.cpp).

Bo tak założyła ta osoba, która dała pomysł, a robiłem według jej pomysłu, więc tak jest. Więc wystarczy dać 'delete' po wykonaniu się procesAll?

Nie rozumiem jaki masz pomysł z tym procesAll, tzn. nie wiem jak chcesz żebym to wywołał i gdzie umieścił.

AtEDIT

Bo ogólnie jak dla mnie to coś nie trybi właśnie w tym bool nextFrame()
Kod:
bool nextFrame() {
        //do{
            //if (player->anim_clock.getElapsedTime() > sf::seconds(0.05f))
            //{
        sf::Vector2f pos;
        std::cout << "nextFrameJumpAnimation->"<<frameId<<"<-\n";
        //std::cout << pos.x<<pos.y;


                //frame++;
                frameId++;
                player->position_x -= 2;
                //player->sprite.move(-2, 0);

                //player->anim_clock.restart();

            //}
        //} while (frameId <30);

to właśnie spowodowane jest tym jak sądzę, że frameId, nie wiadomo dlaczego nie inkrementuje się 30 razy tylko raz i właśnie zaś nie wykonuje się warunek w processAll na usuwanie.

A więc log jaki dostaję to dla '>30': ( dla '<30' nie ma tak jak pisałem 'Usunąłem'):

Działam, nextFrameJumpApplication->0<-, Usunąłem.



Czyli frameId inkrementuje się tylko raz, a nie 30 tak jak być powinno. A znów jak od komentuję pętlę 'while' to frameId doszłoby do '30', ale właśnie nie widać wykonywania się animacji klatka po klatce tylko wykona się 30 razy frameId i jest przeskok o 60 jednostek, w natychmiastowym skoku (tzn. po ustalonym czasie, ale nie klatka po klatce).
 System operacyjny: windows_seven Przeglądarka: firefox
#8
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
Napisałem jasno: wyciągnij instrukcję
Kod:
engine->anim.processAll();
z bloku po warunku
Kod:
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Space)
i umieść pod instrukcją
Kod:
window.clear();
w tym samym pliku.
EDIT: Na moje oko to rysowanie powinno znajdować się wewnątrz tej drugiej pętli. Spróbuj umieścić te instrukcje wewnątrz tej wewnętrznej pętli (tej z warunkiem while(window.pollEvent(event))).
Kod:
window.clear();
engine->processAll();
window.draw(player);
window.draw(oposite);
window.display();

Natomiast w pliku Animation.cpp przed instrukcją
Kod:
animations.erase(i);
umieść
Kod:
delete *i;
 System operacyjny: linux_ubuntu Przeglądarka: firefox
#9
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
(03.06.2015, 20:26)Szachista napisał(a): ...

A więc:
Kod:
engine->processAll();
trzeba było umieścić tak jak pisałeś, ale te wszystkie komendy odpowiedzialne za rysowanie jednak musiały zostać tam gdzie były.

Co do usuwania to jeśli wstawię:


Kod:
delete *i;


To mam błąd: BLOCK_TYPE_IS_VALID(pHead->nBlockUse). Próbowałem też wstawić zaraz po
Kod:
engine->addAnimation(jump)
w event space, ale ten sam błąd. Ale ogólnie dzięki, bo teraz


PS: I ogólnie mówiłeś, żeby był jakiś bool czy trwa animacja, czy ten cały kod to zawiera, bo jak klikam spację i wykonuje się skok, to jak wcisnę d to przemieści się o daną jednostkę.

A więc mistrzu faktycznie jest problem z sterowaniem dwoma jednostkami na raz, jak ten problem rozwiązać? Mógłbyś przytoczyć jeszcze raz co chciałeś wcześniej powiedziećWesoły?
 System operacyjny: windows_seven Przeglądarka: firefox
#10
RE: Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.
Dzisiaj sobie dopiero uświadomiłem, że rysowanie powinno było zostać, gdzie było.
Sprawdziłem u siebie i działa. Jeśli nadal upierasz się przy jawnym alokowaniu pamięci (można bez tego się obejść kosztem niewielkiego skomplikowania programu), to skorzystaj z klasy szablonowej unique_ptr. Poniżej masz małą demonstrację:
Kod:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class K
{
public:
    K()
    {
        cout << "Powstał nowy obiekt K o adresie " << this << endl;
    }

    ~K()
    {
        cout << "Zginął obiekt K o adresie " << this << endl;
    }
};

int main()
{
    vector<K*> wsk;
    vector<unique_ptr<K>> wsk_ptr;
    cout << "Próba bez unique_ptr:\n";
    for (int i = 0; i < 4; i++)
        wsk.push_back(new K);
    cout << "Wynik skasowania zawartości (ręczne kasowanie):\n";
    for (auto i = wsk.begin(); i != wsk.end(); i++)
        delete *i;
    wsk.clear();
    cout << "\nPróba z unique_ptr:\n";
    for (int i = 0; i < 4; i++)
        wsk_ptr.push_back(unique_ptr<K>(new K));
    cout << "Wynik skasowania zawartości:\n";
    wsk_ptr.clear();
    return 0;
}
valgrind nie wykazał żadnego wycieku.

Stwórz jakieś pole w klasie Player przechowujące informację o tym, czy gracz aktualnie skacze, a następnie napisz metodę ustawiającą i pobierającą to pole, a później zmień warunek:
Kod:
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Space && !player.isJumping)
{
   player.set_jumping(true);
   //
}
Po wykonaniu się skoku pamiętaj o zmianie wartości tego pola. Aby sprawdzić, czy wskaźnik pokazuje na obiekt klasy JumpAnimation, możesz posłużyć się operatorem rzutowania dynamic_cast:
Kod:
#include <iostream>
#include <vector>
#include <memory>
#include <cstdlib>
using namespace std;

class Animation
{
public:
    virtual ~Animation() {}
    virtual void info() = 0;
};

class JumpAnimation : public Animation
{
public:
    JumpAnimation()
    {
        cout << "Powstał nowy obiekt JumpAnimation o adresie " << this << endl;
    }

    ~JumpAnimation()
    {
        cout << "Zginął obiekt JumpAnimation o adresie " << this << endl;
    }

    void info()
    {
        cout << "\tJestem obiektem JumpAnimation\n";
    }
};

class WalkAnimation : public Animation
{
public:
    void info()
    {
        cout << "\tJestem obiektem WalkAnimation\n";
    }
};

int main()
{
    vector<unique_ptr<Animation>> anims;
    for (int i = 0; i < 10; i++)
        if (rand() % 2 == 0)
            anims.push_back(unique_ptr<Animation>(new JumpAnimation));
        else
            anims.push_back(unique_ptr<Animation>(new WalkAnimation));

    for (auto i = anims.begin(); i != anims.end(); i++)
        if (dynamic_cast<JumpAnimation*>(i->get()))    // czy typ się zgadza
            i = anims.erase(i) - 1;    // trzeba się cofnąć o 1

    cout << "Koniec\n";
    return 0;
}
 System operacyjny: linux_ubuntu Przeglądarka: firefox
Programy: Polecane / Nowe / Inne




Podobne wątki (Dlaczego animacja wykonuje się skokowo, a nie płynnie klatka po klatce.)
Wątek: Autor Odpowiedzi: Wyświetleń: Ostatni post
Question [SQL]Dlaczego alians nie mmoże być sortowany Cixi 2 8448 13.01.2016, 12:55
Ostatni post: Cixi
  [JavaFX] - animacja - setDuration() działa tylko dla milisekund. Rincewind 5 10631 31.03.2015, 00:31
Ostatni post: koneton
  OpenGL - jak wygenerować animację klatka po klatce. Dreamer1x6xX 3 9865 19.03.2015, 08:55
Ostatni post: Szachista

Skocz do: