SFML - jak zrobić animację skoku?

0

Po prostu nie wiem jak. Stworzyłem sobie już postać, którą poruszam WSADem, nałożyłem spritea, zrobiłem też animację podczas ruchu, ale nie wiem jak zrobić z tego animację.

w konstruktorze playera, stworzyłem sobie tablicę współrzędnych o które ma się poruszyć:

for (int i = 0; i < 30; i++)

    {
        rot += 3.14 / 32.0f;
        ax[i] = 1.5*sin(rot);
        bx[i] = -1.5*cos(rot);
        //std::cout << i << " ->     Rot: " << rot << "     ax: " << ax[i] << "     bx: " << bx[i] << std::endl;



    }
    rot = 0;

I zaś mam właśnie funkcję skok:

void Player::skok()

{
    
    status = IDZ;
    
    sf::Time t2 = anim_clock.getElapsedTime();
    
        if (anim_clock.getElapsedTime()>sf::seconds(0.09f))
        {
            std::cout << t2.asSeconds() << std::endl;
            sprite.move(ax[i], bx[i]);
            anim_clock.restart();
            i++;
            if (i == 29) i = 0;
        }

I nie wiem jak zrobić z tego animację, bo jak przyciskam spację, to w miarę płynnie, po prostu się przemieszcza. Ale no chyba rozumiecie o co chodzi, chcę żeby po wciśnięciu spacji, wykonał się cały zakres ruchów, tak jakbym 30 razy kliknął spację.

To cały kod:

Player.h: http://wklej.org/id/1724806/
Engine.h: http://wklej.org/id/1724810/
Game.h: http://wklej.org/id/1724811/

Player.cpp: http://wklej.org/id/1724814/
Engine.cpp: http://wklej.org/id/1724817/
Game.cpp: http://wklej.org/id/1724816/
main.cpp: http://wklej.org/id/1724815/

Jak coś to czas pobiera się:

anim_clock.getElapsedTime()

Bo jak dla mnie to co napisałem powinno działać. Tzn. jak widzicie odmierzam sobie 0.09s, czyli tyle czasu trwania ma moja klatka. I teraz jeśli umieściłbym to powiedzmy w takiej pętli:

 for (int a = 0; a<30; a++){
		sf::Time t2 = anim_clock.getElapsedTime();

		if (anim_clock.getElapsedTime()>sf::seconds(0.09f))
		{
			std::cout << t2.asSeconds() << std::endl;
			sprite.move(ax[i], bx[i]);
			anim_clock.restart();
			i++;
			if (i == 29) i = 0;
		}
	}

To kod odpowiedzialny za poruszanie się mojej postaci podczas skoku powinien jak dla mnie wykonać się te '30' razy, ale wykonuje się tylko raz, dlaczego?

0

Generalnie to w tej pętli to chyba chodziło CI o:

 

 for (int a = 0; a<30; a++){
        sf::Time t2 = anim_clock.getElapsedTime();
 
        if (anim_clock.getElapsedTime()>sf::seconds(0.09f))
        {
            std::cout << t2.asSeconds() << std::endl;
            sprite.move(ax[a], bx[a]);
            anim_clock.restart();
            i++;
            if (a == 29) a = 0;
        }
    }

UWAGA! Pisane na kolanie, bez debugowania.

0
EroSanin napisał(a):

Generalnie to w tej pętli to chyba chodziło CI o:

 

 for (int a = 0; a<30; a++){
        sf::Time t2 = anim_clock.getElapsedTime();
 
        if (anim_clock.getElapsedTime()>sf::seconds(0.09f))
        {
            std::cout << t2.asSeconds() << std::endl;
            sprite.move(ax[a], bx[a]);
            anim_clock.restart();
            i++;
            if (a == 29) a = 0;
        }
    }

UWAGA! Pisane na kolanie, bez debugowania.

Nie, no tą pętlę dodałem tylko po to żeby według mnie, po prostu wykonał mój kod 30 razy, klatka po klatce. Ale nie działa tak, bo gdy się wykona to po prostu przechodzi do 30 kroku, nie klatka po klatce, ale Od razu.

A mój kod:

if (anim_clock.getElapsedTime()>sf::seconds(0.09f))
		{
			std::cout << t2.asSeconds() << std::endl;
			sprite.move(ax[i], bx[i]);
			cx[i] = bx[i]; 
			std::cout <<"i -> "<<i<<"bx: -> "<< bx[i]<<std::endl;
			anim_clock.restart();
			i++;
			if (i == 30) i = 0;
		}

Tzn. zeruje tu "i" po to żeby przy kolejnym kliknięciu spacji, postać znów przesunęła się po kolei od współrzędnej 'zerowej' skoku do ostatniej. I teraz mam właśnie problem z tym, żeby nie klikać spacji 30 razy, żeby wykonywało się to klatka, po klatce. Tylko zautomatyzować, to i żeby po jednym wciśnięciu spacji, wykonało się kolejnych 30 kroków.

0

Źle do tego podchodzisz - prędzej czy później będziesz musiał wykonywać bardziej skomplikowane animacje, więc zrób sobie jakiś prosty edytor animacji, w którym ustawiasz punkty klatkowe i potem interpoluj po splajnach.

0
Patryk27 napisał(a):

Źle do tego podchodzisz - prędzej czy później będziesz musiał wykonywać bardziej skomplikowane animacje, więc zrób sobie jakiś prosty edytor animacji, w którym ustawiasz punkty klatkowe i potem interpoluj po splajnach.

Ja jestem w tym zielony i proszę o pomoc tylko w tym, do tej gry nie będę tego typu skomplikowanych animacji potrzebował. Więc przykro mi, ale nawet nie mam pojęcia o czym piszesz.

1

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
  }
}

W renderze wywołujesz tylko nextFrame danej animacji i od razu masz zanimowaną postać.
Jeżeli nextFrame zwróci false, oznacza to koniec animacji.

Ten sposób ma jeszcze jedną zaletę, a mianowicie trzymanie się zasady jednej odpowiedzialności.

0
Patryk27 napisał(a):

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.

No dobrze spróbuję, ale miło by było gdybyście jednak pomogli z moją wersją, bo raczej ciężko mi będzie zrealizować to co napisałeś, ale spróbuję.

I ogólnie co ma robić:

void setPlayer(Player* player);

?? Pobierać tylko wskaźnik na postać?

A i jeszcze jedno:

player->position->x = frameId * jakieś tam równanie okręgu;
    player->position->y = frameId * jakieś tam równanie okręgu;

Nie posiadam takich pól w moim kodzie mam je dodać?

0

Pobierać tylko wskaźnik na postać?

No animacja musi być przypisana do jakiejś postaci, zatem po naciśnięciu spacji robisz powiedzmy tak:

JumpAnimation *jump = new JumpAnimation();
jump->setPlayer(gracz);

Engine->animationManager->addAnimation(jump);

I setPlayer to generalnie jednolinijkowiec: this->player = player;, no chyba że korzystasz z method chainingu.

animationManager to menedżer animacji, w uproszczeniu możesz zrobić z tego po prostu std::vector i wykonywać push_back.

Nie posiadam takich pól w moim kodzie mam je dodać?

No to dostosuj podany przeze mnie kod do Ciebie.
Pomyśl, co może zwracać position->x oraz position->y - i podpowiem, że nie jest to kolor postaci.

miło by było gdybyście jednak pomogli z moją wersją

Twoja wersja jest dziwna i nieczytelna i nie działa i fe :P

0

<quote="1145280">

Twoja wersja jest dziwna i nieczytelna i nie działa i fe :P

Powiem Tobie tak, raczej nie dam rady zrealizować twojego pomysłu, a pomoc przy dokończeniu mojego pomysłu, wiele by wyjaśniła. Ale spróbuję. Poza tym dlaczego mam brać pozycję 'playera' a nie sprite`a? Przecież teraz w kodzie czy to przy poruszaniu postacią, czy też przy skoku operowałem na nim.

0

Poza tym dlaczego mam brać pozycję 'playera' a nie spritea?

Bo podczas animacji fizycznie przesuwasz postać, a nie tylko ikonkę, która go reprezentuje?
Co, gdyby postać po drodze do czegoś dobiła - silnik fizyczny też ma znać pozycję ikonki, a nie gracza per se?

Poza tym w ten sposób możesz sobie zmienić zarządzanie spriteami or sth, a nie będziesz musiał aktualizować kodu do animacji.


Twoje podejście, które podałeś na początku jest złe od samych założeń - jeżeli już chcesz to robić w taki sposób, to musiałbyś dodać osobne pole `bool isJumping` oraz `uint jumpFrame` i podczas aktualizacji fizyki gracza sprawdzać czy postać skacze - jeżeli tak, to przesuwać klatkę skoku (`jumpFrame++`) i poprawiać odpowiednio pozycję czy najlepiej wektor ruchu. Ale jak mówiłem, to niebywale brzydkie rozwiązanie i gdybym sam takie coś widział, kod poszedłby do kosza :P Lecz cóż, na `newbie` starczy - na przyszłość takie tematy zakładaj w dziale `newbie` właśnie.
0
Patryk27 napisał(a):

.....

Im dłużej się wypowiadasz, tym więcej Cię nie rozumiem, to nie mój sposób, ale z tego poradnika i bazuje głównie na tym kodzie, z biblioteką SFML znam zapoznaję się od wczoraj-> http://szymonsiarkiewicz.pl/piszemy-gre-w-sfmlu-lekcja-3/

Więc dobrze zrealizujmy twój kod, ale bez twojej pomocy nie dam rady.

Chciałem stworzyć pola odpowiedzialne, za pozycję tak jak pisałeś.

protected:
	int klatka_animacji;
	sf::Sprite sprite;
	sf::Texture texture;
	float position_x = getPosition().x, position_y = getPosition().y ;

Zaś starałem się do wrzucić do 'jump_animation' do deklaracji klasy:

player->position_y=frameId;

-> is not accesible through a Player pointer or object

Jak pobrać tą pozycję w Player.h ?

0

Nie robić jej protected :|
Dodaj gettery i settery do position_x oraz position_y.

Btw, co to jest getPosition().x? Mówisz, że masz metodę getPosition i nie wpadłeś na to, aby jej użyć, tylko piszesz dalszy code bloat?
Czy jednak nie masz getPosition? :P

0
Patryk27 napisał(a):

Czy jednak nie masz getPosition? :P

Powiedz mi czy dobrze myślę, dodałem position_x i position_y do Player.h jako protected (jak dawałem private, to nie miałem dostępu w Animation.h)

I dodałem taki kod do domyślnego konstruktora:

sprite.setPosition(1280/2,720/2);
	this->position_x = this->sprite.getPosition().x;

	this->position_y = this->sprite.getPosition().y;

Bo w nim na początku ustawiam pozycję sprite`a na środek ekranu i zaś go tylko przesuwam, dobrze myślę?

No i stworzyłem jeszcze funkcję:

 void Player::aktualizuj()
{
	


	this->position_x = this->sprite.getPosition().x;
	 
	this->position_y = this->sprite.getPosition().y;
	
}

I wywołałem tam gdzie poruszam postacią.

1

Źle kombinujesz - dlaczego pozycja gracza jest uzależniona od sprite'a?
To powinno działać w drugą stronę: pozycję gracza masz w klasie gracza, a to ikonkę dostosowujesz pod niego, bo inaczej takie machlojki właśnie wychodzą.

0
Patryk27 napisał(a):

Źle kombinujesz - dlaczego pozycja gracza jest uzależniona od sprite'a?
To powinno działać w drugą stronę: pozycję gracza masz w klasie gracza, a to ikonkę dostosowujesz pod niego, bo inaczej takie machlojki właśnie wychodzą.

Dobrze mistrzu ogólnie dzięki, że mi pomagasz, pewnie już Ci się nie chce nawet doradzać:D

Więc idąc twoim tokiem myślenia stworzyłem tak:

1: Funkcje:

void Player::setPosition(float x, float y)
{
	this->position_x = x;
	this->position_y = y;
}
void Player::move(float x, float y)
{
	this->position_x += x;
	this->position_y += y;
}
void Player::info_pos()
{
	


	

	std::cout << "X-owa" << this->position_x << std::endl;
	std::cout << "Y-kowa" << this->position_y << std::endl;
	
}
  1. W konstruktorze domyślny mam:
sprite.setPosition(1280/2,720/2);
	this->setPosition(1280 / 2, 720 / 2);
  1. W każdej funkcji odpowiedzialnej za poruszanie postacią mam:
 
void Player::idz_l()
{
	status = IDZ;

	
	//info_pos();
	this->move(-2, 0);
	sprite.move(-2, 0);
}

O to Tobie chodziło?:)

@Edit

No i ogólnie mam do Ciebie jeszcze kilka pytań, bo stanąłem w miejscu.

Jak dotąd : Animation.cpp -> http://wklej.org/id/1724946/

A więc:

1:

virtual bool nextFrame();

co ma robić, ma sprawdzać czy już pora na następną klatkę? - czyli ma odmierzyć określony czas, jeśli czas jest większy od ustalonego, to ma zwrócić 'true' i tyle? Bo ta metoda już istnieje, ale w klasie 'jump_animation'.

2:

Player* getPlayer();
  • czemu ma to służyć?
  1. (Najważniejsze) "animationManager to menedżer animacji, w uproszczeniu możesz zrobić z tego po prostu std::vector i wykonywać push_back." - rozumiem, że animationManager ma być metodą klasy "Engine", ale dalszej treści nie rozumiem.

To chyba wszystkie pytania, jak mi się przypomni coś to dam 'edita'.

0

O to Tobie chodziło?:)

Nie do końca - po co duplikujesz ustawianie pozycji gracza i jego ikonki?
Wrzuć sprajta jako pole w klasie Player i aktualizując pozycję gracza (niech będzie w tym setPosition) zmieniaj także pozycję spritea, po co sobie utrudniać życie.

A najlepiej zrób jeszcze klasę bardziej ogólną - Entity (lub jaką tam nazwę lubisz w stylu "jednostka"), która będzie opakowywała właśnie te podstawowe funkcje jak set/get Position, i dopiero po niej dziedzicz gracza. Ułatwisz sobie życie i to bardzo.

co ma [nextFrame] robić, ma sprawdzać czy już pora na następną klatkę?

Ma ustawić daną jednostkę zgodnie z następną klatką animacji (czyli na przykład jak masz tę animację skakania to po prostu przesunie gracza o coś w lewo/prawo i coś w górę/dół) i zwrócić false, jeżeli animacja już się zakończyła (np. wylądował z powrotem na platformie) lub true, jeżeli wciąż pozostały jakieś klatki animacji.

czemu ma to [getPlayer] służyć?

Każdy prymitywny setter powinien mieć odpowiadający getter, to jedna sprawa;

A druga: player masz jako pole prywatne w klasie Animation (bo i po co je odsłaniać światu w tak dużym stopniu), dlatego musisz się do niego odnieść jakoś w JumpAnimation (btw, nie nazywaj klas wykorzystując underscore), tutaj też wyszedł mój jeden błąd, ponieważ metoda będzie wyglądała tak, dla skakania:

bool JumpAnimation::nextFrame() {
  vector2 pos = getPlayer()->getPosition();

  // jakieś tam obliczenia
  getPlayer()->setPosition(pos);

  return (frame++ < 30);
}

I btw2, zapisuj pozycję jako wektor (w uproszczeniu może być i nawet struktura z dwoma polami: x oraz y), dzięki temu będziesz mógł sobie łatwo zrobić operacje na wektorach (dodawanie, odejmowanie etc. korzystając z przeładowywania operatorów) oraz poruszanie jednostką (to będzie samo position += velocity;).
No i masz wtedy jeszcze automatycznie plus do czytelności kodu.

rozumiem, że animationManager ma być metodą klasy "Engine", ale dalszej treści nie rozumiem.

Nie, animationManager ma być polem klasy Engine, a samo per se klasą w stylu:

class AnimationManager {
private:
  std::vector<Animation> animations;

public:
  void addAnimation(Animation anim); // lub samo `add`, kto co lubi
  void removeAnimation(Animation anim); // lub samo `remove`, ale konsekwentnie do `addAnimation`
  void processAll(); // wykonuje `nextFrame` na każdej animacji z listy `animations`, i jeżeli dane wywołanie `nextFrame` zwróci `false`, usuwa daną animację z listy
}

Formalnie to pole animationManager powinno być prywatne i powinieneś się do niego odnosić za pomocą metody getAnimationManager() z klasy Engine.

No i chcąc dodać jakąś animację, wykonujesz Engine->getAnimationManager()->addAnimation(jakiśtam_obiekt_animacji);, a potem podczas renderowania klatki (czy w timerze czy jak tam chcesz) wywołujesz metodę processAll w tej klasie.

0
Patryk27 napisał(a):

O to Tobie chodziło?:)

co ma [nextFrame] robić, ma sprawdzać czy już pora na następną klatkę?

Ma ustawić daną jednostkę zgodnie z następną klatką animacji (czyli na przykład jak masz tę animację skakania to po prostu przesunie gracza o coś w lewo/prawo i coś w górę/dół) i zwrócić false, jeżeli animacja już się zakończyła (np. wylądował z powrotem na platformie) lub true, jeżeli wciąż pozostały jakieś klatki animacji.

czemu ma to [getPlayer] służyć?

Każdy prymitywny setter powinien mieć odpowiadający getter, to jedna sprawa;

Ok dzięki za odpowiedź, ale powoli, małymi kroczkami do przodu.

A więc to ja mam pisać te

bool nextFrame() {
				frameId++;
				player->position_x = frameId;
				player->position_y = frameId;
				
				
		return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
	}

dla klasy Animation, czy ona już jest?

Drugie próbowałem: dodać tą metodę (to jest metoda tak

Player* getPlayer();
  • bo pierwszy raz taki zapis na oczy widzę??)

A więc jak w ogóle napisać definicję tej metody: tzn. chodzi mi o ten zapis:

Player getPlayer() {}; 
Player Animation::getPlayer() {};
Player Player::getPlayer() {};

???

I on ma zwracać adres obiektu klasy Player, tak?

tzn. ja próbowałem, tak:

Player getPlayer()
{

	Player *wsk = this->player;
	return *wsk;
}

Ale tak jak pisałem przy moim zapisie nie da się używać 'this', dlatego właśnie o tą wersję pytam.

@Edit

void Animation::reset()
{
	frameId = 0;
}

void Animation::setPlayer(Player *player)
{
	this->player = player;
}

Player *Animation::getPlayer()
{

	return player;
}
	/* to jest i w Animation i JumpAnimation*/

bool nextFrame() {
		sf::Vector2f pos;
		pos.x = getPlayer()->position_x;
		pos.y = getPlayer()->position_y;
		frameId++;
		player->setPosition(1, 1);



		return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
	}


O to się rozchodziło?

A z tym AnimationManager to tak ma wyglądać:



#pragma once

#include "Player.h"
#include<vector>
#include"Animation.h"


class AnimationManager  {
private:
		std::vector<Animation> animations;

public:
	void addAnimation(Animation anim); // lub samo `add`, kto co lubi
	void removeAnimation(Animation anim); // lub samo `remove`, ale konsekwentnie do `addAnimation`
	void processAll(); // wykonuje `nextFrame` na każdej animacji z listy `animations`, i jeżeli dane wywołanie `nextFrame` zwróci `false`, usuwa daną animację z listy
};
class Engine
{
public:
	Engine(sf::RenderWindow &win);
	~Engine(void);

	void runEngine(sf::RenderWindow &window);

private:
	Player player;
	AnimationManager anim;
};

I te addAnimation - ma odpowiadać za co konkretnie? Bo szczerze nie wiem jak teraz to skleić w całość. Bo na początku pisałeś, że będziemy wywoływali animację w ten sposób: Engine->getAnimationManager()->addAnimation(jakiśtam_obiekt_animacji);

Więc z twojej rozmowy, rozumiem, że ta funkcja ma odpowiadać za dodanie do tego całego wektora, animacji, ale co to znaczy, że co mam wrzucić do tego wektora, ten obiekt co jest w argumencie przekazywany?

Tzn. chodzi Ci o to:


void AnimationManager::addAnimation(Animation anim)
{
	
	animations.push_back(anim);
}; // lub samo `add`, kto co lubi

void AnimationManager::removeAnimation(Animation anim)
{

	animations.pop_back();
}; // lub samo `add`, kto c

??

I napomnij coś jeszcze o tym Process All (tak bardziej bym prosił żebyś rozwinął, bo teraz stoję i niezbyt wiem jak z tym ruszyć:) )

0

A więc to ja mam pisać te (...) dla klasy Animation, czy ona już jest?

Klasa Animation jest klasą abstrakcyjną reprezentującą pewną animację, a klasy dziedziczące po niej są dopiero klasami zawierającymi daną animację.
Tak, jak miałbyś na przykład klasę Auto, a z niej dopiero podklasy Volvo, Audi i tak dalej.

O to się rozchodziło?

Coraz bliżej.

Moja wizja wygląda mniej-więcej tak (ten matematyczny wzór wzięty z czapy):

bool JumpAnimation::nextFrame() {
  sf::Vector2f pos = getPlayer()->getPosition();

  pos.x += cos(frameId) * 10;
  pos.y += sin(frameId) * 10;
        
  getPlayer()->setPosition(pos);

  return (frameId++ > 30); // załóżmy, że nasza animacja ma 30 klatek
}

Tzn. chodzi Ci o to:

addAnimation masz ok, ale removeAnimation ma usuwać wskazaną animację, a nie ostatnią.

I napomnij coś jeszcze o tym Process All

Iterujesz po całym kontenerze i wywołujesz nextFrame:

void AnimationManager::processAll() {
  // przeiteruj każdą animację
  for (auto anim: animations) {
    // wywołaj ją i jeżeli skończyła się - usuń
    if (!anim->nextFrame()) {
      removeAnimation(anim);
    }
  }
}

Edit: for-in się tutaj jednak nie sprawdzi, patrz: http://stackoverflow.com/questions/10360461/removing-item-from-vector-while-in-c11-range-for-loop ale przepisanie tej pętli na poprawną zostawiam już Tobie :P

0
Patryk27 napisał(a):

...

No dobrze dziś się za to wziąłem więc tak. Ogólnie to mam kilka pytań.

  1. Tak zmieniłem funkcję add i remove. Dodałem pole finder, przeładowałem operator '==' no i zmieniłem (tzn. add nie zmieniałem tylko wyświetlanie dałem, żebym nie musiał debugować) - nie wiem czy to dobre rozwiązanie:
....
protected:
	unsigned int frameId; // klatka animacji
	unsigned int finder;
public:
int operator==(const Animation &q) { return finder == q.finder; }
....


void Animation::fn(int z)
{
	finder = z;
}; 
void Animation::display_fn()
{
	std::cout << this->finder;
}; 


void AnimationManager::addAnimation(Animation anim)
{
	animations.push_back(anim);
	std::cout << "\n\nDzialam->";
	anim.display_fn();
	std::cout << "<-\n";
}; // lub samo `add`, kto co lubi

void AnimationManager::removeAnimation(Animation anim)
{
	
	std::vector<Animation>::iterator it;
	it = find(animations.begin(), animations.end(), anim);
	animations.erase(it);
	std::cout << "\nUsunalem->";
	anim.display_fn();
	std::cout << "<-"<<std::endl;
}; // lub samo `add`, kto co lubi
  1. Z tym ProcessAll nie potrafiłem użyć funkcji removeAnimation, bo ona przyjmuje jako argument obiekt klasy anim, a w wersji którą zaproponowałeś, w funkcji ProcessAll brak argumentu. I też nie wiedziałem o co chodziło Tobie z tym zapisem:
auto anim: animations

może to miało mi umożliwić użycie zaś 'anim' jako argumentu do funkcji remove, ale wyskakiwały błędy.

Więc napisałem to tak, niestety nie wywołuję funkcji removeAnnimation, bo jak pisałem nie potrafiłem dostarczyć argumentu, ale używam wektor.erase(iterator), a więc wygląda to tak:


void AnimationManager::processAll() {

	
	
	for (auto i = animations.begin(); i != animations.end();) {
		
		if (!i->nextFrame()) {
			std::cout << " Usunalem: ->";
			i->display_fn();
			std::cout << "<-" << std::endl;
			i = animations.erase(i); // reseat iterator to a valid value post-erase
		}
		else {
			
			++i;
		}
		}
  1. Nie potrafiłem także stworzyć
void Engine :: getAnimationManager()
{
	/bla bla bla
}
  • aby wywoływać animację w sposób jaki opisałeś, czyli:
 Engine->getAnimationManager()->addAnimation(jakiśtam_obiekt_animacji)

I ja wywołałem to tak w pliku 'Engine.cpp':

Animation *jump = new Animation();
Animation *walk = new Animation();
Player* gr_1 = &player; //gracz_1
Player* gr_2= &oposite; //gracz_2
				walk->setPlayer(gr_2);
				jump->setPlayer(gr_1);
				walk->fn(20); //finder=20;
			        jump->fn(30); //finder=30;

/* Tu właśnie przez to, że nie wiedziałem jak stworzyć tą funkcję getAnimationManager() wywołałem to tak*/
Engine *engine = new Engine();

					engine->anim.addAnimation(*walk); 
					engine->anim.addAnimation(*jump);
					
					engine->anim.processAll();

/*Log jaki otrzymałem to:

Działam->20<-
Działam->30<-

Usunąłem->20<-
Usunąłem->30<-

Więc dobrze jak dla mnie.
*/
  1. Mam problem z tym nextFrame(), bo pisałeś, że ma być tylko w klasie 'JumpAnimation', a właśnie jak nie mam tej funkcji w klasie 'Animation', to w ogóle mi się nie chce skompilować. I ogólnie nie rozumiem jak ma to działać, bo teraz te dwie funkcje wyglądają tak:
 /*Animation*/
bool nextFrame() {
		
		sf::Vector2f pos;
		std::cout << "nextFrameAnimation\n";
		std::cout << "X'kowa: " << pos.x << "  Y'kowa: " << pos.y;
		
		frameId++;
		
		std::cout << " frameId: " << frameId<<std::endl;
				player->position_x -= 5;  // na to nie patrz zmienię to jak będzie działać
				player->position_y -= 5;  // na to nie patrz zmienię to jak będzie działać
				
			

		



		return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
	}

/*JumpAnimation*/
bool nextFrame() {
		
		sf::Vector2f pos;
		std::cout << "nextFrameJumpAnimation\n";
		std::cout << "X'kowa: " << pos.x << "  Y'kowa: " << pos.y;
		
		frameId++;
		
		std::cout << " frameId: " << frameId<<std::endl;
				player->position_x -= 5;   // na to nie patrz zmienię to jak będzie działać
				player->position_y -= 5; // na to nie patrz zmienię to jak będzie działać
				
			

		



		return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
	}

I z uwagi na to, że funkcja 'addAnimation' przyjmuje jako argument obiekt klasy Animation, to właśnie wywołuje mi się funkcja 'nextFrame()' z klasy Animation, a nie 'JumpAnimation' i nie wiem jak to właśnie ma wyglądać. A i właśnie chyba coś jest nie tak z tą funkcją, bo jak wcisnę 'spacje' to cały kod się wywołuje, ale nie ma animacji, tylko dopiero jak wcisnę WSAD to dopiero postać mi się przesuwa o te 5 jednostek. I nie jest to płynna animacja klatka po klatce, ale skokowe przejście.

To wszystko chyba co chciałem napisać. Prosiłbym o odpowiedź, bo wydaje mi się, że już blisko do celu:P

0

Dodałem pole finder

A co to pole ma oznaczać?

przeładowałem operator '=='

No ale po co?
Jaki sens ma w ogóle porównywanie animacji?
Czy animacja skoku jest równa animacji latania? Toż to sensu nie ma.

bo ona [removeAnimation] przyjmuje jako argument obiekt klasy anim

Przyjmuje jako argument obiekt klasy Animation, czyli dokładnie to, po czym iterujesz w processAll :P
Ale faktycznie, szybciej będzie wykonywać bezpośrednio operacje na wektorze (tak jak masz teraz) - dzięki temu masz liniową złożoność, a nie kwadratową (tak jak ja chciałem zrobić).

Nie potrafiłem także stworzyć [getAnimationManager]

Dodaj do klasy silnika prywatne pole AnimationManager animation; (chyba że już coś takiego masz) i zwracaj właśnie to. Na logikę - akcesory (getXXX, setXXX) przeważnie robią tylko takie proste rzeczy jak zwracanie czy ustawianie pola.

Mam problem z tym nextFrame(), bo pisałeś, że być tylko w klasie 'JumpAnimation'

Więc się nie zrozumieliśmy :P
W klasie abstrakcyjnej Animation masz abstrakcyjną metodę nextFrame jako podstawę (ta metoda ma być niezaimplementowana, tylko pusta!) i każdy z potomków (JumpAnimation, WalkAnimation i cokolwiek tam będziesz jeszcze chciał mieć) musi dopiero nadpisywać tę metodę.

/*JumpAnimation*/
bool nextFrame() {
 
        sf::Vector2f pos;
        std::cout << "nextFrameJumpAnimation\n";
        std::cout << "X'kowa: " << pos.x << "  Y'kowa: " << pos.y;
 
        frameId++;
 
        std::cout << " frameId: " << frameId<<std::endl;
                player->position_x -= 5;
                player->position_y -= 5;

        return (frameId > 30); // załóżmy, że nasza animacja ma 30 klatek
    }

Wypisujesz jakieś xowe, choć nie inicjujesz niczym zmiennej pos wewnątrz tej metody.

Zatem raz jeszcze:

bool JumpAnimation::nextFrame() {
  sf::Vector2f pos = this->getPlayer()->getPosition(); // musisz zmienić `Player`, aby nie miał dwóch osobnych pól `position_x` oraz `position_y`, a zamiast tego trzymał pozycję jako wektor

  pos.x -= 3;
  pos.y -= 3;

  this->getPlayer()->setPosition(pos);

  return (frameId < 30); // zwróć uwagę na znak!
}
0
Patryk27 napisał(a):

Mam problem z tym nextFrame(), bo pisałeś, że być tylko w klasie 'JumpAnimation'

Więc się nie zrozumieliśmy :P
W klasie abstrakcyjnej Animation masz abstrakcyjną metodę nextFrame jako podstawę (ta metoda ma być niezaimplementowana, tylko pusta!) i każdy z potomków (JumpAnimation, WalkAnimation i cokolwiek tam będziesz jeszcze chciał mieć) musi dopiero nadpisywać tę metodę.

Zatem raz jeszcze:

bool JumpAnimation::nextFrame() {
  sf::Vector2f pos = this->getPlayer()->getPosition(); // musisz zmienić `Player`, aby nie miał dwóch osobnych pól `position_x` oraz `position_y`, a zamiast tego trzymał pozycję jako wektor

  pos.x -= 3;
  pos.y -= 3;

  this->getPlayer()->setPosition(pos);

  return (frameId < 30); // zwróć uwagę na znak!
}

Widzę, że jesteś więc na szybko odpiszę, a zaś jak zrobię wstawię kolejnego posta. No dobrze, ale popatrz, jeśli zrobię tak jak teraz mi mówisz, czyli nextFrame w "Animation' będzie puste, co przed chwilą zmieniłem, to dla tego kodu co teraz mam nic się nie wywoła i właśnie nie wiem, czemu.

Pewnie chciałeś zastosować polimorfizm, ale właśnie nie wiem dlaczego też nie działa, popatrz teraz wyglądają one tak:

/*Animation*/
virtual bool nextFrame() {
		
		std::cout << "nextFrameAnimation\n";
	}

/*JumpAnimation*/
sf::Vector2f pos;
		std::cout << "nextFrameJumpAnimation\n";
		
		
		frameId++;
		
		std::cout << " frameId: " << frameId<<std::endl;
				player->position_x -= 5;
				player->position_y -= 5;
				
			

		



		return (frameId < 30); // załóżmy, że nasza animacja ma 30 klatek

I dla tego wywołania:

                                        Animation *jump;
					Animation *walk = new Animation();
					JumpAnimation skok;

				Player* gr_1 = &player;
				Player* gr_2 = &oposite;
				jump = &skok;
				walk->setPlayer(gr_2);
				jump->setPlayer(gr_1);
				walk->fn(25);
				jump->fn(30);
				jump->nextFrame();
				
					Engine *engine = new Engine();

					engine->anim.addAnimation(*walk); 
					engine->anim.addAnimation(*jump);
					
					
/*Dostaję z wywołania bezpośredniego tj: 

jump->nextFrame();  nextFrameJumpAnimation, czyli właśnie tu zadziałał polimorfizm

ale jak już jest wywołanie przez


engine->anim.addAnimation(*jump);  to mam nextFrameAnimation i właśnie o tym mówię nie działa mi to, powiedz mi czy to naprawi się, tzn.
zacznie działać ten polimorfizm, jeśli stworzę tą funkcję getAnimationManager(), a wywołanie będzie tak jak ty pisałeś, czyli:  Engine->getAnimationManager()->addAnimation(jakiśtam_obiekt_animacji) ??


A i jeszcze co muszę zrobić oprócz tej zmiany znaku na '<30" żeby w końcu ta animacja wyświetla się płynnie, a nie z przeskokiem dopiero po wciśnięciu innego klawisza?
*/

Te pole finder, stworzyłem, bo potrzebowałem do tego, żeby przeciążyć operator i zaś móc go użyć w funkcji removeAnimation, ale jeśli teraz usuwam w processAll przez vector.erase(iterator) to nie jest to potrzebne. Chyba, że mam zmienić, tak żeby korzystało z funkcji remove?

PS: Tak jak pisałem jak zrobię to wstawię kolejnego posta.

0

Teraz widzę mój problem: musisz trzymać wektor Animation* i wszędzie działać na Animation* (addAnimation/removeAnimation powinny właśnie przyjmować wskaźnik jako parametr), wtedy będzie ok - przyzwyczajenie z innych języków (Pascal/PHP) robi swoje :P
Wtedy także to pole finder stanie się zbędne.

Btw, jaki sens ma jednoczesne wywoływanie animacji skoku i chodzenia?

0

Czyli tylko mam teraz processAll zmienić czy jeszcze coś? Co do chodzenia i skoku to po prostu chciałem odróżnić je i tylko tyle, chodzenie na razie nie ma żadnej reprezentacji w kodzie dla gracza 2, więc łatwo było odróżnić:P

0

Czyli tylko mam teraz processAll zmienić czy jeszcze coś?

No a co napisałem wyżej?
Poza tym pomyśl trochę - skoro teraz masz listę Animation*, to co jeszcze musisz zmienić :P

Co do chodzenia i skoku to po prostu chciałem odróżnić je i tylko tyle, chodzenie na razie nie ma żadnej reprezentacji w kodzie dla gracza 2, więc łatwo było odróżnić:P

Nie o tym mówię - dodajesz jednocześnie za pomocą addAnimation dwie animacje, czyli potencjalnie obydwie będą się wykonywać w tym samym czasie. Będzie z tego niezły WTF potem.

0
Patryk27 napisał(a):

Czyli tylko mam teraz processAll zmienić czy jeszcze coś?

No a co napisałem wyżej?
Poza tym pomyśl trochę - skoro teraz masz listę Animation*, to co jeszcze musisz zmienić :P

Co do chodzenia i skoku to po prostu chciałem odróżnić je i tylko tyle, chodzenie na razie nie ma żadnej reprezentacji w kodzie dla gracza 2, więc łatwo było odróżnić:P

Nie o tym mówię - dodajesz jednocześnie za pomocą addAnimation dwie animacje, czyli potencjalnie obydwie będą się wykonywać w tym samym czasie. Będzie z tego niezły WTF potem.

Dodałem po prostu dwie animacje, żeby porównać findery i zobaczyć czy prawidłowo się usuwają.
A więc:

...
std::vector<Animation*> animations;

...
void AnimationManager::addAnimation(Animation* anim)
{
	animations.push_back(anim);
	std::cout << "\n\nDzialam->";
	anim->display_fn();
	std::cout << "<-\n";
}; // lub samo `add`, kto co lubi

void AnimationManager::processAll() {

	//auto i = std::begin(animations);
	
	for (auto i = animations.begin(); i != animations.end();) {
		
		if (!(*i)->nextFrame()) {
			std::cout << " Usunalem: ->";
			(*i)->nextFrame();
			std::cout << "<-" << std::endl;
			i = animations.erase(i); // reseat iterator to a valid value post-erase
		}
		else {
			
			++i;
		}
		}

Teraz faktycznie działa polimorfizm. Tylko nadal mam problem z tym, że animacja nie wykonuje się płynnie, tylko skokowo tzn. jeśli wciskam spację, log w konsoli jest, i gdy dopiero wcisnę np. 'W' to postać mi natychmiastowo, nie klatka po klatce przeskakuje o daną liczbę jednostek. Co z tym zrobić?

0

processAll powinno być w osobnym timerze

0
Patryk27 napisał(a):

processAll powinno być w osobnym timerze

Ale która dokładnie część deklaracji tej funkcji, powinna być w timerze, ta co wywołuje nextFrame()?

/*Dla przypomnienia*/

void AnimationManager::processAll() {
 
    //auto i = std::begin(animations);
 
    for (auto i = animations.begin(); i != animations.end();) {
 
        if (!(*i)->nextFrame()) {
            std::cout << " Usunalem: ->";
            (*i)->nextFrame();
            std::cout << "<-" << std::endl;
            i = animations.erase(i); // reseat iterator to a valid value post-erase
        }
        else {
 
            ++i;
        }
        }

I mam pytanie co do dźwięków jak będę chciał żeby były podczas chodzenia itd. to też trzeba by stworzyć coś podobnego jak manager animacji, czy dodawać je do klas z poszczególnymi animacjami?

0

processAll powinno być wywoływane z timera.

czy dodawać je [dźwięki] do klas z poszczególnymi animacjami?

Dźwięki powinny być sprzężone z animacją, tutaj już nie ma sensu bawić się w osobne klasy JumpAnimationSound czy kij wie co :P

Natomiast potrzebować będziesz menedżera dźwięków - ale jak już powiedziałem: bez setek osobnych klas, tylko jeden, konkretny menedżer z metodą w stylu bool playSound(std::string sound) i potem podczas animacji wywołujesz this->getSoundManager()->playSound("jump00"); (choć rzecz jasna będzie się to wiązać z dodaniem osobnego pola na instancję tego menedżera w klasie silnika).

0
Patryk27 napisał(a):

processAll powinno być wywoływane z timera.

Nie chcę wyjść na idiotę, ale też chcę iść dalej z projektem:D

A więc w SFML mam taki timer:

	sf::Clock anim_clock;

I wcześniej jak nie było tego menadżera to animacja chodu była wywoływana w ten sposób:

if (anim_clock.getElapsedTime() > sf::seconds(0.09f))
	{
		...


		anim_clock.restart();
	}

I teraz jak mam to zrobić, bo ja poprzez wywołanie funkcji po prostu rozumiem:

engine->getAnimationManager()->processAll();

I to mam jakoś dać w if'a podobnego do tego czy jak? (jak coś to próbowałem:P - sory, ale na serio nie wiem jak, zresztą jak widziałeś właśnie przez to ten temat powstał, z timerem miałem do czynienia tylko w openGl i tam istniała jakaś funkcja

glutTimerFunc(...)

i wtedy jakoś działało tu nie bardzo wiem jak to ma wyglądać).

PS: A i co do tej funkcji do wywoływania w sposób

engine->getAnimationManager()->processAll();

, udało mi się ją napisać:

AnimationManager* Engine :: getAnimationManager()
{
	return &anim;
}
0

sf::Clock to zegar, a nie timer działający w osobnym wątku :P
Generalnie to animowanie możesz wrzucić do renderowania klatki, na sam koniec - też będzie okej - zamiast bawić się w timery.

udało mi się ją [getAnimationManager] napisać:

Nie musisz zwracać referencji przecież, AnimationManager Engine::getAnimationManager() wystarczy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1