Hazel引擎自我学习历程-程序运行逻辑

文章详细描述了一个基于Hazel框架的应用程序启动过程,涉及EntryPoint、Application、Window、Sandbox和LayerStack的交互,展示了如何创建窗口、初始化GLFW和ImGui,以及事件处理和层栈管理的机制。
摘要由CSDN通过智能技术生成

 #程序入口

程序启动点文件EntryPoint.主函数在此文件之中,忽略日志类的设置。主要代码就是这三行

auto app = Hazel::CreateApplication();
app->Run();
delete app;
Hazel::Application* Hazel::CreateApplication()
{
    return new Sandbox();
}

**值得注意的是创建Sandbox子类会先在其内部创建一个父类实例,并且调用其构造函数后才调用子类构造函数。相当于子类内嵌了一个父类。

#Application-程序第一步

Application::Application()
	{
		HZ_CORE_ASSERT(!s_Instance, "Application already exists!");
		s_Instance = this;//不采用单例模式,而是自行判断APPlication是否只有一个实例

		m_Window = std::unique_ptr<Window>(Window::Create());//实际就是运转Init函数
		m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
	}

1.  m_Window = std::unique_ptr<Window>(Window::Create());   实际就是运转Init函数

//WindowsWindow.cpp文件下
---------------
Window* Window::Create(const WindowProps& props)
{
	return new WindowsWindow(props);
}

WindowsWindow::WindowsWindow(const WindowProps& props)
{
	Init(props);
}

void WindowsWindow::Init(const WindowProps& props)
{
	m_Data.Title = props.Title;
	m_Data.Width = props.Width;
	m_Data.Height = props.Height;
	HZ_CORE_INFO("Creating window {0} ({1}, {2})", props.Title, props.Width, props.Height);//log日志系统里面的函数
	if (!s_GLFWInitialized)
	{
		// TODO: glfwTerminate on system shutdown
		int success = glfwInit();
		HZ_CORE_ASSERT(success, "Could not intialize GLFW!");
		glfwSetErrorCallback(GLFWErrorCallback);
		s_GLFWInitialized = true;
	}

	m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);

	glfwMakeContextCurrent(m_Window);
	int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
	HZ_CORE_ASSERT(status, "Failed to initialize Glad!");
	glfwSetWindowUserPointer(m_Window, &m_Data);
	SetVSync(true);

	// Set GLFW callback
	glfwSetWindowSizeCallback(m_Window, [](GLFWwindow* window, int width, int height)
		{
			WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
			data.Width = width;
			data.Height = height;
			WindowResizeEvent event(width, height);
			data.EventCallback(event);
		});

	....其他glfw的回调函数
}
//Init函数基本上只是设置窗口,初始化OpenGL的函数
//(设置上下文,取得OpenGL函数,设置垂直同步)打印消息和安全检查,
//以及设置glfw的回调函数。为i/o设备提供交互

 1.回调函数的简单解释。

glfwSetWindowSizeCallback(m_Window, [](GLFWwindow* window, int width, int height)
		{
			WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
			data.Width = width;
			data.Height = height;
			WindowResizeEvent event(width, height);
			data.EventCallback(event);
		});
glfwSetWindowCloseCallback(m_Window, [](GLFWwindow* window)
		{
			WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
			WindowCloseEvent event;
			data.EventCallback(event);
		});

//以这个两个回调函数为例,第一行代码指定跟踪的事件是在哪个窗口,如果该窗口发生了事件,那么操作系统会把对应事件放入对应的回调函数。
//相当于在指定窗口放在了一堆坑一样,只等事件发生来填入

 2.  m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent)); 

Application类的OnEvent函数

最重要的一个功能就是对每个层栈都调用对应的OnEvent函数

//Application文件下
------------
define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)
//绑定函数用于设置回调函数
void Application::OnEvent(Event& e)
	{
		EventDispatcher dispatcher(e);
		dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));
        //将传入参数用EventDispatcher类来判断事件是否为窗口关闭事件,如果是调用窗口关闭函数
		for (auto it = m_LayerStack.end(); it != m_LayerStack.begin(); )//循环层栈,对每个层调用对应的OnEvent函数
		{
			(*--it)->OnEvent(e); 
			if (e.Handled)
				break;
		}
	}

bool Application::OnWindowClose(WindowCloseEvent& e)//窗口关闭函数,用于终止循环
	{
		m_Running = false;//因为后面的Run的循环需要该变量来条件判断,所以修改m_Running以用来终止循环
		return true;//指示事件已经消耗了
	}

#Sandbox-程序第二步

相关:Sandbox类为Application子类,ExampleLayer为Layer的子类

Sandbox()
    {
        PushLayer(new ExampleLayer());//示例层推入层栈
        PushOverlay(new Hazel::ImGuiLayer());//将ImGui层推入栈底,即最后操作该层
    }

层推入函数

将层推入层栈,以及调用每个层的OnAttach函数

void Application::PushLayer(Layer* layer)
	{
		m_LayerStack.PushLayer(layer);//在层栈组尾部加入一个函数
		layer->OnAttach();
	}

//因为m_LayerStack是Application类中一个变量,并且是LayerStack类,同时上述函数实际就是单纯调用了
//LayerStack类中的推入函数,所以后面把layer->OnAttach()改为放入LayerStack类中的推入函数中
void Application::PushOverlay(Layer* layer)
	{
		m_LayerStack.PushOverlay(layer);
		layer->OnAttach();
	}

ExampleLayer类暂时无OnAttact函数函数,但其被推入层栈,会被前面提及过的Application::OnEvent函数调用其OnEvent函数

void OnEvent(Hazel::Event& event) override
	{
		HZ_TRACE("{0}", event);
	}
//此处就是跟踪打印发生什么事件

ImguiLayer类的OnAttact函数

void ImGuiLayer::OnAttach()
	{
		ImGui::CreateContext();
		ImGui::StyleColorsDark();//设定ImGui的样式为暗色系

		ImGuiIO& io = ImGui::GetIO();//获取 ImGui 的 IO 对象
		io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;// 当设置此标志时,ImGui会使用后端的鼠标光标。例如,当鼠标浮动在可点击的项目上时,该光标可能会更改为手形。
		io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;//位运算的优势可以设置多个标志,当需要更改鼠标光标形状时,应该使用应用程序的原生光标。如果不使用此标志,ImGui 将会使用它自带的基础光标

		// TEMPORARY: should eventually use Hazel key codes
		io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;  //把GLFW的GLFW_KEY_TAB键映射到ImGui的Tab操作上,Tab代表Tab键,在GLFW窗口按下Tab键,ImGui会进行特定操作
		io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
		io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
		io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
		io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
		io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
		io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
		io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
		io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
		io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
		io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
		io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
		io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
		io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
		io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
		io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
		io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
		io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
		io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
		io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
		io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;

		ImGui_ImplOpenGL3_Init("#version 410");//初始化OpenGL3的上下文和设定GLSL版本,需要环境OpenGL上下文
		//在创建渲染上下文后并每次的窗口大小改变时,需正确地设置视图窗口,否则ImGui将无法正确渲染。
	}

#run函数 

void Application::Run()
	{
		while (m_Running)
		{
			glClearColor(1, 0, 1, 1);//glClearColor()函数用于设置颜色缓冲区清除后应用的颜色,
			glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存区,清除缓存区后填入之前设置好的颜色
			for (Layer* layer : m_LayerStack)
				layer->OnUpdate();//对层栈内的所有层运用更新函数
			m_Window->OnUpdate();
		}
	}

关于m_Window->OnUpdate(); 

void WindowsWindow::OnUpdate()
	{
		glfwPollEvents();//从事件队列获取事件
		glfwSwapBuffers(m_Window);//交换前台缓冲区和后台缓冲区,把你的渲染结果展示在窗口上.
	}

 调用所有层栈的更新函数

void ImGuiLayer::OnUpdate()
	{
		ImGuiIO& io = ImGui::GetIO();//更新函数中加入IO对象,因为ImGui的io对象也用于控制渲染选项。io对象还可以用于监视ImGui的当前状态
		Application& app = Application::Get();
		io.DisplaySize = ImVec2(app.GetWindow().GetWidth(), app.GetWindow().GetHeight());//ImGui 的显示大小设置为应用程序窗口的大小,ImVec2 是一个存储二维向量/点的结构,

		float time = (float)glfwGetTime();//glfwGetTime()函数来获取当前的时间(单位是秒)。
		io.DeltaTime = m_Time > 0.0f ? (time - m_Time) : (1.0f / 60.0f);//设定了一个"时间步",如果上一帧的时间大于0(即不是我们正在处理的第一帧)否则,它将被默认设置为1/60秒,这对应于60帧每秒(FPS)的渲染
		m_Time = time;

		ImGui_ImplOpenGL3_NewFrame();//OpenGL3实际上是指OpenGL版本3为基础的实现。这个函数主要是为了确保每一帧的时候,所有的图形资源都准备好,并且设定正确的状态。
		ImGui::NewFrame();//它表示每一帧的开始。这通常被放在你的主循环中,为即将创建的任何ImGui窗口、小组件等设置新的帧上下文。

		static bool show = true;
		ImGui::ShowDemoWindow(&show);//创建一个ImGui演示窗口
		
		ImGui::Render();//这行代码在你完成用户界面命令的构建后调用,它将计算并生成绘制命令列表。
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());//我们用OpenGL3的实现来实际执行绘制命令。ImGui::GetDrawData()提供了我们需要的所有绘图数据
	}

#小结

1.Application::OnEvent会调用所有层栈的OnEvent函数

2.开始为Appicalition的层推入函数调用,后面改为放入层栈类推入函数,会调用层对应的OnAttach函数

3.Run函数,会调用层对应的OnUpdate函数

4.预设....,会调用层的OnDetach函数

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值