Koduję prostą grę roguelike w C++ przy użyciu biblioteki SDL i mam pewne problemy z poruszaniem moją postacią na ekranie. Za każdym razem, gdy ramka wymaga renderowania, aktualizuję pozycję ikonki za pomocą funkcji update(), która nie robi nic, jeśli odtwarzacz stoi w miejscu. Aby wydać komendę ruchu, a tym samym uruchomić animację, używam funkcji step() wywoływanej tylko raz na każdy ruch gracza z jednej płytki na drugą. Po otrzymaniu polecenia "w górę" gra zachowuje się dobrze i postać porusza się płynnie w ciągu jednej sekundy do nowej pozycji. Jednakże, gdy polecenie "w dół" jest podane, porusza się z prędkością około połowy prędkości, i oczywiście po upływie jednej sekundy, jest on natychmiast "teleportowany" do pozycji końcowej, z nagłym migotaniem. Kod ruchu jest w zasadzie identyczny, ale z tego powodu, że w jednym przypadku ruch delta jest sumowany do pozycji y, w drugim przypadku jest odejmowany. Być może fakt, że pozycja jest liczbą całkowitą, a delta jest podwójnym, powoduje problemy? Czy suma i subract zachowują się inaczej (może inne zaokrąglenia)? Oto odpowiedni kod (przepraszam za długość):Dziwne zachowanie aktualizujące pozycję sprite
void Player::step(Player::Direction dir)
{
if(m_status != STANDING) // no animation while standing
return;
switch(dir)
{
case UP:
if(m_currMap->tileAt(m_xPos, m_yPos - m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
// if next tile is not a wall, set up animation
m_status = WALKING_UP;
m_yDelta = m_currMap->tileHeight(); // sprite have to move by a tile
m_yVel = m_currMap->tileHeight()/1000.0f; // in one second
m_yNext = m_yPos - m_currMap->tileHeight(); // store final destination
}
break;
case DOWN:
if(m_currMap->tileAt(m_xPos, m_yPos + m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
m_status = WALKING_DOWN;
m_yDelta = m_currMap->tileHeight();
m_yVel = m_currMap->tileHeight()/1000.0f;
m_yNext = m_yPos + m_currMap->tileHeight();
}
break;
//...
default:
break;
}
m_animTimer = SDL_GetTicks();
}
void Player::update()
{
m_animTimer = SDL_GetTicks() - m_animTimer; // get the ms passed since last update
switch(m_status)
{
case WALKING_UP:
m_yPos -= m_yVel * m_animTimer; // update position
m_yDelta -= m_yVel * m_animTimer; // update the remaining space
break;
case WALKING_DOWN:
m_yPos += m_yVel * m_animTimer;
m_yDelta -= m_yVel * m_animTimer;
break;
//...
default:
break;
}
if(m_xDelta <= 0 && m_yDelta <= 0) // if i'm done moving
{
m_xPos = m_xNext; // adjust position
m_yPos = m_yNext;
m_status = STANDING; // and stop
}
else
m_animTimer = SDL_GetTicks(); // else update timer
}
EDIT: usunąłem kilka zmiennych i lewo tylko upływ czasu, prędkości i ostatecznego stanowiska. Teraz porusza się bez migotania, ale ruchy w dół i w prawo są wyraźnie wolniejsze niż w górę i w lewo. Wciąż zastanawiam się, dlaczego ...
EDYTUJ 2: Ok, zorientowałem się, dlaczego tak się dzieje. Tak jak przypuszczałem, po zaokrągleniu od podwójnego do całkowitego istnieje inne zaokrąglenie, jeśli chodzi o sumowanie i odejmowanie. Jeśli wykonam rzut tak:
m_xPos += (int)(m_xVel * m_animTimer);
prędkość animacji jest taka sama, a problem został rozwiązany.
Może być źle, ale w 'Player :: update', w przypadku' WALKING_DOWN', czy obie linie nie powinny używać '+ =' zamiast '- ='? Zgaduję, że 'WALKING_DOWN' powinno być dokładnym przeciwieństwem' WALKING_UP'. Nie jestem pewien, czy ma to coś wspólnego z twoim problemem. –
@KenWayneVanderLinde Zmienna m_yDelta przechowuje pozostałe piksele do pokonania, aby osiągnąć pozycję, więc w obu przypadkach powinna być zmniejszona. –
Jaki jest typ 'm_yPos',' m_yVel' i 'm_animTimer'? I myślę, że 'm_animTimer' powinien być zawsze aktualizowany, w przeciwnym razie dostaniesz fałszywą wartość przy następnym wywołaniu' update'. –