• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

SFML - problem z czasowym poruszaniem obiektów

Object Storage Arubacloud
0 głosów
901 wizyt
pytanie zadane 19 czerwca 2016 w C i C++ przez niezalogowany
otwarte ponownie 11 lipca 2016

Cześć. Piszę Space Invaders i mam problem z ujęciem w c++ automatycznego poruszania obiektami. Zamiar jest następujący: co sekundę, każdy ze stworków (na potrzeby testów pracuje przy jednym) porusza się w prawo np. 10 razy, potem 1 raz w dół, potem 10 razy w lewo, raz w dół i sekwencja rozpoczyna się od nowa, zupełnie jak w grze (pomijam fakt, że przyśpieszają po jakimś czasie).

if (zegar.getElapsedTime().asSeconds() > 1.0f) 
{ 
    inv.move(10, 0); 
    if (przemieszczenia > 5) 
    { 
        przemieszczenia = 0; 
        inv.move(0, 10); 
        break; 
    } 
//tutaj nie mam pojecia jak calkowicie wyjsc z pierwszego ifa, bo gdy dam break pierwszy if gdy minie sekunda po prostu sie zerwie za pierwszym razem, a ja chcialbym dopiero gdy drugi if sie spelni 
}

 Próbowałem tego, nie mam pojęcia jak to rozwiązać.

for (int przemieszczenia = 0; przemieszczenia > 5; przemieszczenia++) 
{ 
    if (zegar.getElapsedTime().asSeconds() > 1.0f) 
    { 
        inv.move(10, 0); 
        zegar.restart(); 
    } 
} 

Kombinowałem dalej, pętla w ogóle się nie wykonuje, najbliżej byłem gdy:

float x=10, y; 
if (zegar.getElapsedTime().asSeconds() > 1.0f) 
{ 
    inv.move(x, 0); 
    przemieszczenia++; 
    zegar.restart(); 
    if (przemieszczenia > 5) 
    { 
        y = 10; 
        inv.move(0, y); 
        przemieszczenia = 0; 
        x = -10; 
    } 
}

 Masa jeszcze innych dziwactw przedarła się przez kod źródłowy, ale były jeszcze gorsze niż powyższe. Nie chcę gotowego rozwiązania na tacy, proszę tylko o wskazówkę, bo czuję, że tkwię w dużym błędzie myślowym. Istota tej animacji obiektów opiera się właśnie o czas, a gdy czas jest warunkiem poruszanie się w jedną stronę będzie wiecznie. Podsumowując, nie mam jakiegokolwiek pomysłu na warunek tej animacji, dlatego liczę na małą wskazówkę, a czekając kombinuję dalej :D :)

//edit: Wow! Dziękuję wszystkim za tak szybki i pozytywny odzew :D Na dniach pogłębię swoją wiedzę o pętlach i wrzucę posta w jaki sposób akurat ja to rozwiązałem. ;)

//edit2: Myślę, że zrozumiałem już sens pętli stałokrokowej i zrobiłem animacje poruszania się stworka :)
Kod:


#include <SFML/Graphics.hpp>
#include <iostream>

using namespace std;


int main()
{
	sf::RenderWindow okno(sf::VideoMode(800, 600), "sfml");

	sf::Texture invtxd;
	invtxd.loadFromFile("inv1ab.png");
	sf::Sprite inv(invtxd);
	inv.setTextureRect(sf::IntRect(0, 0, 256, 256));
	sf::Clock stoper;

	sf::Clock zegar; //tworzymy zegar, który będzie odliczał nam czas od jego utworzenia
	sf::Time CzasOdAktualizacji = sf::Time::Zero; //tworzymy obiekt, który będzie zapamiętywał czas, który upłynał od ostatniej aktualizacji i zerujemy go.
	const sf::Time KrokCzasowy = sf::seconds(1.f / 60.f); //nasz krok czasowy i czas klatki, tutaj około 60 klatek na sekunde

	int przemieszczenia = 0;
	const int jakDalekoPrzeniesc = 20;

	while (okno.isOpen())
	{
		sf::Time Czas = zegar.restart(); //tworzymy obiekt, który będzie przetrzymywał czas jaki zajął obieg pętli
		CzasOdAktualizacji += Czas; // zwiększamy czas od ostatniej aktualizacji(skrót: COOA) o czas obiegu pętli
		while (CzasOdAktualizacji > KrokCzasowy) //jeśli COOA większe od kroku czasowego
		{
			sf::Event event;
			while (okno.pollEvent(event))
			{
				if (event.type == sf::Event::Closed)
					okno.close();
			}
			
			//tutaj aktualizacja gry
			if (stoper.getElapsedTime().asSeconds() > 1.f)
			{
				int x = inv.getPosition().x;
				int y = inv.getPosition().y;
				inv.setPosition(x + 10, y);
				if (przemieszczenia < 22)
				{
					if (przemieszczenia < 10)
					{
						przemieszczenia++;
						inv.setPosition(x + jakDalekoPrzeniesc, y); //prawo
					}
					else if (przemieszczenia < 11)
					{
						przemieszczenia++;
						inv.setPosition(x, y + jakDalekoPrzeniesc); //dol
					}
					else if (przemieszczenia < 21)
					{
						przemieszczenia++;
						inv.setPosition(x - jakDalekoPrzeniesc, y); //lewo
					}		
					else if (przemieszczenia < 22) //dol
					{
						przemieszczenia++;
						inv.setPosition(x, y + jakDalekoPrzeniesc);
					}
					stoper.restart();

				}
				CzasOdAktualizacji -= KrokCzasowy; // odejmujemy czas trwania klatki
			}

			okno.clear(sf::Color::Black);
			okno.draw(inv);
			okno.display();
		}
	}
	return 0;
}

 

Muszę nałożyć ostatnie szlify, bo gdy daję przemieszczenia = (przemieszczenia + 1) % 22; aby zapewnić powtarzalność tej sekwencji (prawo, dół, lewo i tak w kółko) to nie schodzi w dół, ale generalnie jest ok. :)

komentarz 19 czerwca 2016 przez obl Maniak (51,280 p.)
edycja 19 czerwca 2016 przez obl

Musisz uzależnić ruch od czasu ponieważ klatki odświeżania mogą być różne na różnych komputerach i w zależności jak komputer jest mocno obciążony. W twoim przypadku powinieneś zliczać czas w jakiejś zmiennej i gdy przekroczy on 1s to przemieścić obiekt a zmienną zliczającą czas wyzerować.

Przepraszam nie spojrzałem w kod w sumie to tak już zrobiłeś.

Sam warunek powinien wyglądać mniej więcej tak:

if (zegar.getElapsedTime().asSeconds() > 1.0f) 
{ 
	przemieszczenia = (przemieszczenia + 1) % 22; // do zapewnia mi powtarzalność
    if (przemieszczenia < 10) 
    { 
        inv.move(1, 0); // przemieszczam krokowo 10 razy w prawo
    }else if(przemieszczenia < 11)
	{
		inv.move(0, -1); // przemieszczam krokowo 1 raz w dół
	}else if(przemieszczenia < 21)
	{
		inv.move(-1,0); // przemieszczam krokowo 10 razy w lewo
	}else{
		inv.move(0, 1); // przemieszczam krokowo raz 1 górę
	}
}

Całość powinna zostać zamknięta w pętli while co możesz znaleźć na stronie tutaj.

komentarz 19 czerwca 2016 przez niezalogowany
Problem w tym, że gdy ten if jest w mainloopie to wykonuje się ciągle i wygląda to tak (według mojego rozumowania):
1. zaczynam odmierzać czas
2. jeżeli czas, który upłynął wynosi jedną sekundę to porusz obiekt
3. wyzeruj czas
I dalej jest moja kombinatorka, żeby poruszał się w różne strony, nie tylko w jedną. Potrzebuję "czegoś" co sprawi, że ten if wykona się tylko kilka razy mimo, że jest w mainloopie, więc dałem go w pętlę (patrz drugi przykład) ale wtedy obiekt w ogóle się nie porusza, nie mam zielonego pojęcia dlaczego.
komentarz 19 czerwca 2016 przez obl Maniak (51,280 p.)

To w takim razie:

if (zegar.getElapsedTime().asSeconds() > 1.0f)
{
    if(przemieszczenia < 22){
        przemieszczenia++;
        if (przemieszczenia < 10)
        {
            inv.move(1, 0); // przemieszczam krokowo 10 razy w prawo
        }else if(przemieszczenia < 11)
        {
            inv.move(0, -1); // przemieszczam krokowo 1 raz w dół
        }else if(przemieszczenia < 21)
        {
            inv.move(-1,0); // przemieszczam krokowo 10 razy w lewo
        }else{
            inv.move(0, 1); // przemieszczam krokowo raz 1 górę
        }
    }
}

 

komentarz 19 czerwca 2016 przez niezalogowany

Przy takim kodzie obiekt porusza się ciągle, a mi chodzi o to, żeby wykonywał 1 krok na sekundę.

przemieszczenia = (przemieszczenia + 1) % 22; // do zapewnia mi powtarzalność

Nie rozumiem w jaki sposób zapewnia im to powtarzalność.

komentarz 19 czerwca 2016 przez obl Maniak (51,280 p.)
jak licznik osiągnie 22 to 22 modulo 22 = 0 i licznik znów zaczyna startować od 0

zapomniałem dodać że % to operator dzielenia modulo w C++
komentarz 19 czerwca 2016 przez niezalogowany
Ok, a efekt krok/sekunde uzyskałem przez zmiane okno.setFramerateLimit() na 1.
komentarz 19 czerwca 2016 przez niezalogowany

Dodatkowe materiały (odnośnie pętli stałokrokowej):

Ja użyłem pętli z tej strony z PDF-em, bo wydawała mi się najklarowniejsza, więc polecam ci jej używać, a interpolacją się na razie nie zajmuj, o tym możesz pomyśleć kiedy indziej.

2 odpowiedzi

+1 głos
odpowiedź 19 czerwca 2016 przez Dash Nałogowiec (29,650 p.)
wybrane 19 czerwca 2016
 
Najlepsza
Po pierwsze poczytaj o  pętli stałokrokowej. To podstawa tworzenia gier.

Po drugie, rozbij problem na części. Co sekundę ma się poruszyć kolejny stwór, tak? Więc niech klasa kosmity ma funkcję mover(), a obiekty zorganizuj w np. std::vector, bądź jeżeli nie znasz stl'a, w klasycznej tablicy. Dodatkowo powinnna zawirać trzy zmienne: deltaX i deltaY, speed. Ustawiasz wtedy timer i co sekundę  wywołujesz metodę move() na kolejnej komórce tablicy. Funkcja mover ma sprawdzić czy stworek musi poruszyć się w lew, prawo czy w dół i odpowiednio dodać do licznika wartość.  Jeżeli o 5 w prawo, to dodaje 5 do wartości deltaX, jeżeli o 5 w lewo to odejmuje pięć od deltaX, góra/dół analogicznie tylko z deltaY.

Po trzecie każdy stworek ma mieć funkcję updatePosition().  Masz już deltę o którą stworek ma się poruszyć. Teraz ustawiasz drugi timer żeby co powiedzmy 1/30 sekundy iterował po całej tablicy i wywoływał funkcję updatePosition. Funkcja updatePosition  sprawdza czy delty są zerowe. Jeżeli nie, to sprawdza czy są ujemne/dodatnie. I tak gdy powiedzmy deltaX wynosi 3, to odejmuje od deltyX i od pozycji x sprite'a kosmity wartość speed. Skoro z każdym ruchem wartość deltaX i pozycja x potwora zmienia się o tyle samo, to w pewnym momencie delta będzie równa zero, a kosmita się zatrzyma.  

Jeżeli czegoś nie rozumiesz, to mogę wieczorem napisać Ci te klasy. Takie rozwiązanie pozwoli Ci dokładnie kontrolować m.in. prędkość potworków.
komentarz 19 czerwca 2016 przez niezalogowany
Możemy się umówić, że jak będę jak jeszcze jakieś problemy to napiszę w prywatnej wiadomości, ok? Obecnie czytam książkę "Język C++ Szkoła programowania" i pozyskałem z niej wiedzę na temat vector (rozmawiamy o tablicy dynamicznej, dokładniej klasie szablonowej?). Przeczytałem kilka razy to co napisałeś na temat poruszania stworkami i teraz mam mętlik w głowie. Ja to widziałem w następujący sposób:
Tworzę klasę entity i w niej zawieram wszystkie informacje na temat tekstury, sprajta, intrecta (mam spritesheeta, bo ten stworek nie tylko będzie się poruszać, ale także się animować!) etc. a potem tylko tworzę obiekt tej klasy, każdemu przypisuje pozycje początkową na ekranie, a potem używam metod z klasy dotyczącą właśnie poruszanie się stworka. Twój komentarz obecnie brzmi dla mnie skomplikowanie, jest jakieś sprawdzone źródło wiedzy jak to zrobić? Jeżeli chcesz, to możesz zrobić te klasy, ale chciałbym je dokładnie przeanalizować, żeby zrozumieć jak to działa, bo w przeciwnym wypadku nie będzie miało to najmniejszego sensu.
komentarz 19 czerwca 2016 przez Dash Nałogowiec (29,650 p.)
Dobrze myślisz. Tylko zamiast entity, nazwałbym ja np. Enemy, Entity jest bardzo popularną nazwą, i przyjęło się że tak nazywa się abstrakcyjna klasa po której interaktywne rzeczy w grze dziedziczą. Taki szczegół.

Mętlik w głowie ma się zawsze jak się uczy czegoś tak abstrakcyjnego jak obiektówka :D. Wyobraź sobie dwie klasy. Klasę Enemy, która przechowuje sprite, funkcję mover i update, zmienne delta i speed. Reprezentuje ona pojedynczego stwora. Teraz kolejna klasa, EnemyHord która reprezentuje całą grupę tych stworów. To EnemyHord przechowuje wszystkie stwory w sobie, wewnątrz jakiegoś kontenera, i to ona odpowiada za ten impuls co sekundę. To nie jest skomplikowane, tylko trzeba to zrozumieć, przetrawić.

Co do źródła wiedzy, blog SzymonaSiarkiewicza jest podobno całkiem sensowny, ja uczyłem się z książek SFML Development i SFML Blueprints, chociaż Ci ich teraz nie polecam, najpierw trzeba dobrze opanować język.

  I problem wolałbym rozwiązywać tutaj, nie tylko ty masz taki problem :). Niech i przyszłe pokolenia skorzystają.
0 głosów
odpowiedź 19 czerwca 2016 przez MetRiko Nałogowiec (37,110 p.)

Proponuję zrobić prosty licznik progresu.. czyt. jakiś prosty int który inkrementuje się co jakiś tam czas (czas przeskoku do następnej klatki ruchu). Następnie zrobić warunki w stylu:
if(JestPrzeskok())
{
   licznik%=21;
   if(licznik<10) skok_w_prawo();

   else if(licznik==10||licznik==20) skok_w_dół();
   else if(licznik>10) skok_w_lewo();
}

Podobne pytania

0 głosów
1 odpowiedź 144 wizyt
pytanie zadane 14 stycznia 2016 w C i C++ przez Avernis Nałogowiec (27,400 p.)
+1 głos
3 odpowiedzi 194 wizyt
pytanie zadane 27 grudnia 2015 w C i C++ przez Avernis Nałogowiec (27,400 p.)
0 głosów
1 odpowiedź 576 wizyt

92,579 zapytań

141,429 odpowiedzi

319,657 komentarzy

61,963 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...