news 2026/5/30 12:27:30

XEngine开发日记(day4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XEngine开发日记(day4)

Input系统

为了实现面向不同系统时接收输入的能力,因此需要实现Input系统。我们定义一个Input基类,表示所有输入类要实现的方法,他基本上就是静态类和虚函数接口。
Input.h

#pragmaonce#include"core.h"namespaceXEngine{classX_APIInput{public:inlinestaticboolIsKeyPressed(intkey){returns_Instance->IsKeyPressedImpl(key);}inlinestaticboolIsMouseButtonPressed(intbutton){returns_Instance->IsMouseButtonPressedImpl(button);}inlinestaticfloatGetMouseX(){returns_Instance->GetMouseXImpl();}inlinestaticfloatGetMouseY(){returns_Instance->GetMouseYImpl();}inlinestaticstd::pair<float,float>GetMousePosition(){returns_Instance->GetMousePositionImpl();}protected:virtualboolIsKeyPressedImpl(intkey)=0;virtualboolIsMouseButtonPressedImpl(intbutton)=0;virtualfloatGetMouseXImpl()=0;virtualstd::pair<float,float>GetMousePositionImpl()=0;virtualfloatGetMouseYImpl()=0;private:staticInput*s_Instance;};}

WindowsInput

通过继承Input,我们实现面向Windows平台的输入类。

//WindowsInput.h#pragmaonce#include"XEngine/core.h"#include"XEngine/Input.h"namespaceXEngine{classX_APIWindowsInput:publicInput{public:protected:virtualboolIsKeyPressedImpl(intkey)override;virtualboolIsMouseButtonPressedImpl(intbutton)override;virtualfloatGetMouseXImpl()override;virtualfloatGetMouseYImpl()override;virtualstd::pair<float,float>GetMousePositionImpl()override;};}//WindowsInput.cpp#include"xepch.h"#include"WindowsInput.h"#include"XEngine/Application.h"#include<GLFW/glfw3.h>namespaceXEngine{Input*Input::s_Instance=newWindowsInput();boolWindowsInput::IsKeyPressedImpl(intkey){autowindow=static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());autostate=glfwGetKey(window,key);returnstate==GLFW_PRESS||state==GLFW_REPEAT;}boolWindowsInput::IsMouseButtonPressedImpl(intbutton){autowindow=static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());autostate=glfwGetMouseButton(window,button);returnstate==GLFW_PRESS;}floatWindowsInput::GetMouseXImpl(){auto[x,y]=GetMousePositionImpl();return(float)x;}floatWindowsInput::GetMouseYImpl(){auto[x,y]=GetMousePositionImpl();returny;}std::pair<float,float>WindowsInput::GetMousePositionImpl(){autowindow=static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());doublexpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);return{xpos,ypos};}}
  • 通过实现接口来实现输入功能,包括获取键盘按键状态,获取鼠标按键状态,获取鼠标位置等。
  • 其中返回鼠标位置时使用了
    结构化绑定(Structured Binding)是 C++17 引入的一项特性,它允许你将一个结构体、数组、元组或类似聚合类型的多个成员一次性解包到多个独立的变量中,从而简化代码并提高可读性。

在你的代码中,GetMousePositionImpl()函数返回一个std::pair<float, float>。在 C++17 之前,你需要这样获取其成员:

std::pair<float,float>pos=GetMousePositionImpl();floatx=pos.first;floaty=pos.second;

或者使用std::tie

floatx,y;std::tie(x,y)=GetMousePositionImpl();

而使用 C++17 的结构化绑定,你可以直接写成:

auto[x,y]=GetMousePositionImpl();

这行代码做了以下几件事:

  1. auto让编译器自动推导类型。
  2. [x, y]是结构化绑定的语法,它声明了两个变量xy
  3. 函数返回的std::pair<float, float>被自动“解包”,first成员赋值给xsecond成员赋值给y

结构化绑定的主要优点:

  • 代码简洁:一行代码完成声明和赋值。
  • 意图清晰:直接表明你要解包这个结构。
  • 避免中间变量:无需先声明一个std::pair临时对象。

适用场景:
除了std::pair,结构化绑定还适用于:

  • std::tuple
  • std::array和原生数组
  • 结构体或类(其所有非静态数据成员必须是 public 的)

在你的WindowsInput.cpp中的使用:

floatWindowsInput::GetMouseXImpl(){auto[x,y]=GetMousePositionImpl();// 使用结构化绑定解包鼠标坐标return(float)x;}

这里,GetMousePositionImpl()返回的pair被直接解包到xy中,然后函数返回x。这使得代码非常直观,一眼就能看出x是鼠标的横坐标。

因此,结构化绑定是一种让处理复合返回值(如坐标、多返回值函数)的代码变得更优雅和高效的现代 C++ 特性。

  • 除此之外,由于在接口实现中需要使用到原生的glfw窗口,因此我们在Windowswindow.cpp中新加一个返回原生窗口的方法,用于返回保存的glfw窗口实例。
//Window.hvirtualvoid*GetNativeWindow()const=0;//WindowsWindowinlinevirtualvoid*GetNativeWindow()constoverride{returnm_Window;}

本地化keycode

为了保持不同系统间的代码复用,我们需要一套自己的按键代码与目标平台的代码转换,一般有两种思路,一种是将代码存储在文件中,通过宏定义确认取用不同的按键码。一种是通过转换函数将目标码转换为自己的代码或者将自己的代码转换为目标码。这里直接存储glfw的代码作为引擎代码。

//KeysCode.h#pragmaonce#pragmaonce// From glfw3.h/* Printable keys */#defineX_KEY_SPACE32#defineX_KEY_APOSTROPHE39/* ' */#defineX_KEY_COMMA44/* , */#defineX_KEY_MINUS45/* - */#defineX_KEY_PERIOD46/* . */#defineX_KEY_SLASH47/* / */#defineX_KEY_048#defineX_KEY_149#defineX_KEY_250#defineX_KEY_351#defineX_KEY_452#defineX_KEY_553#defineX_KEY_654#defineX_KEY_755#defineX_KEY_856#defineX_KEY_957#defineX_KEY_SEMICOLON59/* ; */#defineX_KEY_EQUAL61/* = */#defineX_KEY_A65#defineX_KEY_B66#defineX_KEY_C67#defineX_KEY_D68#defineX_KEY_E69#defineX_KEY_F70#defineX_KEY_G71#defineX_KEY_H72#defineX_KEY_I73#defineX_KEY_J74#defineX_KEY_K75#defineX_KEY_L76#defineX_KEY_M77#defineX_KEY_N78#defineX_KEY_O79#defineX_KEY_P80#defineX_KEY_Q81#defineX_KEY_R82#defineX_KEY_S83#defineX_KEY_T84#defineX_KEY_U85#defineX_KEY_V86#defineX_KEY_W87#defineX_KEY_X88#defineX_KEY_Y89#defineX_KEY_Z90#defineX_KEY_LEFT_BRACKET91/* [ */#defineX_KEY_BACKSLASH92/* \ */#defineX_KEY_RIGHT_BRACKET93/* ] */#defineX_KEY_GRAVE_ACCENT96/* ` */#defineX_KEY_WORLD_1161/* non-US #1 */#defineX_KEY_WORLD_2162/* non-US #2 *//* Function keys */#defineX_KEY_ESCAPE256#defineX_KEY_ENTER257#defineX_KEY_TAB258#defineX_KEY_BACKSPACE259#defineX_KEY_INSERT260#defineX_KEY_DELETE261#defineX_KEY_RIGHT262#defineX_KEY_LEFT263#defineX_KEY_DOWN264#defineX_KEY_UP265#defineX_KEY_PAGE_UP266#defineX_KEY_PAGE_DOWN267#defineX_KEY_HOME268#defineX_KEY_END269#defineX_KEY_CAPS_LOCK280#defineX_KEY_SCROLL_LOCK281#defineX_KEY_NUM_LOCK282#defineX_KEY_PRINT_SCREEN283#defineX_KEY_PAUSE284#defineX_KEY_F1290#defineX_KEY_F2291#defineX_KEY_F3292#defineX_KEY_F4293#defineX_KEY_F5294#defineX_KEY_F6295#defineX_KEY_F7296#defineX_KEY_F8297#defineX_KEY_F9298#defineX_KEY_F10299#defineX_KEY_F11300#defineX_KEY_F12301#defineX_KEY_F13302#defineX_KEY_F14303#defineX_KEY_F15304#defineX_KEY_F16305#defineX_KEY_F17306#defineX_KEY_F18307#defineX_KEY_F19308#defineX_KEY_F20309#defineX_KEY_F21310#defineX_KEY_F22311#defineX_KEY_F23312#defineX_KEY_F24313#defineX_KEY_F25314#defineX_KEY_KP_0320#defineX_KEY_KP_1321#defineX_KEY_KP_2322#defineX_KEY_KP_3323#defineX_KEY_KP_4324#defineX_KEY_KP_5325#defineX_KEY_KP_6326#defineX_KEY_KP_7327#defineX_KEY_KP_8328#defineX_KEY_KP_9329#defineX_KEY_KP_DECIMAL330#defineX_KEY_KP_DIVIDE331#defineX_KEY_KP_MULTIPLY332#defineX_KEY_KP_SUBTRACT333#defineX_KEY_KP_ADD334#defineX_KEY_KP_ENTER335#defineX_KEY_KP_EQUAL336#defineX_KEY_LEFT_SHIFT340#defineX_KEY_LEFT_CONTROL341#defineX_KEY_LEFT_ALT342#defineX_KEY_LEFT_SUPER343#defineX_KEY_RIGHT_SHIFT344#defineX_KEY_RIGHT_CONTROL345#defineX_KEY_RIGHT_ALT346#defineX_KEY_RIGHT_SUPER347#defineX_KEY_MENU348//MouseButtonCodes.h#pragmaonce// From glfw3.h#defineNUT_MOUSE_BUTTON_10#defineNUT_MOUSE_BUTTON_21#defineNUT_MOUSE_BUTTON_32#defineNUT_MOUSE_BUTTON_43#defineNUT_MOUSE_BUTTON_54#defineNUT_MOUSE_BUTTON_65#defineNUT_MOUSE_BUTTON_76#defineNUT_MOUSE_BUTTON_87#defineNUT_MOUSE_BUTTON_LASTNUT_MOUSE_BUTTON_8#defineNUT_MOUSE_BUTTON_LEFTNUT_MOUSE_BUTTON_1#defineNUT_MOUSE_BUTTON_RIGHTNUT_MOUSE_BUTTON_2#defineNUT_MOUSE_BUTTON_MIDDLENUT_MOUSE_BUTTON_3

然后我们就可以在layer中进行使用了。
测试一下。
Sandbox.cpp

voidOnEvent(XEngine::Event&event)override{if(event.GetEventType()==XEngine::EventType::KeyPressed){XEngine::KeyPressedEvent&e=(XEngine::KeyPressedEvent&)event;if(e.GetKeyCode()==X_KEY_TAB)X_TRACE("{0} is pressed","Tab");X_TRACE("{0} is pressed",(char)e.GetKeyCode());}}

让我们改造一下Sandbox的exampleLayer
通过判断是否按下了X_KEY_TAB来证明我们的按键系统正确的工作。

当我们按下 Tab 时,可以看到正确打印了 “Tab”。但是转换为char的那行并没有正确打印,这是因为X_KEY_TAB的值为 258,而char类型(通常为有符号 8 位整数)的范围是 -128 到 127(或 0 到 255 对于无符号unsigned char)。将 258 强制转换为char会导致溢出,得到一个不可打印的字符(或乱码)。

解决方案:

  1. 避免对非 ASCII 字符进行char转换:像 Tab、Enter、方向键等功能键的键码通常大于 255,不应直接当作字符打印。
  2. 使用条件判断或映射:对于特殊功能键,可以单独处理或使用查找表将其映射为可读的字符串。

例如,修改您的OnEvent函数:

voidOnEvent(XEngine::Event&event)override{if(event.GetEventType()==XEngine::EventType::KeyPressed){XEngine::KeyPressedEvent&e=(XEngine::KeyPressedEvent&)event;intkeyCode=e.GetKeyCode();if(keyCode==X_KEY_TAB){X_TRACE("Tab is pressed");// 对于 Tab 等特殊键,不尝试转换为 char// 或者可以映射为字符串X_TRACE("Key pressed: [Tab] (code: {0})",keyCode);}elseif(keyCode>=32&&keyCode<=126){// 仅当键码在可打印 ASCII 范围内时转换为 charX_TRACE("Key pressed: '{0}' (code: {1})",(char)keyCode,keyCode);}else{// 其他功能键X_TRACE("Key pressed: [Special Key] (code: {0})",keyCode);}}}

根本原因glfwGetKey返回的键码(如GLFW_KEY_TAB)是 GLFW 定义的整型常量,它们与 ASCII 码并不一一对应。直接将其强制转换为char只对部分字母、数字和标点符号有效(键码与 ASCII 码重合的部分)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/30 12:26:56

终极AMD处理器调试指南:7步掌握Ryzen性能优化技巧

终极AMD处理器调试指南&#xff1a;7步掌握Ryzen性能优化技巧 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/5/30 12:26:17

别再收藏杂七杂八的链接了!一个网站搞定开发调试所有需求

【写在前面】 作为开发者&#xff0c;日常工作中少不了各种"小工具"&#xff1a; 调试接口时&#xff0c;需要格式化返回的JSON 写正则时&#xff0c;需要在线测试匹配结果 处理时间戳时&#xff0c;需要快速转换成日期 看日志时&#xff0c;需要Base64编解码 传代码…

作者头像 李华
网站建设 2026/5/30 12:25:03

Nintendo Switch注入完整指南:使用TegraRcmGUI实现一键Payload注入

Nintendo Switch注入完整指南&#xff1a;使用TegraRcmGUI实现一键Payload注入 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI TegraRcmGUI是一款专为Ninten…

作者头像 李华
网站建设 2026/5/30 12:21:55

开源阅读鸿蒙版:打造你的专属数字图书馆,告别平台限制

开源阅读鸿蒙版&#xff1a;打造你的专属数字图书馆&#xff0c;告别平台限制 【免费下载链接】legado-Harmony 开源阅读鸿蒙版仓库 项目地址: https://gitcode.com/gh_mirrors/le/legado-Harmony 你是否厌倦了在不同阅读应用间频繁切换&#xff1f;是否受够了无处不在的…

作者头像 李华
网站建设 2026/5/30 12:20:40

混多功能合混合按键处理

/************************************************************* brief 按键驱动* param NULL* return NULL* author * date 2018-xx-xx* version v1.0* note button.c***********************************************************/ #include "button.h&qu…

作者头像 李华