更新精灵位置的奇怪行为
Strange behaviour updating sprite position
我正在使用SDL库用C++编写一个简单的roguelike游戏,在屏幕上移动角色时遇到了一些问题。每次需要渲染帧时,我都会使用update()函数更新精灵的位置,如果玩家静止不动,该函数将不起任何作用。为了发出移动命令,从而启动动画,我使用了step()函数,每次玩家从一个瓦片移动到另一个瓦片时只调用一次。收到"向上"命令后,游戏表现良好,角色在一秒钟内顺利移动到新位置。然而,当发出"向下"命令时,他以大约一半的速度移动,显然在一秒钟后,他立即"传送"到最终位置,并突然闪烁。移动的代码基本上是相同的,但在一种情况下,德尔塔移动被加和到y位置,在另一种情况中被减去。也许这个位置是一个整数,而delta是一个二重这一事实造成了问题?sum和subact的表现是否不同(可能取整不同)?以下是相关代码(抱歉长度过长):
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);
动画的速度是相同的,并且问题得到了解决。
考虑以下内容:
#include <iostream>
void main()
{
int a = 1, b = 1;
a += 0.1f;
b -= 0.1f;
std::cout << a << std::endl;
std::cout << b << std::endl;
}
当a和b被赋值时,在浮点到int的隐式转换过程中,超过小数点的所有内容都将被截断,而不是四舍五入。这个程序的结果是:
1
0
你说过m_yPos是一个整数,m_yVel是一个二重。考虑一下如果m_yVel * m_animTimer
的结果小于1,在Player::update
中会发生什么。在UP
的情况下,结果是你的精灵向下移动一个像素,但在DOWN
的情况下你的精灵根本不会移动,因为如果你把小于一的整数加在一起,什么都不会发生。尝试将位置存储为双精度,并且仅在需要将其传递给绘图函数时将其转换为整数。
在转换过程中,要确保舍入而不是截断,可以使用的一个技巧是,在赋值给整数时,始终在浮点值上加0.5。
例如:
double d1 = 1.2;
double d2 = 1.6;
int x = d1 + 0.5;
int y = d2 + 0.5;
在这种情况下,x将变为1,而y将变为2。
我宁愿不做增量计算。这更简单,即使你回到过去,也会给出正确的结果,不会失去精度,而且在现代硬件上也会同样快,甚至更快:
void Player::step(Player::Direction 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_yStart = m_yPos;
m_yDelta = -m_currMap->tileHeight(); // sprite have to move by a tile
m_tStart = SDL_GetTicks(); // Started now
m_tDelta = 1000.0f; // in one second
}
break;
case DOWN:
if(m_currMap->tileAt(m_xPos, m_yPos + m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
m_status = WALKING_DOWN;
m_yStart = m_yPos;
m_yDelta = m_currMap->tileHeight();
m_tStart = SDL_GetTicks(); // Started now
m_tDelta = 1000.0f; // in one second
}
break;
// ...
}
void Player::update()
{
auto tDelta = SDL_GetTicks() - m_tStart;
switch(m_status)
{
case WALKING_UP:
case WALKING_DOWN:
m_yPos = m_yStart + m_yDelta*tDelta/m_tDelta; // update position
break;
default:
break;
}
if(tDelta >= m_tDelta) // if i'm done moving
{
m_xPos = m_xStart + m_xDelta; // adjust position
m_yPos = m_yStart + m_yDelta;
m_status = STANDING; // and stop
}
}
- 将值指定给向量(2D)的向量中的某个位置
- 使用Unreal C++获取VR耳机的世界位置/方向
- 写入位置0x0000000C时发生访问冲突
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 在不同位置渲染相同精灵的数组
- 在鼠标位置移动精灵
- 更新函数之间精灵的位置
- 如何获得精灵的实际位置并将其打印在C 和SFML中
- 如何根据鼠标位置旋转精灵
- 将聚焦的精灵逐渐移动到目标位置(C++和SFML)
- 使用日程表功能更新精灵位置
- 未在指定位置绘制的精灵
- C++ SFML 精灵位置在功能中时不会更新
- 精灵旋转偏移不会停留在它所属的位置。(SDL)
- C++SFML 1.6精灵位置与鼠标
- 更新精灵位置的奇怪行为
- 在不改变精灵位置的情况下缩放资产
- cocos2dx精灵设置位置错误
- DirectX精灵在缩放时呈现在不同的位置
- 如何使用精灵解析器获得错误位置