拿到input输入的时间_C++ Game Engine (Hazel) 学习笔记 - 7. GLM/输入/KeyCode

简介

这一篇中,主要介绍GLM的导入和设置,Input System的搭建,以及使用跨平台的KeyCode。

GLM全程OpenGL Mathematics,主要是一个用于辅助图形编程的数学库,其中定义了包括常用的vec3,vec4,mat3,mat4等数据类型,为之后的渲染做好铺垫。


项目的导入

也是和其他项目一样,用submodule就可以导入了,不再赘述

git submodule add https://github.com/g-truc/glm JEngine/ThirdParty/glm

Premake.lua

// 在table里面加入一个key pair
IncludeDir["glm"] = "JEngine/ThirdParty/glm"

//...省略
//选择要包括进引擎项目的文件目录
files
{
    "%{prj.name}/src/**.h",
    "%{prj.name}/src/**.cpp",
    "%{prj.name}/ThirdParty/glm/glm/**.hpp",
    "%{prj.name}/ThirdParty/glm/glm/**.inl",
}

//...省略
//用于#include查找的目录,在JEngine中
includedirs
{
    "%{prj.name}/src/Public",
    "%{IncludeDir.GLFW}",
    "%{IncludeDir.Glad}",
    "%{IncludeDir.Imgui}",
    "%{IncludeDir.glm}"
}

//...省略
//用于#include查找的目录,在Sandbox中
includedirs
{
    "JEngine/ThirdParty/SpdLog/include",
    "JEngine/src",
    "%{IncludeDir.glm}"
}

输入

接下来说说输入,对于一般的键鼠操作而言,输入其实仅仅只是一个keycode事件的callback,其基本可以分为鼠标的事件和键盘的事件,从代码中看:

//Input.h
#pragma once

#include "Core.h"

namespace JEngine
{
	class Input
	{
	public:
		inline static bool IsKeyDown(int keycode) { return s_Instance->IsKeyDownImpl(keycode); };
		inline static bool IsMouseButtonDown(int mouseButton) { return s_Instance->IsMouseButtonDownImpl(mouseButton); };
		inline static std::pair<float, float> GetMousePos() { return s_Instance->GetMousePosImpl(); };
		inline static float GetMouseX() { return s_Instance->GetMouseXImpl(); };
		inline static float GetMouseY() { return s_Instance->GetMouseYImpl(); };
		
	protected:
		virtual bool IsKeyDownImpl(int keycode) = 0;
		virtual bool IsMouseButtonDownImpl(int mouseButton) = 0;
		virtual std::pair<float, float> GetMousePosImpl() = 0;
		virtual float GetMouseXImpl() = 0;
		virtual float GetMouseYImpl() = 0;

	private:
		static Input* s_Instance;
	};
}

不难看出,这个Input是一个singleton的接口,而且甚至没有构造函数,意味着不能直接把这个类实例出来。所有的public函数都是静态函数,并且只是protected函数实现的一个wrap,再看这些protected函数都是纯虚函数/抽象函数,这也意味着派生类必须根据平台做不同的实现。而高层逻辑在调用时,则不用考虑具体实现,直接从这里调用静态函数即可。


WindowsInput.h/.cpp

既然说到了不同平台的实现,就直接看一下代码:

#pragma once
#include "Input.h"

namespace JEngine
{
	class WindowsInput : public Input
	{
	protected:
		virtual bool IsKeyDownImpl(int keycode) override;
		virtual bool IsMouseButtonDownImpl(int mouseButton) override;
		virtual std::pair<float, float> GetMousePosImpl() override;
		virtual float GetMouseXImpl() override;
		virtual float GetMouseYImpl() override;
	};
}

非常简单直白的,只是给纯虚函数一个override定义而已。

#include "JE_PCH.h"
#include "Platform/Windows/WindowsInput.h"

#include "Application.h"
#include "GLFW/glfw3.h"

namespace JEngine
{
	Input* Input::s_Instance = new WindowsInput();

	bool WindowsInput::IsKeyDownImpl(int keycode)
	{
		auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
		auto status = glfwGetKey(window, keycode);
		return status == GLFW_PRESS || status == GLFW_REPEAT;
	}

	bool WindowsInput::IsMouseButtonDownImpl(int mouseButton)
	{
		auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
		auto status = glfwGetKey(window, mouseButton);
		return status == GLFW_PRESS;
	}

	std::pair<float, float> WindowsInput::GetMousePosImpl()
	{
		auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
		double x, y;
		glfwGetCursorPos(window, &x, &y);
		return { (float)x, (float)y };
	}

	float WindowsInput::GetMouseXImpl()
	{
		auto [x, y] = GetMousePosImpl();
		return x;
	}

	float WindowsInput::GetMouseYImpl()
	{
		auto [x, y] = GetMousePosImpl();
		return y;
	}

}

首先要说的是,由于目前还没有资源管理器,所以逻辑中有相当一部分的raw pointer和new关键字来开辟新的内存空间,这些在日后资源管理一章中会重新讨论并冲构成对应的内存管理方式。

接下来的逻辑其实很好理解,本质上IsKey/MouseButtonDownImpl就三行:

auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
auto status = glfwGetKey(window, mouseButton);
return status == GLFW_PRESS;
  1. 找到当前input所对应平台的window实例,cast成GLFWwindow指针。
  2. glfwGetKey用于找到给定window中,最后一次输入的指定Key的状态。
  3. 这个状态包含三种,松开,按下,重复。
  4. 判断是否是按下或重复状态,返回判断的结果。

d40a1974a60c93d06525f137c537ec41.png

GetMousePosImpl也是类似的,glfwGetCursorPos(window, &x, &y)可以拿到给定window中鼠标的position,并且赋值给引用传参进来的x和y两个double。


GetNativeWindow

在上文中需要注意的是,拿到当前window的这行逻辑:

auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());

还并没有实现,就简单介绍一下这段逻辑实际上做的事:

  1. Application::Get()返回了Application实例的引用。
  2. GetWindow()返回了当前Application的主Window的引用。
  3. GetNativeWindow()返回了实际平台的Window的指针。
  4. 将第三步拿到的指针静态cast成GLFWwindow*
  5. 赋值给window变量。

也就是说:

//Application.h
inline static Application& Get() { return *s_Instance; }
inline Window& GetWindow() { return *m_Window; }

//...
private:
    unique_ptr<Window> m_Window;
    static Application* s_Instance;
//...

然后在Window.h中:

public:
    virtual void* GetNativeWindow() const = 0;

最后在WindowsWindow中:

inline virtual void* GetNativeWindow() const override { return m_Window; }

//...

private:
    GLFWwindow* m_Window;

这样,就完成了拿到GLFW所需要的glfwWindow的方式,也可以看出,在这里WindowsInput和WindowsWindow是紧密绑定的。(也就是说WindowsInput并不可能拿到其他不使用GLFW实现的window的返回值,因为input本身也在使用GLFW,其它的平台静态转换不会成功)


KeyCodes

在输入系统写完之前还有最后一个问题,之前在ImguiLayer中,我们不得不使用glfw的KeyCode来进行编码,这就意味着只要用到了KeyCode,我们就不得不include GLFW,这就降低了跨平台的友好性,因此,我们会用自己的KeyCode来解决这件事:

//KeyCodes.h
#pragma once

// From glfw3.h
#define JE_KEY_SPACE              32
#define JE_KEY_APOSTROPHE         39  /* ' */
#define JE_KEY_COMMA              44  /* , */
#define JE_KEY_MINUS              45  /* - */
#define JE_KEY_PERIOD             46  /* . */
#define JE_KEY_SLASH              47  /* / */
#define JE_KEY_0                  48
#define JE_KEY_1                  49
#define JE_KEY_2                  50
#define JE_KEY_3                  51
#define JE_KEY_4                  52
#define JE_KEY_5                  53
#define JE_KEY_6                  54
#define JE_KEY_7                  55
#define JE_KEY_8                  56
#define JE_KEY_9                  57
#define JE_KEY_SEMICOLON          59  /* ; */
#define JE_KEY_EQUAL              61  /* = */
#define JE_KEY_A                  65
#define JE_KEY_B                  66
#define JE_KEY_C                  67
#define JE_KEY_D                  68
#define JE_KEY_E                  69
#define JE_KEY_F                  70
#define JE_KEY_G                  71
#define JE_KEY_H                  72
#define JE_KEY_I                  73
#define JE_KEY_J                  74
#define JE_KEY_K                  75
#define JE_KEY_L                  76
#define JE_KEY_M                  77
#define JE_KEY_N                  78
#define JE_KEY_O                  79
#define JE_KEY_P                  80
#define JE_KEY_Q                  81
#define JE_KEY_R                  82
#define JE_KEY_S                  83
#define JE_KEY_T                  84
#define JE_KEY_U                  85
#define JE_KEY_V                  86
#define JE_KEY_W                  87
#define JE_KEY_X                  88
#define JE_KEY_Y                  89
#define JE_KEY_Z                  90
#define JE_KEY_LEFT_BRACKET       91  /* [ */
#define JE_KEY_BACKSLASH          92  /*  */
#define JE_KEY_RIGHT_BRACKET      93  /* ] */
#define JE_KEY_GRAVE_ACCENT       96  /* ` */
#define JE_KEY_WORLD_1            161 /* non-US #1 */
#define JE_KEY_WORLD_2            162 /* non-US #2 */

/* Function keys */
#define JE_KEY_ESCAPE             256
#define JE_KEY_ENTER              257
#define JE_KEY_TAB                258
#define JE_KEY_BACKSPACE          259
#define JE_KEY_INSERT             260
#define JE_KEY_DELETE             261
#define JE_KEY_RIGHT              262
#define JE_KEY_LEFT               263
#define JE_KEY_DOWN               264
#define JE_KEY_UP                 265
#define JE_KEY_PAGE_UP            266
#define JE_KEY_PAGE_DOWN          267
#define JE_KEY_HOME               268
#define JE_KEY_END                269
#define JE_KEY_CAPS_LOCK          280
#define JE_KEY_SCROLL_LOCK        281
#define JE_KEY_NUM_LOCK           282
#define JE_KEY_PRINT_SCREEN       283
#define JE_KEY_PAUSE              284
#define JE_KEY_F1                 290
#define JE_KEY_F2                 291
#define JE_KEY_F3                 292
#define JE_KEY_F4                 293
#define JE_KEY_F5                 294
#define JE_KEY_F6                 295
#define JE_KEY_F7                 296
#define JE_KEY_F8                 297
#define JE_KEY_F9                 298
#define JE_KEY_F10                299
#define JE_KEY_F11                300
#define JE_KEY_F12                301
#define JE_KEY_F13                302
#define JE_KEY_F14                303
#define JE_KEY_F15                304
#define JE_KEY_F16                305
#define JE_KEY_F17                306
#define JE_KEY_F18                307
#define JE_KEY_F19                308
#define JE_KEY_F20                309
#define JE_KEY_F21                310
#define JE_KEY_F22                311
#define JE_KEY_F23                312
#define JE_KEY_F24                313
#define JE_KEY_F25                314
#define JE_KEY_KP_0               320
#define JE_KEY_KP_1               321
#define JE_KEY_KP_2               322
#define JE_KEY_KP_3               323
#define JE_KEY_KP_4               324
#define JE_KEY_KP_5               325
#define JE_KEY_KP_6               326
#define JE_KEY_KP_7               327
#define JE_KEY_KP_8               328
#define JE_KEY_KP_9               329
#define JE_KEY_KP_DECIMAL         330
#define JE_KEY_KP_DIVIDE          331
#define JE_KEY_KP_MULTIPLY        332
#define JE_KEY_KP_SUBTRACT        333
#define JE_KEY_KP_ADD             334
#define JE_KEY_KP_ENTER           335
#define JE_KEY_KP_EQUAL           336
#define JE_KEY_LEFT_SHIFT         340
#define JE_KEY_LEFT_CONTROL       341
#define JE_KEY_LEFT_ALT           342
#define JE_KEY_LEFT_SUPER         343
#define JE_KEY_RIGHT_SHIFT        344
#define JE_KEY_RIGHT_CONTROL      345
#define JE_KEY_RIGHT_ALT          346
#define JE_KEY_RIGHT_SUPER        347
#define JE_KEY_MENU               348

MouseCode也是类似的:

//MouseButtonCode.h
#pragma once

// From glfw3.h
#define JE_MOUSE_BUTTON_1         0
#define JE_MOUSE_BUTTON_2         1
#define JE_MOUSE_BUTTON_3         2
#define JE_MOUSE_BUTTON_4         3
#define JE_MOUSE_BUTTON_5         4
#define JE_MOUSE_BUTTON_6         5
#define JE_MOUSE_BUTTON_7         6
#define JE_MOUSE_BUTTON_8         7
#define JE_MOUSE_BUTTON_LAST      JE_MOUSE_BUTTON_8
#define JE_MOUSE_BUTTON_LEFT      JE_MOUSE_BUTTON_1
#define JE_MOUSE_BUTTON_RIGHT     JE_MOUSE_BUTTON_2
#define JE_MOUSE_BUTTON_MIDDLE    JE_MOUSE_BUTTON_3

总结

本篇我们导入了glm数学库,建立了自己的Input系统和KeyCode,这样一来我们就可以获取和监听按键和鼠标的事件,在任何时候去询问某个特定按键的状态,下一章重回Imgui,建立一个基本可用的Docking Imgui,并开始重构整理一些目前为止的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值