You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:35:59