跟着cherno手搓游戏引擎【7】Input轮询

在引擎程序中任何时间,任何位置都能知道按键是否按下、鼠标的位置等等信息。

与事件系统的区别:事件系统是在按下时调用并传递按键状态;轮询是每时每刻都能获取按键状态

创建基类:

YOTO/Input.h:名如其意

cpp 复制代码
#pragma once
#include"YOTO/Core.h"
namespace YOTO {
	class YOTO_API Input {
	public:
		inline static bool IsKeyPressed(int keycode){
			return s_Instance->IsKeyPressedImpl(keycode);
		}
		inline static bool IsMouseButtonPressed(int button) {
			return s_Instance->IsMouseButtonPressedImpl(button);
		}
		inline static float GetMouseX() {
			return s_Instance->GetMouseXImpl();
		}
		inline static float GetMouseY() {
			return s_Instance->GetMouseYImpl();
		}
		inline static std::pair<float,float> GetMousePostion() {
			return s_Instance->GetMousePositionImpl();
		}
	protected:
		virtual bool IsKeyPressedImpl(int keycode )=0;
		virtual bool IsMouseButtonPressedImpl(int button) = 0;
		virtual float GetMouseXImpl() = 0;
		virtual float GetMouseYImpl() = 0;
		virtual  std::pair<float, float>  GetMousePositionImpl() = 0;

	private:
		static  Input* s_Instance;
	};
}

实现基类:

在Platform/Windows/下创建WindowsInput.h:

cpp 复制代码
#pragma once
#include"YOTO/Input.h"
namespace YOTO {
	class WindowsInput :public Input {
	protected:
		virtual bool IsKeyPressedImpl(int keycode) override;
		virtual bool IsMouseButtonPressedImpl(int button)override;
		virtual  std::pair<float, float>  GetMousePositionImpl()override;
		virtual float GetMouseXImpl()override;
		virtual float GetMouseYImpl() override;
	};
}

WindowsInput.cpp:获取window然后用glfw自带的事件检测来

cpp 复制代码
#include"ytpch.h"
#include"WindowsInput.h"
#include<GLFW/glfw3.h>
#include"YOTO/Application.h"
namespace YOTO {

	Input* Input::s_Instance = new WindowsInput();
	bool WindowsInput::IsKeyPressedImpl(int keycode)
	{// 获取GLFW原生窗口void*,转为GLFWwindow*
		auto window =static_cast<GLFWwindow*>( Application::Get().GetWindow().GetNativeWindow());
		// 用已有的GLFW函数来获取按键状态
		auto state=	glfwGetKey(window, keycode);
		return state==GLFW_PRESS|| state== GLFW_REPEAT;
	}
	bool WindowsInput::IsMouseButtonPressedImpl(int button)
	{
		auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
		auto state = glfwGetMouseButton(window, button);
		return state == GLFW_PRESS ;
	}
	std::pair<float, float> WindowsInput::GetMousePositionImpl()
	{
		auto window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
		double xpos, ypos;
		glfwGetCursorPos(window, &xpos, &ypos);
		return {(float)xpos,(float)ypos};
	}
	float WindowsInput::GetMouseXImpl()
	{
		auto [x, y] = GetMousePositionImpl();

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

		return y;
	}
}

为了能在任何时候获取到GLFWwindow,在Window.h中创建方法获取Window

cpp 复制代码
#pragma once

#include"ytpch.h"
#include"YOTO/Core.h"
#include"YOTO/Event/Event.h"
namespace YOTO {
	struct WindowProps {
		std::string Title;
		unsigned int Width;
		unsigned int Height;
		WindowProps(const std::string &title="YOTO Engine",unsigned int width =1280, unsigned int height = 1280 )
			:Title(title),Width(width),Height(height){}
	};
	class YOTO_API Window {
	public:
		//用EventCallbackFn代替std::function<void(Event&)>:输入为Event&返回值为void 的函数
		using EventCallbackFn = std::function<void(Event&)>;
		virtual ~Window(){}
		//=0为纯虚函数
		virtual void OnUpdate() = 0;
		virtual unsigned int GetWidth() const = 0;
		virtual unsigned int GetHeight() const = 0;
		
		virtual void SetEventCallback(const EventCallbackFn& callback) = 0;
		virtual void SetVSync(bool enable)= 0;
		virtual bool IsVSync() const = 0;
		//返回当前窗口
		virtual void* GetNativeWindow() const=0;

		static Window* Creat(const WindowProps& props = WindowProps());

	};
}

WindowsWindow.h :实现返回window的方法:

cpp 复制代码
#pragma once
#include "YOTO/Window.h"
#include<GLFW/glfw3.h>
#include"YOTO/Log.h"
struct GLFWwindow;
namespace YOTO {
	class WindowsWindow :public Window
	{
	public :
		WindowsWindow(const WindowProps& props);
		virtual ~WindowsWindow();
		void OnUpdate() override;
		
		inline unsigned int GetWidth() const override { return m_Data.Width; };
		inline unsigned int GetHeight() const override { return m_Data.Height; };

		inline void SetEventCallback(const EventCallbackFn& callback)override
		{ m_Data.EventCallback = callback; };
		void SetVSync(bool enable) ;
		bool IsVSync()const;
		//返回window
		inline virtual void* GetNativeWindow() const { return m_Window; }
	private: 
		virtual void Init(const WindowProps& props);
		virtual void ShutDown();
	private:
		GLFWwindow* m_Window;
		struct WindowData {
			std::string Title;
			unsigned int Width, Height;
			bool VSync;
			EventCallbackFn EventCallback;
		};
		WindowData m_Data;
	};
}

Application.cpp:在Run中获取鼠标位置:

cpp 复制代码
#include"ytpch.h"
#include "Application.h"

#include"Log.h"
#include<glad/glad.h>
#include"Input.h"
namespace YOTO {
#define BIND_EVENT_FN(x) std::bind(&x, this, std::placeholders::_1)

	 Application* Application::s_Instance = nullptr;

	Application::Application() {

		YT_CORE_ASSERT(!s_Instance, "Application需要为空!")
		s_Instance = this;
		//智能指针
		m_Window = std::unique_ptr<Window>(Window::Creat());
		//设置回调函数
		m_Window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));
		unsigned int id;
		glGenBuffers(1, &id);
	}
	Application::~Application() {

	}
	/// <summary>
	/// 所有的Window事件都会在这触发,作为参数e
	/// </summary>
	/// <param name="e"></param>
	void Application::OnEvent(Event& e) {
		//根据事件类型绑定对应事件
		EventDispatcher dispatcher(e);
		dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(Application::OnWindowClosed));
		//输出事件信息
		YT_CORE_INFO("{0}",e);
		for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
			(*--it)->OnEvent(e);
			if (e.m_Handled)
				break;
		}
	}

	bool Application::OnWindowClosed(WindowCloseEvent& e) {
		m_Running = false;
		return true;
	}
	void Application::Run() {
		WindowResizeEvent e(1280, 720);
		if (e.IsInCategory(EventCategoryApplication)) {
			YT_CORE_TRACE(e);
		}
		if (e.IsInCategory(EventCategoryInput)) {
			YT_CORE_ERROR(e);
		}

		while (m_Running)
		{
			glClearColor(1,0,1,1);
			glClear(GL_COLOR_BUFFER_BIT);

			for (Layer* layer : m_LayerStack) {
				layer->OnUpdate();
			}
			auto [x, y] = Input::GetMousePostion();
			YT_CORE_TRACE("{0},{1}",x, y);
			m_Window->OnUpdate();
		}
	}
	void Application::PushLayer(Layer* layer) {
		m_LayerStack.PushLayer(layer);
		layer->OnAttach();
	}
	void Application::PushOverlay(Layer* layer) {
		m_LayerStack.PushOverlay(layer);
		layer->OnDetach();
	}
}

测试:

小改动:

Core.h:

cpp 复制代码
#pragma once
//用于dll的宏
#ifdef YT_PLATFORM_WINDOWS
#ifdef YT_BUILD_DLL
#define YOTO_API __declspec(dllexport) 
#else
#define YOTO_API __declspec(dllimport) 

#endif // DEBUG
#else
#error YOTO_ONLY_SUPPORT_WINDOWS
#endif // YOTO_PLATFORM_WINDOWS

#ifdef YT_DEBUG
#define YT_ENABLE_ASSERTS
#endif

#ifdef YT_ENABLE_ASSERTS
#define YT_CLIENT_ASSERT(x,...) {if(!(x)){YT_CLIENT_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#define YT_CORE_ASSERT(x,...) {if(!(x)){YT_CORE_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#else
#define YT_CLIENT_ASSERT(x,...)
#define YT_CORE_ASSERT(x,...)

#endif // YT_ENABLE_ASSERTS



#define BIT(x)(1<<x)
//绑定事件定义
#define YT_BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1)

SRC下的premake5.lua:

Lua 复制代码
workspace "YOTOEngine"		-- sln文件名
	architecture "x64"	
	configurations{
		"Debug",
		"Release",
		"Dist"
	}
startproject "Sandbox"
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"
-- 包含相对解决方案的目录
IncludeDir={}
IncludeDir["GLFW"]="YOTOEngine/vendor/GLFW/include"
IncludeDir["Glad"]="YOTOEngine/vendor/Glad/include"
IncludeDir["ImGui"] ="YOTOEngine/vendor/imgui"
--项目中包含某包
include "YOTOEngine/vendor/GLFW"
include "YOTOEngine/vendor/Glad"
include "YOTOEngine/vendor/imgui"

project "YOTOEngine"		--YOTOEngine项目
	location "YOTOEngine"--在sln所属文件夹下的YOTOEngine文件夹
	kind "SharedLib"--dll动态库
	language "C++"
	targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录
	objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录

	staticruntime "Off"

	pchheader "ytpch.h"
	pchsource "YOTOEngine/src/ytpch.cpp"
	-- 包含的所有h和cpp文件
	files{
		"%{prj.name}/src/**.h",
		"%{prj.name}/src/**.cpp"
	}
	-- 包含目录
	includedirs{
		"%{prj.name}/src",
		"%{prj.name}/vendor/spdlog-1.x/include",
		"%{IncludeDir.GLFW}",
		"%{IncludeDir.Glad}",
		"%{IncludeDir.ImGui}"
	}
	links{
		"GLFW",-- GLFW.lib库链接到YOTOEngine项目中
		"Glad",-- Glad.lib库链接到YOTOEngine项目中
		"ImGui",-- ImGui.lib库链接到YOTOEngine项目中
		"opengl32.lib"
	}
	-- 如果是window系统
	filter "system:windows"
		cppdialect "C++17"
		-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;
		-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错
		systemversion "latest"	-- windowSDK版本
		-- 预处理器定义
		defines{
			"YT_PLATFORM_WINDOWS",
			"YT_BUILD_DLL",
			-- "YT_ENABLE_ASSERTS",
			"GLFW_INCLUDE_NONE"-- 让GLFW不包含OpenGL
		}
		-- 编译好后移动Hazel.dll文件到Sandbox文件夹下
		postbuildcommands{
			("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
		}
	-- 不同配置下的预定义不同
	filter "configurations:Debug"
		defines "YT_DEBUG"
		runtime "Debug"
		symbols "On"

	filter "configurations:Release"
		defines "YT_RELEASE"
		runtime "Release"
		optimize "On"

	filter "configurations:Dist"
		defines "YT_DIST"
		runtime "Release"
		optimize "On"

project "Sandbox"
	location "Sandbox"
	kind "ConsoleApp"
	language "C++"
	staticruntime "Off"
	targetdir ("bin/" .. outputdir .. "/%{prj.name}")
	objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

	files{
		"%{prj.name}/src/**.h",
		"%{prj.name}/src/**.cpp"
	}
	-- 同样包含spdlog头文件
	includedirs{
		"YOTOEngine/vendor/spdlog-1.x/include",
		"YOTOEngine/src"
	}
	-- 引用YOTOEngine
	links{
		"YOTOEngine",
		"GLFW",
		"opengl32.lib"
	}

	filter "system:windows"
		cppdialect "C++17"
		systemversion "latest"

		defines{
			"YT_PLATFORM_WINDOWS"
		}

	filter "configurations:Debug"
		defines "YT_DEBUG"
		runtime "Debug"
		symbols "On"

	filter "configurations:Release"
		defines "YT_RELEASE"
		runtime "Release"
		optimize "On"

	filter "configurations:Dist"
		defines "YT_DIST"
		runtime "Release"
		optimize "On"
相关推荐
AA陈超几秒前
虚幻引擎5 GAS开发俯视角RPG游戏 P07-18.生成火球术
c++·游戏·ue5·游戏引擎·虚幻
Doc.S4 小时前
多无人机任务自定义(基于ZJU-FAST-Lab / EGO-Planner-v2)
游戏引擎·无人机·cocos2d
nnsix7 小时前
Unity OpenXR开发HTC Vive Cosmos
unity·游戏引擎
nnsix7 小时前
Unity OpenXR,扳机键交互UI时,必须按下扳机才触发
unity·游戏引擎
老朱佩琪!7 小时前
Unity访问者模式
unity·游戏引擎·访问者模式
不定时总结的那啥8 小时前
Unity实现点击Console消息自动选中预制体的方法
unity·游戏引擎
nnsix8 小时前
Unity OpenXR 关闭手柄的震动
unity·游戏引擎
CreasyChan8 小时前
Unity 中的反射使用详解
unity·c#·游戏引擎·游戏开发
天途小编9 小时前
无人机操控模式解析:美国手、日本手、中国手
游戏引擎·无人机·cocos2d
90后小陈老师1 天前
Unity教学 项目2 2D闯关游戏
游戏·unity·游戏引擎