Procházet zdrojové kódy

feat: 添加编辑器扩展中与模块相关的部分

liucong5 před 2 roky
rodič
revize
ae2734f514

binární
UE5/编辑器扩展/Image/001.png


binární
UE5/编辑器扩展/Image/002.png


binární
UE5/编辑器扩展/Image/003.png


+ 134 - 0
UE5/编辑器扩展/README.md

@@ -0,0 +1,134 @@
+# 编辑器扩展
+
+[基础视频](https://www.bilibili.com/video/BV1PW4y1v7Ca)
+
+## 基础概念
+
+### 模块
+
+- 模块的结构
+  - 完成一组功能的代码集合, 最终会编译为同一个dll
+  - 模块(Modules)是虚幻引擎的软件架构的基本构建块
+  - 插件是由模块组成的
+  - 项目也是由模块组成的
+  - 项目和插件都有且只有一个主模块, 但是可以有其他模块
+
+![](./Image/001.png)
+
+```csharp
+using UnrealBuildTool;
+
+public class GASSample : ModuleRules
+{
+	public GASSample(ReadOnlyTargetRules Target) : base(Target)
+	{
+		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+
+		PublicDependencyModuleNames.AddRange(new string[] { 
+            "Core", 
+            "CoreUObject", 
+            "Slate",
+            "SlateCore",
+		});
+
+		PrivateDependencyModuleNames.AddRange(new string[] { "Engine" });
+	}
+}
+```
+
+上述就是 `GASSample.build.cs` 文件的部分内容
+
+`PublicDependencyModuleNames`表示公共依赖模块。公共依赖模块允许模块之间共享功能和接口,促进代码重用和模块化设计。通过指定公共依赖模块,当前模块可以使用其他模块提供的公共功能,而不需要重新实现这些功能
+
+`PrivateDependencyModuleNames`表示私有依赖模块。私有依赖模块允许模块在内部使用其他模块的功能,但不会向外部公开这些功能。私有依赖模块用于实现模块内部的细节和功能,从而提高代码的封装性和安全性。
+
+如果子模块被包含在 `PublicDependencyModuleNames` 中, 那么其他模块可以直接使用当前模块的子模块中的接口; 如果子模块被包含在 `PrivateDependencyModuleNames` 中, 那么其他模块无法直接使用当前模块的子模块的接口
+
+一些建议
+
+1. **头文件**中应避免直接引用其他模块的类
+2. 尽可能使用 `Forward Declaration` 前置声明
+3. 优先选择 `PrivateDependencyModuleNames`, 可减少项目编译时间
+
+**项目中如何知道应该引用哪个模块**
+
+以 `USkeletalMeshComponent` 为例, 定位到对应头文件的文件位置
+
+![](./Image/002.png)
+
+然后往上级目录找, 直到直到 `Build.cs` 文件为止
+
+![](./Image/003.png)
+
+一般来说文件名就是模块名, 比如这里文件名是 `Engine.Build.cs`, 那么模块名就是 `Engine`; 打开 `Engine.Build.cs` 文件, 该文件中定义的类名就是模块名
+
+```csharp
+using UnrealBuildTool;
+using System.IO;
+
+public class Engine : ModuleRules
+{
+	public Engine(ReadOnlyTargetRules Target) : base(Target)
+	{
+        // ... do something
+    }
+}
+```
+
+然后我们要做的就是将 `Engine` 这个模块添加到 `PrivateDependencyModuleNames` 中即可
+
+在创建模块的时候, 会生成与模块同名的 `.h` 和 `.cpp `文件 
+
+```cpp
+class FModuleName : public IModuleInterface
+{
+public:
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+};
+```
+
+```cpp
+#define LOCTEXT_NAMESPACE "FModuleName"
+
+void FModuleName::StartupModule() { /* do something */ }
+
+void FModuleName::ShutdownModule() { /* do something */ }
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FModuleName, ModuleName)
+```
+
+`StartupModule` 和 `ShutdownModule` 就是启动模块和关闭模块会激活的函数了
+
+至于 `IMPLEMENT_MODULE` 宏
+
+```cpp
+#define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \
+    \
+    extern "C" DLLEXPORT IModuleInterface* InitializeModule() \
+    { \
+        return new ModuleImplClass(); \
+    } \
+    extern "C" void IMPLEMENT_MODULE_##ModuleName() { } \
+    PER_MODULE_BOILERPLATE \
+    PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)
+```
+
+展开之后可以发现就是把我们的模块类实例化了一个, 最终这个函数会被 `ModuleManger` 调用, 从而到达模块注册的作用
+
+至于模块内 `Public` 和 `Private` 文件夹的作用, 如果头文件放在 `Private` 文件夹中, 该文件中定义的类、结构、枚举仅对本模块内可见; 如果头文件放在 `Public` 文件夹中, 则其他模块都能使用文件内定义的类、结构、属性
+
+如果模块不可能被其他模块引用, 那么 `Public` 、`Private` 无关紧要
+
+可以在 `Private` 和 `Public` 文件夹中创建子文件夹, 进一步整理代码。但是 `Pubilc` 和 `Private` 中的子文件夹要一一对应
+
+### 扩展的技能点
+
+- 扩展功能菜单、工具栏
+- 自定义编辑器模式
+- 自定义细节面板
+- 自定义资产类型
+- 自定义图形节点编辑器
+