跳转到内容

相对鼠标模式

For certain games, it's useful to disassociate the mouse cursor from mouse input. An FPS, for example, would not want the player's motion to stop as the mouse hits the edge of the window. For these scenarios, use SDL_SetWindowRelativeMouseMode(), which hides the cursor, grabs mouse input to the window, and reads mouse input no matter how far it moves.

核心概念

相对鼠标模式:将鼠标移动与屏幕光标位置分离的模式。

原文翻译

对于某些游戏来说,将鼠标光标与鼠标输入分离是很有用的。例如,FPS(第一人称射击)游戏不希望当鼠标移动到窗口边缘时,玩家的移动就停止。对于这些场景,使用 SDL_SetWindowRelativeMouseMode(),这个函数会:

  1. 隐藏光标
  2. 将鼠标输入限制在窗口内
  3. 无论鼠标移动多远都能读取鼠标输入

实际场景对比

cpp
// 伪代码示例,展示两种模式的区别

// ===== 普通模式(绝对鼠标模式)=====
void normalMouseMode() {
    while (gameRunning) {
        int mouseX, mouseY;
        SDL_GetMouseState(&mouseX, &mouseY);  // 获取屏幕坐标
        
        // 问题:鼠标移动到屏幕边缘就动不了了
        if (mouseX >= screenWidth) {
            // 无法继续向右转
        }
    }
}

// ===== 相对鼠标模式(FPS游戏使用的模式)=====
void relativeMouseMode() {
    SDL_SetRelativeMouseMode(SDL_TRUE);  // 开启相对模式
    
    while (gameRunning) {
        int mouseRelX, mouseRelY;
        SDL_GetRelativeMouseState(&mouseRelX, &mouseRelY);  // 获取相对移动量
        
        // 玩家可以无限旋转!
        camera.yaw += mouseRelX * sensitivity;     // 水平旋转
        camera.pitch += mouseRelY * sensitivity;   // 垂直旋转
        
        // 鼠标位置会被重置到窗口中心,但玩家感觉不到
    }
}

具体例子

1. FPS游戏(反恐精英、使命召唤等)

cpp
// FPS游戏中的鼠标控制
class FPSPlayer {
    float yaw = 0;      // 水平旋转角度
    float pitch = 0;    // 垂直旋转角度
    
public:
    void handleMouse(int relX, int relY) {
        // 鼠标移动多少,视角就转多少,没有限制
        yaw += relX * 0.1f;      // 可以无限旋转
        pitch += relY * 0.1f;    // 上下视角通常有限制
        pitch = clamp(pitch, -89.0f, 89.0f);  // 限制上下视角
    }
};

int main() {
    SDL_SetRelativeMouseMode(SDL_TRUE);  // 开启相对模式
    
    FPSPlayer player;
    SDL_Event e;
    
    while (running) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_MOUSEMOTION) {
                // 得到的是相对移动量,而不是屏幕坐标
                player.handleMouse(e.motion.xrel, e.motion.yrel);
            }
        }
    }
    return 0;
}

2. 普通应用 vs FPS游戏

cpp
#include <iostream>
using namespace std;

// 场景1:普通桌面应用(绝对模式)
void desktopApplication() {
    cout << "普通应用:点击按钮" << endl;
    cout << "- 鼠标移动到屏幕位置(100,200)才能点击按钮" << endl;
    cout << "- 鼠标移动到屏幕边缘就停住了" << endl;
    cout << "- 光标可见" << endl;
}

// 场景2:FPS游戏(相对模式)
void fpsGame() {
    cout << "FPS游戏:控制视角" << endl;
    cout << "- 鼠标向右移动10像素 → 视角向右转10度" << endl;
    cout << "- 鼠标继续向右移动 → 可以无限转圈" << endl;
    cout << "- 鼠标回到中心位置(自动重置)" << endl;
    cout << "- 光标隐藏" << endl;
}

实际代码示例

cpp
#include <SDL2/SDL.h>
#include <iostream>

class Game {
    SDL_Window* window;
    bool running = true;
    float cameraYaw = 0;
    float cameraPitch = 0;
    
public:
    void init() {
        SDL_Init(SDL_INIT_VIDEO);
        window = SDL_CreateWindow("FPS Demo", 
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            800, 600, SDL_WINDOW_SHOWN);
        
        // 关键:开启相对鼠标模式
        SDL_SetRelativeMouseMode(SDL_TRUE);
        // 效果:
        // 1. 鼠标光标自动隐藏
        // 2. 鼠标被限制在窗口内
        // 3. 可以无限移动鼠标
    }
    
    void handleEvents() {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                running = false;
            }
            else if (e.type == SDL_MOUSEMOTION) {
                // 在相对模式下,xrel/yrel 是相对移动量
                // 不管鼠标在屏幕什么位置,都能得到移动值
                cameraYaw += e.motion.xrel * 0.1f;
                cameraPitch += e.motion.yrel * 0.1f;
                
                // 限制上下视角(避免翻跟头)
                if (cameraPitch > 89.0f) cameraPitch = 89.0f;
                if (cameraPitch < -89.0f) cameraPitch = -89.0f;
                
                cout << "视角: 水平=" << cameraYaw 
                     << ", 垂直=" << cameraPitch << endl;
            }
        }
    }
    
    void run() {
        while (running) {
            handleEvents();
            // 渲染游戏画面...
            SDL_Delay(16);  // ~60 FPS
        }
    }
    
    ~Game() {
        SDL_DestroyWindow(window);
        SDL_Quit();
    }
};

两种模式对比

特性绝对鼠标模式(普通)相对鼠标模式(FPS)
鼠标光标可见隐藏
移动范围限于屏幕/窗口内理论上无限
读取数据屏幕坐标 (x,y)相对移动量 (dx,dy)
典型应用办公软件、网页FPS游戏、3D编辑器
边缘情况到边缘就停可以无限旋转

总结

简单来说,相对鼠标模式让游戏开发者可以:

  • 隐藏鼠标光标
  • 获取鼠标的移动量而不是位置
  • 实现"鼠标移动 → 视角旋转"的无缝控制
  • 让玩家可以无限旋转视角(如FPS游戏中的360度转身)

这就是为什么你在玩CS:GO、绝地求生等FPS游戏时,鼠标可以一直往一个方向拖动让角色不断转身,而不会因为鼠标碰到桌子边缘就停下。

基于 MIT 许可发布