Tôi đang mã hóa một trò chơi roguelike đơn giản trong C++ sử dụng thư viện SDL và tôi gặp một số vấn đề khi di chuyển nhân vật của mình trên màn hình. Mỗi khi một frame cần được render, tôi cập nhật vị trí của sprite bằng cách sử dụng hàm update(), không có gì nếu người chơi đang đứng yên. Để phát hành lệnh chuyển động và do đó bắt đầu hoạt ảnh, tôi sử dụng hàm step() chỉ được gọi một lần cho mỗi chuyển động của người chơi từ ô này sang ô khác. Khi nhận được lệnh "lên", trò chơi hoạt động tốt và nhân vật di chuyển suôn sẻ trong một giây tới vị trí mới. Tuy nhiên, khi lệnh "xuống" được đưa ra, anh ta di chuyển với tốc độ khoảng một nửa, và rõ ràng sau một giây trôi qua, anh ta ngay lập tức "dịch chuyển" đến vị trí cuối cùng, với một cú nhấp nháy đột ngột. Mã cho chuyển động về cơ bản là giống hệt nhau, nhưng thực tế là trong một trường hợp, chuyển động delta được cộng vào vị trí y, trong trường hợp khác được trừ. Có lẽ thực tế là vị trí là một số nguyên và đồng bằng là một đôi là gây ra vấn đề? Liệu tổng hợp và phép trừ có hoạt động khác nhau (có thể làm tròn khác nhau) không? Đây là mã có liên quan (xin lỗi vì sự chiều dài):Hành vi lạ cập nhật vị trí 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: Tôi đã gỡ bỏ một số biến và duy nhất còn lại thời gian trôi qua, tốc độ và vị trí cuối cùng. Bây giờ nó di chuyển mà không nhấp nháy, nhưng chuyển động xuống và phải là rõ ràng chậm hơn so với những người lên và trái. Vẫn tự hỏi tại sao ...
EDIT 2: Ok, tôi đã tìm ra lý do tại sao điều này xảy ra. Như tôi nghĩ ở vị trí đầu tiên, có một sự làm tròn khác nhau từ đôi đến số nguyên khi nói đến tổng và trừ. Nếu tôi thực hiện một dàn diễn viên như thế này:
m_xPos += (int)(m_xVel * m_animTimer);
tốc độ hoạt ảnh giống nhau và vấn đề được giải quyết.
Có thể sai, nhưng trong 'Player :: update', trong trường hợp' WALKING_DOWN', không phải cả hai dòng đều sử dụng '+ =' thay vì '- ='? Tôi chỉ đoán 'WALKING_DOWN' phải ngược lại với' WALKING_UP'. Không chắc chắn nếu điều này có bất cứ điều gì để làm với vấn đề của bạn. –
@KenWayneVanderTìm biến m_yDelta lưu các pixel còn lại để đi tới vị trí, vì vậy trong cả hai trường hợp, nó sẽ bị giảm đi. –
Loại 'm_yPos',' m_yVel' và 'm_animTimer' là gì? Và, tôi nghĩ rằng 'm_animTimer' nên luôn được cập nhật, nếu không bạn sẽ nhận được giá trị không có thật tại cuộc gọi tiếp theo để' cập nhật'. –