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

如何在GLFW中实现无需窗口引用的事件输入系统?

解决方案:无需窗口引用的GLFW输入处理改造

核心思路

通过将GLFW窗口指针封装为全局存储或单例管理,让输入函数内部直接访问窗口对象,避免每次调用都传递窗口引用。同时修复原GetKeyDown函数的静态状态残留问题——静态数组不会自动重置,窗口重启或按键松开后会触发异常。

改造实现

方案一:全局变量快速实现

1. 输入头文件(inputHeader.h)

#pragma once
#include <GLFW/glfw3.h>

// 全局存储GLFW窗口指针
extern GLFWwindow* g_GLFWWindow;

bool GetKey(int KeyCode);
bool GetKeyDown(int key);
// 帧结束时调用,重置按键历史状态
void UpdateInputState();

2. 输入实现文件(input.cpp)

#include "inputHeader.h"
#include <GLFW/glfw3.h>

GLFWwindow* g_GLFWWindow = nullptr;
// 替换静态数组为全局数组,支持帧更新重置
bool g_KeyPreviouslyPressed[GLFW_KEY_LAST + 1] = { false };

bool GetKey(int KeyCode)
{
    return glfwGetKey(g_GLFWWindow, KeyCode) == GLFW_PRESS;
}

bool GetKeyDown(int key) {
    bool isPressed = glfwGetKey(g_GLFWWindow, key) == GLFW_PRESS;
    bool wasPressed = g_KeyPreviouslyPressed[key];
    
    if (isPressed && !wasPressed) {
        g_KeyPreviouslyPressed[key] = true;
        return true;
    }
    return false;
}

void UpdateInputState() {
    // 遍历所有按键,重置未按下按键的历史状态
    for (int i = 0; i <= GLFW_KEY_LAST; ++i) {
        if (glfwGetKey(g_GLFWWindow, i) != GLFW_PRESS) {
            g_KeyPreviouslyPressed[i] = false;
        }
    }
}

方案二:单例类封装(更推荐,避免全局污染)

1. 输入头文件(inputHeader.h)

#pragma once
#include <GLFW/glfw3.h>

class Input {
private:
    static Input* s_Instance;
    GLFWwindow* m_Window;
    bool m_KeyPreviouslyPressed[GLFW_KEY_LAST + 1] = { false };

    Input() = default;
public:
    static void Init(GLFWwindow* window);
    static Input& Get();

    bool GetKey(int keyCode);
    bool GetKeyDown(int key);
    void UpdateState();
};

// 全局快捷调用函数
inline bool GetKey(int keyCode) { return Input::Get().GetKey(keyCode); }
inline bool GetKeyDown(int key) { return Input::Get().GetKeyDown(key); }
inline void UpdateInputState() { Input::Get().UpdateState(); }

2. 输入实现文件(input.cpp)

#include "inputHeader.h"
#include <GLFW/glfw3.h>

Input* Input::s_Instance = nullptr;

void Input::Init(GLFWwindow* window) {
    if (!s_Instance) {
        s_Instance = new Input();
        s_Instance->m_Window = window;
    }
}

Input& Input::Get() {
    return *s_Instance;
}

bool Input::GetKey(int keyCode) {
    return glfwGetKey(m_Window, keyCode) == GLFW_PRESS;
}

bool Input::GetKeyDown(int key) {
    bool isPressed = glfwGetKey(m_Window, key) == GLFW_PRESS;
    bool wasPressed = m_KeyPreviouslyPressed[key];
    
    if (isPressed && !wasPressed) {
        m_KeyPreviouslyPressed[key] = true;
        return true;
    }
    return false;
}

void Input::UpdateState() {
    for (int i = 0; i <= GLFW_KEY_LAST; ++i) {
        if (glfwGetKey(m_Window, i) != GLFW_PRESS) {
            m_KeyPreviouslyPressed[i] = false;
        }
    }
}

3. main.cpp中使用示例

#include <GLFW/glfw3.h>
#include "inputHeader.h"

int main() {
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(800, 600, "Game Engine", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // 初始化输入系统
    // 全局版本:g_GLFWWindow = window;
    Input::Init(window);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        // 无需传递window的输入调用
        if (GetKey(GLFW_KEY_SPACE)) {
            // 处理持续按住逻辑
        }
        if (GetKeyDown(GLFW_KEY_ENTER)) {
            // 处理单次按下触发逻辑
        }

        // 渲染逻辑...

        glfwSwapBuffers(window);

        // 必须在帧结束时调用,更新按键状态
        UpdateInputState();
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

关键注意事项

  • 帧末状态更新:必须在每帧结束调用UpdateInputState,否则GetKeyDown会出现按键松开后仍无法再次触发的问题。
  • 架构选择:全局变量实现简单但易引发命名冲突;单例类更符合游戏引擎的面向对象架构,便于后续扩展输入功能(如鼠标、手柄支持)。
  • 线程安全:如果引擎涉及多线程渲染/输入,需要给输入状态的访问添加锁,避免竞态条件。

内容的提问来源于stack exchange,提问作者Aseed Kid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 08:30:24