Как сделать правильный класс ввода в GLFW для игрового движка - PullRequest
0 голосов
/ 08 апреля 2019

Я делаю небольшой проект, в котором я в основном создаю свой собственный движок / фреймворк для c ++ для создания графики и / или простых игр.Я использую OpenGL с GLFW.Моя цель - сделать что-то похожее на различные графические фреймворки, такие как raylib или openFrameworks (но, конечно, урезанные)

На самом деле пока все работает нормально, но я не могу понять, как правильно отделить вход откласс окна, поскольку наличие ввода дескриптора окна мне кажется довольно неуклюжим и просто загромождает класс окна.

Это быстрое упрощенное воссоздание моего класса окна.(Я не включил класс enum в коды клавиш.)

#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;
}

Как я могу исходить из этого?Моя главная проблема в том, что я не могу соединить свое окно и класс ввода.Должен ли я использовать классы наследования или друзей.Должны ли я иметь обратные вызовы glfw в классе окна (который я предполагаю) или я должен переместить их в класс ввода?Как я могу соединить эти два класса, чтобы мне не всегда приходилось использовать указатели окон, например «isKeyDown (GLFWwindow * window, Key keycode)», а вместо этого только «isKeyDown (Key keycode)».Если это не слишком много, спросите, может кто-нибудь написать упрощенный класс ввода?

Заранее спасибо

1 Ответ

0 голосов
/ 09 апреля 2019

Следует отметить, что GLFW обеспечивает обработку ввода как по запросу, так и по запросу. В вашем примере вы используете оба: опрос для обработки клавиш A и B и обратный вызов для обработки клавиши escape . Я считаю, что с обратным вызовом будет проще работать при создании отдельного класса ввода (для клавиатуры и мыши).

Существует множество способов создания класса KeyInput с использованием метода обратного вызова GLFW. Вот мой предпочтительный метод (написан для вашего кода). Он допускает несколько KeyInput экземпляров (в отличие от одиночного), что означает, что вы можете иметь экземпляр KeyInput для ключей пользовательского интерфейса, экземпляр для внутриигровых ключей и т. д.

В итоге: каждый KeyInput контролирует нажатие состояния списка клавиш, определенных пользователем при создании. Он имеет геттер и сеттер для доступа к нажатому состоянию для любой клавиши (хотя геттер и сеттер работают только с контролируемыми клавишами). Всякий раз, когда вызывается обратный вызов GLFW (статический), он вызывает этот установщик для всех экземпляров из KeyInput . Экземпляры хранятся в статическом векторе, который добавляется при построении и удаляется при уничтожении.

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);
  }
}
...