Layers

引言

上一节我们借助 GLFW 为游戏引擎创建窗口事件,今天我们来看一看层堆栈以及我们该如何使用它们。

每当我向人们谈论层次时,我通常最终都将其与类似 Photoshop 这样的东西比较,但显然它不像游戏引擎那样具有交互性,我们可以把其视为 Photoshop 中的一层,拥有一个有序列表(通常情况是栈)。

它们不仅决定图形绘制的顺序,也包含引擎中的事件、更新逻辑等等,所以在我们的引擎具有运行功能(基于while循环),这种循环不断重复到循环被终止。我们可以视为该层堆栈内的一层,就像可以打开图层一样在 Photoshop 中关闭,然后将其隐藏或禁用就可以了。同样的,只要启用了图层类型,我们就可以在这里进行同样的操作:通过游戏循环,各层将按照其实际顺序进行更新。

层实现

我们新建了 Layer.h 头文件:

#pragma once

#include "Core.h"
#include "Events/Event.h"

namespace Infinite {

class INFINITE_API Layer
{
public:
Layer(const std::string& name = "Layer");
virtual ~Layer();

virtual void OnAttach() {}
virtual void OnDetach() {}
virtual void OnUpdate() {}
virtual void OnEvent(Event& event) {}

inline const std::string& GetName() const { return m_DebugName; }

protected:
std::string m_DebugName;
};
}

这是一个非常简单的实现,我们在更新(OnUpdate())和事件(OnEvent())做了分离。

除此之外这里还有一个非常重要的功能是禁用层的功能,此功能将在之后实现,我们暂时不想把事情复杂化,当然在您自己实现时需要这个 bool 值让该层不会被更新。

而在 Layer.cpp 中,我们包含空的构造函数和析构函数:

#include "ifnpch.h"
#include "Layer.h"

namespace Infinite {

Layer::Layer(const std::string& debugName)
: m_DebugName(debugName)
{

}

Layer::~Layer()
{

}
}

堆栈实现

堆栈实现要稍微复杂一点,LayerStack.h 我们使用向量 vector 封装():

#pragma once

#include "Core.h"
#include "Layer.h"

#include <vector>

namespace Infinite {

class INFINITE_API LayerStack
{
public:
LayerStack();
~LayerStack();

void PushLayer(Layer* layer);
void PushOverlay(Layer* layer);
void PopLayer(Layer* layer);
void PopOverlay(Layer* layer);

std::vector<Layer*>::iterator begin() { return m_Layers.begin(); }
std::vector<Layer*>::iterator end() { return m_Layers.end(); }

private:
std::vector<Layer*> m_Layers;
std::vector<Layer*>::iterator m_LayerInsert;
};
}

使用 vector 是因为每帧都可能需要正向或者反向迭代,我们总是需要遍历,我们想要某种连续的数据存储。

接下来我们来看 LayerStack.spp

#include "ifnpch.h"
#include "LayerStack.h"

namespace Infinite {

LayerStack::LayerStack()
{
m_LayerInsert = m_Layers.begin();
}

LayerStack::~LayerStack()
{
for (Layer* layer : m_Layers)
delete layer;
}

void LayerStack::PushLayer(Layer* layer)
{
m_LayerInsert = m_Layers.emplace(m_LayerInsert,layer);
}

void LayerStack::PushOverlay(Layer* overlay)
{
m_Layers.emplace_back(overlay);
}

void LayerStack::PopLayer(Layer* layer)
{
auto it = std::find(m_Layers.begin(),m_Layers.end(),layer);
if (it != m_Layers.end())
{
m_Layers.erase(it);
m_LayerInsert--;
}
}

void LayerStack::PopOverlay(Layer* overlay)
{
auto it = std::find(m_Layers.begin(),m_Layers.end(),overlay);
if (it != m_Layers.end())
m_Layers.erase(it);
}

}

有趣的是我们弹出层时不会丢失它们,只有析构函数中调用 delete 才会销毁。应用程序拥有堆栈,基本的运行方式为将 Layer 分配给堆栈;当应用程序关闭时将被取消分配。

回到 Application.h 为我们的应用程序添加堆栈:

#pragma once

#include "Core.h"
+#include "Window.h"
+#include "LayerStack.h"
+#include "Events/Event.h"
#include "Events/ApplicationEvent.h"


namespace Infinite {
class INFINITE_API Application
{
public:
Application();
virtual ~Application();

void Run();
void OnEvent(Event& e);

+ void PushLayer(Layer* layer);
+ void PushOverlay(Layer* layer);
private:
bool OnWindowClose(WindowCloseEvent& e);

std::unique_ptr<Window> m_Window;
bool m_Running = true;
+ LayerStack m_LayerStack;
};

//To be define in CLIENT
Application* CreateApplication();
}

我们添加了一些需要的头文件和刚刚创建的堆栈。接着我们实现一下声明的函数:

#include "ifnpch.h"
#include "Application.h"

#include "Events/ApplicationEvent.h"
#include "Log.h"

#include <GLFW/glfw3.h>

namespace Infinite {

#define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)

Application::Application()
{
m_Window = std::unique_ptr<Window>(Window::Create());
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
}

Application::~Application() {}

+ void Application::PushLayer(Layer* layer)
+ {
+ m_LayerStack.PushLayer(layer);
+ }

+ void Application::PushOverlay(Layer* layer)
+ {
+ m_LayerStack.PushOverlay(layer);
+ }


void Application::OnEvent(Event& e)
{
EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));

+ for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();)
+ {
+ (*--it)->OnEvent(e);
+ if (e.Handled)
+ break;
+ }
}

void Application::Run()
{
while (m_Running)
{
glClearColor(1, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);

+ for (Layer* layer : m_LayerStack)
+ layer->OnUpdate();

m_Window->OnUpdate();
}
}

bool Application::OnWindowClose(WindowCloseEvent& e)
{
m_Running = false;
return true;
}

}

预生成文件

workspace "Infinite"
architecture "x64"
startproject "Sandbox"

configurations
{
"Debug",
"Release",
"Dist"
}

outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

-- Include directories relative to root folder (solution directory)
IncludeDir = {}
IncludeDir["GLFW"] = "Infinite/vendor/GLFW/include"

include "Infinite/vendor/GLFW"

project "Infinite"
location "Infinite"
kind "SharedLib"
language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

pchheader "ifnpch.h"
pchsource "Infinite/src/ifnpch.cpp"

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"%{prj.name}/src",
"%{prj.name}/vendor/spdlog/include",
"%{IncludeDir.GLFW}"
}

links
{
"GLFW",
"opengl32.lib"
}

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

defines
{
"IFN_PLATFORM_WINDOWS",
"IFN_BUILD_DLL"
}

postbuildcommands
{
("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
}

filter "configurations:Debug"
defines "IFN_DEBUG"
+ buildoptions "/MDd"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
+ buildoptions "/MD"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
+ buildoptions "/MD"
optimize "On"



project "Sandbox"
location "Sandbox"
kind "ConsoleApp"

language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"Infinite/vendor/spdlog/include",
"Infinite/src/Infinite",
"Infinite/src/Events"
}

links
{
"Infinite"
}

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

defines
{
"IFN_PLATFORM_WINDOWS"
}

filter "configurations:Debug"
defines "IFN_DEBUG"
buildoptions "/MDd"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
buildoptions "/MD"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
buildoptions "/MD"
optimize "On"

我们需要确保构建的动态链接库链接到实际的 dll。在 VisualStudio 中的 C++ 代码生成中的运行库默认设置多线程调试:

沙盒应用

最后我们在沙盒 Sandbox.cpp 中创建一个示例层 ExampleLayer 作为测试:

#include <Infinite.h>

+class ExampleLayer : public Infinite::Layer
+{
+public:
+ ExampleLayer()
+ : Layer("Example")
+ {
+
+ }

void OnUpdate() override
{
+ IFN_INFO("ExampleLayer::Update");
}

void OnEvent(Infinite::Event& event) override
{
IFN_TRACE("{0}",event);
}

};

class Sandbox : public Infinite::Application {
public:
Sandbox()
{
+ PushLayer(new ExampleLayer());
}
~Sandbox() {}
};

Infinite::Application* Infinite::CreateApplication() {
return new Sandbox();
}

调试