我正在使用SDL库用C++编写一个简单的地牢游戏,但我在屏幕上移动我的角色时遇到了一些问题。每当需要呈现一帧时,我都会使用update()函数更新精灵的位置,如果玩家静止不动,则什么也不做。为了发出移动命令并开始动画,我使用step()函数,每次玩家从一个图块移动到另一个图块时只调用一次。收到“向上”命令后,游戏表现良好,角色在一秒钟内平稳移动到新位置。然而,当给出“向下”命令时,他的速度减半,并且显然在一秒钟过去后,他会瞬间“传送”到最终位置,出现突然的闪烁。移动代码基本相同,但是一个情况下增量移动被加到y位置中,在另一个情况下则被减去。也许位置为整数而增量为双精度浮点数导致了问题?加法和减法行为是否不同(可能存在不同的舍入)?以下是相关代码(对于长度我很抱歉):
动画速度相同,问题得到解决。
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
}
编辑:我删除了一些变量,只留下了经过的时间、速度和最终位置。现在它可以平滑地移动,但向下和向右的移动速度明显比向上和向左的慢。仍然不知道为什么...
编辑2:好的,我弄清楚了这是为什么。正如我首先所猜测的那样,在将双精度转换为整数时,加法和减法有不同的舍入方式。如果我进行如下强制转换:
m_xPos += (int)(m_xVel * m_animTimer);
动画速度相同,问题得到解决。
m_yPos
、m_yVel
和m_animTimer
的类型是什么?我认为m_animTimer
应该始终更新,否则在下一次调用update
时会得到虚假值。 - Piotr Praszmom_yDelta
实际上是在一秒钟内完成的步骤,并且速度包含在该计算中。然后,您通过将delta乘以animTimer来更新m_yPos
,而delta永远不会改变。这将是每秒钟的恒定步长。(免责声明:我可能在这里说了一些无意义的话,我现在有点困。) - Xeo