SDL2玩家X/Y轴正向移动缓慢,鼠标激活后恢复正常的问题
SDL2玩家移动速度异常问题排查与解决
问题描述
在Windows 11的WSL2 Linux环境下运行SDL2游戏程序时出现以下异常:
- 按下S/D键使玩家Y/X坐标正向增加时,移动速度异常缓慢
- 按下W/A键使坐标反向减少时无此问题
- 仅移动鼠标后,正向移动速度才恢复正常
排查过程
- 更换正向移动的绑定按键,问题未解决
- 将
update函数中的toMove设为固定常量后,移动速度恢复正常,初步定位问题与delta time(帧间隔时间)相关
问题根源
玩家坐标使用SDL_Point类型存储,而SDL_Point的x、y字段为int类型。每次移动计算出的toMove是浮点值(playerSpeed * dt):
- 正向移动时,
player.y += toMove会将浮点值截断向下取整,若toMove小于1,坐标不会发生变化,导致移动看起来极慢 - 反向移动时,
player.y -= toMove相当于减去一个小于1的浮点数,截断后实际是对坐标减1(例如100 - 0.3 = 99.7,截断后为99),因此反向移动速度正常 - 移动鼠标后,窗口事件触发可能使dt值变大,
toMove超过1,此时正向移动才会产生明显变化
修复方案
将玩家坐标从SDL_Point(int类型)改为自定义的float类型变量存储,保留浮点精度的累计,渲染时再转换为int类型传递给绘图函数,避免截断丢失移动量。
修复后完整代码
#include <SDL2/SDL.h> #include <stdbool.h> #define SCREEN_WIDTH 640*2 #define SCREEN_HEIGHT 640 #define BLOCK_SIZE (SCREEN_WIDTH / 20) #define BOARD_HEIGHT (SCREEN_HEIGHT / BLOCK_SIZE) #define BOARD_WIDTH (SCREEN_WIDTH / BLOCK_SIZE) enum GridStates { EMPTY, SOLID, PLAYER, }; SDL_Window* gWindow = NULL; SDL_Renderer* gRenderer = NULL; float playerSpeed = 300; int playerRadius = 15; // 改用float存储玩家坐标,保留浮点精度 float playerX = SCREEN_WIDTH / 2.0f; float playerY = SCREEN_HEIGHT / 2.0f; enum GridStates board[SCREEN_HEIGHT / BLOCK_SIZE][SCREEN_WIDTH / BLOCK_SIZE]; bool init() { if (SDL_Init(SDL_INIT_VIDEO) < 0) { SDL_Log("could not init sdl video"); return false; } gWindow = SDL_CreateWindow( "raycasting", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0 ); if (gWindow == NULL) { SDL_Log("could not create an sdl window"); return false; } gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED); if (gRenderer == NULL) { SDL_Log("could not create sdl renderer"); return false; } return true; } void snapPlayerBack() { if (playerX - playerRadius <= 0) { playerX = SCREEN_WIDTH - playerRadius; } if (playerX + playerRadius > SCREEN_WIDTH) { playerX = playerRadius; } if (playerY - playerRadius < 0) { playerY = SCREEN_HEIGHT - playerRadius; } if (playerY + playerRadius > SCREEN_HEIGHT) { playerY = playerRadius; } } void update(float dt) { const Uint8* keysState = SDL_GetKeyboardState(NULL); float toMove = playerSpeed * dt; if (keysState[SDL_SCANCODE_S]) { playerY += toMove; } if (keysState[SDL_SCANCODE_D]) { playerX += toMove; } if (keysState[SDL_SCANCODE_W]) { playerY -= toMove; } if (keysState[SDL_SCANCODE_A]) { playerX -= toMove; } snapPlayerBack(); } void renderGrid() { SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xff); // horizontal lines for (int x = BLOCK_SIZE; x < SCREEN_WIDTH; x += BLOCK_SIZE) { SDL_RenderDrawLine(gRenderer, x, 0, x, SCREEN_HEIGHT); } // verticle line for (int y = BLOCK_SIZE; y < SCREEN_HEIGHT; y += BLOCK_SIZE) { SDL_RenderDrawLine(gRenderer, 0, y, SCREEN_WIDTH, y); } } void renderBoard() { for (int i = 0; i < BOARD_HEIGHT; i++) { for (int j = 0; j < BOARD_WIDTH; j++) { if (board[i][j] == SOLID) { SDL_Rect coordinates = {j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE}; SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0xff, 0xff); SDL_RenderFillRect(gRenderer, &coordinates); } } } } void drawCircle(int centerX, int centerY, int radius) { for (int w = 0; w < radius * 2; ++w) { for (int h = 0; h < radius * 2; ++h) { int dx = radius - w; int dy = radius - h; if ((dx*dx + dy*dy) <= (radius * radius)) { SDL_RenderDrawPoint(gRenderer, centerX + dx, centerY + dy); } } } } void draw() { renderGrid(); renderBoard(); SDL_SetRenderDrawColor(gRenderer, 0x00, 0xff, 0x00, 0xff); // 渲染时转换为int类型 drawCircle((int)playerX, (int)playerY, playerRadius); } void quitSDL() { SDL_DestroyRenderer(gRenderer); SDL_DestroyWindow(gWindow); SDL_Quit(); } int main(int argc, char* args[]) { if (!init()) { return 1; } Uint32 lastTicks = SDL_GetTicks(); SDL_Event e; bool quit = false; while (!quit) { while (SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT) { quit = true; } } Uint32 nowTicks = SDL_GetTicks(); float dt = (nowTicks - lastTicks) / 1000.0f; lastTicks = nowTicks; SDL_SetRenderDrawColor(gRenderer, 0xff, 0xff, 0xff, 0xff); SDL_RenderClear(gRenderer); update(dt); draw(); SDL_RenderPresent(gRenderer); } quitSDL(); }
内容的提问来源于stack exchange,提问作者Moe Fa
相关产品推荐
相关产品推荐

