如何在 GLFW 中为游戏引擎进行正确的输入 class
How do I do a proper input class in GLFW for a game engine
我正在做我的一个小项目,我基本上是在创建我自己的 c++ 游戏引擎/框架来创建图形和/或简单的游戏。我将 OpenGL 与 GLFW 结合使用。我的目标是拥有类似于各种图形框架的东西,比如 raylib 或 openFrameworks(当然是精简)
实际上到目前为止一切正常,但我不知道如何正确地将输入与 window class 分开,因为 window 处理输入似乎相当对我来说很笨重,只会让 window class.
混乱
这是对我的 window class 的快速简化重现。 (我没有在键码中包含枚举 class。)
#pragma once
#include "../extern/GLFW/glfw3.h"
#include <string>
class Window {
private:
GLFWwindow* mWindow;
int mWidth;
int mHeight;
std::string mTitle;
public:
Window();
~Window();
void createWindow(std::string title, int width, int height);
void mainLoop();
GLFWwindow* getWindow() const { return mWindow; }
// Input
private:
bool Window::getKeyStatus(KEY key) {
static void keyCallback(GLFWwindow* mWindow, int key, int scancode, int action, int mods);
bool isKeyDown(KEY key);
};
这是实施加上
#include "Window.h"
#include <iostream>
Window::Window() {}
Window::~Window() {}
void Window::createWindow(std::string title, int width, int height) {
if (!glfwInit());
mWindow = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (!getWindow()) {
glfwTerminate();
}
glfwSetWindowUserPointer(getWindow(), this);
glfwMakeContextCurrent(getWindow());
glfwSetKeyCallback(mWindow, keyCallback);
}
void Window::mainLoop() {
while (!glfwWindowShouldClose(getWindow())) {
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);
/* Swap front and back buffers */
glfwSwapBuffers(getWindow());
/* Poll for and process events */
glfwPollEvents();
if (isKeyDown(KEY::A)) {
std::cout << "A down" << std::endl;
}
if (isKeyDown(KEY::B)) {
std::cout << "B down" << std::endl;
}
}
glfwTerminate();
}
void Window::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
if (key == (int)KEY::ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
} else {
win->currentKeyState[key] = action;
}
}
bool Window::getKeyStatus(KEY key) {
return glfwGetKey(mWindow, (int)key);
}
bool Window::isKeyDown(KEY key) {
bool down = false;
if (getKeyStatus(key) == 1) {
down = true;
}
return down;
}
我该如何继续?我的主要问题是我似乎无法连接我的 window 和输入 class。我应该使用继承还是朋友classes。我应该在 window class(我假设)中有 glfw 的回调,还是应该将它们移动到输入 class?我如何连接这两个 class,这样我就不必总是使用 window 指针,例如 "isKeyDown(GLFWwindow* window, Key keycode)",而只使用 "isKeyDown(Key keycode)"。如果不是太多,请问有人可以写一个简单的输入吗class?
提前致谢
需要指出的一件事是 GLFW 提供轮询和基于回调的输入处理。在您的示例中,您同时使用了:用于处理 A 和 B 键的轮询以及用于处理 escape 键。我相信在创建单独的输入 class(对于键盘和鼠标)时,您会发现回调更容易使用。
有很多方法可以使用 GLFW 的回调方法创建 KeyInput class。这是我的首选技术(为适合您的代码而编写)。它允许 multiple KeyInput 实例(不像单例)意味着你可以有一个 KeyInput 的实例对于 UI 键,游戏中键的实例等
总而言之:每个 KeyInput 实例监视用户在构造时定义的键列表的按下状态。它有一个 getter 和一个 setter 来访问任何键的按下状态(尽管 getter 和 setter 仅适用于受监视的键)。每当调用 GLFW 键输入回调(静态)时,它都会为 KeyInput 的 all instances 调用 setter。实例存储在一个静态向量中,该向量在构建时添加并在销毁时从中删除。
Input.h
#include "../extern/GLFW/glfw3.h"
#include <map>
#include <vector>
class KeyInput {
// Main KeyInput functionality
public:
// Takes a list of which keys to keep state for
KeyInput(std::vector<int> keysToMonitor);
~KeyInput();
// If this KeyInput is enabled and the given key is monitored,
// returns pressed state. Else returns false.
bool getIsKeyDown(int key);
// See _isEnabled for details
bool getIsEnabled() { return _isEnabled; }
void setIsEnabled(bool value) { _isEnabled = value; }
private:
// Used internally to update key states. Called by the GLFW callback.
void setIsKeyDown(int key, bool isDown);
// Map from monitored keyes to their pressed states
std::map<int, bool> _keys;
// If disabled, KeyInput.getIsKeyDown always returns false
bool _isEnabled;
// Workaround for C++ class using a c-style-callback
public:
// Must be called before any KeyInput instances will work
static void setupKeyInputs(Window& window);
private:
// The GLFW callback for key events. Sends events to all KeyInput instances
static void callback(
GLFWwindow* window, int key, int scancode, int action, int mods);
// Keep a list of all KeyInput instances and notify them all of key events
static std::vector<KeyInput*> _instances;
};
Input.cpp
#include "KeyInput.h"
#include <algorithm>
std::vector<KeyInput*> KeyInput::_instances;
KeyInput::KeyInput(std::vector<int> keysToMonitor) : _isEnabled(true) {
for (int key : keysToMonitor) {
_keys[key] = false;
}
// Add this instance to the list of instances
KeyInput::_instances.push_back(this);
}
KeyInput::~KeyInput() {
// Remove this instance from the list of instances
_instances.erase(std::remove(_instances.begin(), _instances.end(), this), _instances.end());
}
bool KeyInput::getIsKeyDown(int key) {
bool result = false;
if (_isEnabled) {
std::map<int,bool>::iterator it = _keys.find(key);
if (it != _keys.end()) {
result = _keys[key];
}
}
return result;
}
void KeyInput::setIsKeyDown(int key, bool isDown) {
std::map<int,bool>::iterator it = _keys.find(key);
if (it != _keys.end()) {
_keys[key] = isDown;
}
}
void KeyInput::setupKeyInputs(Window& window) {
glfwSetKeyCallback(window.getWindow(), KeyInput::callback);
}
void KeyInput::callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
// Send key event to all KeyInput instances
for (KeyInput* keyInput : _instances) {
keyInput->setIsKeyDown(key, action != GLFW_RELEASE);
}
}
我正在做我的一个小项目,我基本上是在创建我自己的 c++ 游戏引擎/框架来创建图形和/或简单的游戏。我将 OpenGL 与 GLFW 结合使用。我的目标是拥有类似于各种图形框架的东西,比如 raylib 或 openFrameworks(当然是精简)
实际上到目前为止一切正常,但我不知道如何正确地将输入与 window class 分开,因为 window 处理输入似乎相当对我来说很笨重,只会让 window class.
混乱这是对我的 window class 的快速简化重现。 (我没有在键码中包含枚举 class。)
#pragma once
#include "../extern/GLFW/glfw3.h"
#include <string>
class Window {
private:
GLFWwindow* mWindow;
int mWidth;
int mHeight;
std::string mTitle;
public:
Window();
~Window();
void createWindow(std::string title, int width, int height);
void mainLoop();
GLFWwindow* getWindow() const { return mWindow; }
// Input
private:
bool Window::getKeyStatus(KEY key) {
static void keyCallback(GLFWwindow* mWindow, int key, int scancode, int action, int mods);
bool isKeyDown(KEY key);
};
这是实施加上
#include "Window.h"
#include <iostream>
Window::Window() {}
Window::~Window() {}
void Window::createWindow(std::string title, int width, int height) {
if (!glfwInit());
mWindow = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (!getWindow()) {
glfwTerminate();
}
glfwSetWindowUserPointer(getWindow(), this);
glfwMakeContextCurrent(getWindow());
glfwSetKeyCallback(mWindow, keyCallback);
}
void Window::mainLoop() {
while (!glfwWindowShouldClose(getWindow())) {
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);
/* Swap front and back buffers */
glfwSwapBuffers(getWindow());
/* Poll for and process events */
glfwPollEvents();
if (isKeyDown(KEY::A)) {
std::cout << "A down" << std::endl;
}
if (isKeyDown(KEY::B)) {
std::cout << "B down" << std::endl;
}
}
glfwTerminate();
}
void Window::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
if (key == (int)KEY::ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
} else {
win->currentKeyState[key] = action;
}
}
bool Window::getKeyStatus(KEY key) {
return glfwGetKey(mWindow, (int)key);
}
bool Window::isKeyDown(KEY key) {
bool down = false;
if (getKeyStatus(key) == 1) {
down = true;
}
return down;
}
我该如何继续?我的主要问题是我似乎无法连接我的 window 和输入 class。我应该使用继承还是朋友classes。我应该在 window class(我假设)中有 glfw 的回调,还是应该将它们移动到输入 class?我如何连接这两个 class,这样我就不必总是使用 window 指针,例如 "isKeyDown(GLFWwindow* window, Key keycode)",而只使用 "isKeyDown(Key keycode)"。如果不是太多,请问有人可以写一个简单的输入吗class?
提前致谢
需要指出的一件事是 GLFW 提供轮询和基于回调的输入处理。在您的示例中,您同时使用了:用于处理 A 和 B 键的轮询以及用于处理 escape 键。我相信在创建单独的输入 class(对于键盘和鼠标)时,您会发现回调更容易使用。
有很多方法可以使用 GLFW 的回调方法创建 KeyInput class。这是我的首选技术(为适合您的代码而编写)。它允许 multiple KeyInput 实例(不像单例)意味着你可以有一个 KeyInput 的实例对于 UI 键,游戏中键的实例等
总而言之:每个 KeyInput 实例监视用户在构造时定义的键列表的按下状态。它有一个 getter 和一个 setter 来访问任何键的按下状态(尽管 getter 和 setter 仅适用于受监视的键)。每当调用 GLFW 键输入回调(静态)时,它都会为 KeyInput 的 all instances 调用 setter。实例存储在一个静态向量中,该向量在构建时添加并在销毁时从中删除。
Input.h
#include "../extern/GLFW/glfw3.h"
#include <map>
#include <vector>
class KeyInput {
// Main KeyInput functionality
public:
// Takes a list of which keys to keep state for
KeyInput(std::vector<int> keysToMonitor);
~KeyInput();
// If this KeyInput is enabled and the given key is monitored,
// returns pressed state. Else returns false.
bool getIsKeyDown(int key);
// See _isEnabled for details
bool getIsEnabled() { return _isEnabled; }
void setIsEnabled(bool value) { _isEnabled = value; }
private:
// Used internally to update key states. Called by the GLFW callback.
void setIsKeyDown(int key, bool isDown);
// Map from monitored keyes to their pressed states
std::map<int, bool> _keys;
// If disabled, KeyInput.getIsKeyDown always returns false
bool _isEnabled;
// Workaround for C++ class using a c-style-callback
public:
// Must be called before any KeyInput instances will work
static void setupKeyInputs(Window& window);
private:
// The GLFW callback for key events. Sends events to all KeyInput instances
static void callback(
GLFWwindow* window, int key, int scancode, int action, int mods);
// Keep a list of all KeyInput instances and notify them all of key events
static std::vector<KeyInput*> _instances;
};
Input.cpp
#include "KeyInput.h"
#include <algorithm>
std::vector<KeyInput*> KeyInput::_instances;
KeyInput::KeyInput(std::vector<int> keysToMonitor) : _isEnabled(true) {
for (int key : keysToMonitor) {
_keys[key] = false;
}
// Add this instance to the list of instances
KeyInput::_instances.push_back(this);
}
KeyInput::~KeyInput() {
// Remove this instance from the list of instances
_instances.erase(std::remove(_instances.begin(), _instances.end(), this), _instances.end());
}
bool KeyInput::getIsKeyDown(int key) {
bool result = false;
if (_isEnabled) {
std::map<int,bool>::iterator it = _keys.find(key);
if (it != _keys.end()) {
result = _keys[key];
}
}
return result;
}
void KeyInput::setIsKeyDown(int key, bool isDown) {
std::map<int,bool>::iterator it = _keys.find(key);
if (it != _keys.end()) {
_keys[key] = isDown;
}
}
void KeyInput::setupKeyInputs(Window& window) {
glfwSetKeyCallback(window.getWindow(), KeyInput::callback);
}
void KeyInput::callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
// Send key event to all KeyInput instances
for (KeyInput* keyInput : _instances) {
keyInput->setIsKeyDown(key, action != GLFW_RELEASE);
}
}