UE5.6 Slate独立程序------初始化空白Slate环境
1.从BlackProgram 拷贝一份出来用当作初始化模板
这个过程不做赘述
2.配置模块(主要)
直接贴出代码对关键部分进行描述
.Target
cpp
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
[SupportedPlatforms(UnrealPlatformClass.All)]
public class TestBlankProgramTarget : TargetRules
{
public TestBlankProgramTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Program;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
LinkType = TargetLinkType.Monolithic;
LaunchModuleName = "TestBlankProgram";
// Lean and mean
bBuildDeveloperTools = false;
bUseMallocProfiler = false;
SolutionDirectory = "Programs/ZenithZhouPrograms";
ExeBinariesSubFolder = "ZenithZhouPrograms/TestBlankProgram";
// Editor-only is enabled for desktop platforms to run unit tests that depend on editor-only data
// It's disabled in test and shipping configs to make profiling similar to the game
bool bDebugOrDevelopment = Target.Configuration == UnrealTargetConfiguration.Debug || Target.Configuration == UnrealTargetConfiguration.Development;
bBuildWithEditorOnlyData = Target.Platform.IsInGroup(UnrealPlatformGroup.Desktop) && bDebugOrDevelopment;
// Currently this app is not linking against the engine, so we'll compile out references from Core to the rest of the engine
bCompileAgainstEngine = false;
bCompileAgainstCoreUObject = true;
bCompileAgainstApplicationCore = true;
bCompileICU = false;
// to build with automation tests:
// bForceCompileDevelopmentAutomationTests = true;
// to enable tracing:
// bEnableTrace = true;
// This app is a Windows app (sets entry point to WinMain(), instead of main())
bIsBuildingConsoleApplication = false;
}
}
.Build
cpp
using UnrealBuildTool;
public class TestBlankProgram : ModuleRules
{
public TestBlankProgram(ReadOnlyTargetRules Target) : base(Target)
{
PublicIncludePathModuleNames.Add("Launch");
PrivateIncludePathModuleNames.Add("Launch");
PrivateDependencyModuleNames.Add("Core");
PrivateDependencyModuleNames.Add("Projects");
PrivateDependencyModuleNames.AddRange(new string[] { "AppFramework","ApplicationCore","Slate", "SlateCore", "StandaloneRenderer" });
PrivateIncludePathModuleNames.AddRange(
new string[] {
"SlateReflector",
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[] {
"SlateReflector",
}
);
// to link with CoreUObject module:
// PrivateDependencyModuleNames.Add("CoreUObject");
// to enable LLM tracing:
// GlobalDefinitions.Add("LLM_ENABLED_IN_CONFIG=1");
// GlobalDefinitions.Add("UE_MEMORY_TAGS_TRACE_ENABLED=1");
}
}
主入口.CPP
cpp
#include "TestBlankProgram.h"
// 独立程序必须包含的核心头文件,内部直接 #include LaunchEngineLoop.cpp
// 这使得本 .cpp 成为引擎主循环的编译单元,提供 GEngineLoop 等全局对象
#include "RequiredProgramMainCPPInclude.h"
// 独立渲染器(非编辑器渲染器),为 Slate 提供轻量级 RHI 渲染后端
// 提供 GetStandardStandaloneRenderer() 工厂函数
#include "StandaloneRenderer.h"
// Slate 应用框架核心,提供窗口管理、消息泵、Tick 驱动等功能
#include "Framework/Application/SlateApplication.h"
// SlateReflector 模块的公共接口,用于注册 Widget Reflector(UI 调试工具)
// 该模块以动态加载方式引入,不静态链接
#include "ISlateReflectorModule.h"
// 定义仅限本编译单元使用的日志分类
// 默认级别 Log,最高可输出 All 级别,运行时可通过控制台命令动态调整
DEFINE_LOG_CATEGORY_STATIC(LogTestBlankProgram, Log, All);
// 注册应用程序唯一标识符
// 影响:日志文件路径、用户配置目录、崩溃报告标识等
IMPLEMENT_APPLICATION(TestBlankProgram, "TestBlankProgram");
// 本地化文本命名空间,LOCTEXT() 宏生成的文本 Key 都归属于此命名空间
// 对应 .po/.po 本地化文件中的 namespace 字段
#define LOCTEXT_NAMESPACE "TestBlankProgram"
// Windows 平台程序入口点
// 独立程序不使用跨平台的 RunXxx() 包装,直接在 WinMain 中完成所有逻辑
int WINAPI WinMain( _In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow )
{
// =====================================================================
// 阶段一:引擎基础初始化
// =====================================================================
// 将当前线程标记为「游戏线程」
// UE 的 TaskGraph、断言、线程安全检查等机制依赖此标记
// 若缺少此标记,部分模块加载时会触发 check() 断言失败
FTaskTagScope TaskTagScope(ETaskTag::EGameThread);
// 引擎预初始化:解析命令行、初始化内存分配器、日志系统、
// 配置系统(GConfig)、模块管理器、任务图(TaskGraph)等基础子系统
// 注意:这里不会启动完整的游戏引擎,只初始化独立程序所需的最小集合
GEngineLoop.PreInit(GetCommandLineW());
// 处理所有已加载模块中新注册的 UObject 类
// 确保 UClass、UStruct、UEnum 等反射信息完成注册和默认属性初始化
ProcessNewlyLoadedUObjects();
// 通知模块管理器进入「处理新加载对象」模式
// 此后每次动态加载新模块,都会自动调用 ProcessNewlyLoadedUObjects()
FModuleManager::Get().StartProcessingNewlyLoadedObjects();
// =====================================================================
// 阶段二:Slate 应用初始化
// =====================================================================
// 以独立程序模式初始化 Slate 应用
// GetStandardStandaloneRenderer() 返回基于 RHI 的轻量级渲染器
// 该渲染器不依赖编辑器,适合独立工具程序使用
FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer());
// 启用 DPI 感知,Slate 会根据系统 DPI 缩放比例自动调整 UI 尺寸
// 在高分辨率(HiDPI/Retina)屏幕上保证 UI 清晰不模糊
FSlateApplication::InitHighDPI(true);
// 设置操作系统窗口标题栏显示的应用程序名称
// 同时也是 Tab 管理器在无 Tab 时显示的默认标题
FGlobalTabmanager::Get()->SetApplicationTitle(LOCTEXT("AppTitle","My Test Slate Program"));
// =====================================================================
// 阶段三:模块加载
// =====================================================================
// 动态加载 SlateReflector 模块(运行时按需加载,不静态链接)
// RegisterTabSpawner:将 Widget Reflector 的 Tab 入口注册到 DeveloperMenu 菜单节点
// Widget Reflector 是 Slate 的 UI 调试工具,可实时查看 Widget 树、布局、绘制信息
FModuleManager::LoadModuleChecked<ISlateReflectorModule>("SlateReflector").RegisterTabSpawner(WorkspaceMenu::DeveloperMenu);
//=============================这里窗口逻辑=============================
TSharedRef<SWindow> NewWindow = SNew(SWindow)
.Title(LOCTEXT("WindowTitle", "我的空窗口"))
.ClientSize(FVector2D(800, 600)) // 窗口初始尺寸
.ScreenPosition(FVector2D(200, 100)) // 窗口左上角屏幕坐标
.IsInitiallyMaximized(false) // 是否最大化启动
[
// 窗口内容区,放你想要的 Widget
SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("EmptyContent", "这是一个空窗口"))
]
];
// 将窗口添加到 Slate 应用并显示
FSlateApplication::Get().AddWindow(NewWindow);
// =====================================================================
// 阶段四:主循环
// =====================================================================
double LastTime = FPlatformTime::Seconds();
// 目标帧间隔:1/120 秒 ≈ 8.3ms,对应约 120fps 的帧率上限
const double DefaultWaitTime = 1.0f / 120;
// IsEngineExitRequested():检查全局退出标志(由关闭窗口、RequestEngineExit() 等触发)
while (!IsEngineExitRequested())
{
// 若有挂起的退出请求,执行必要的清理并设置退出标志
// 与 IsEngineExitRequested() 配合,确保退出流程只执行一次
BeginExitIfRequested();
double CurrentTime = FPlatformTime::Seconds();
// 本帧实际耗时(秒),可用于帧率统计或调试
double Dt = CurrentTime - LastTime;
// 处理游戏线程上所有待执行的 TaskGraph 任务
// 包括:异步委托回调、跨线程任务完成通知、延迟执行的 Lambda 等
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
// 推进性能统计系统的帧计数器
// 参数 false:不强制刷新统计数据(由 Stats 系统自行决定刷新时机)
UE::Stats::FStats::AdvanceFrame(false);
// 驱动全局 Ticker,触发所有通过 FTSTicker::GetCoreTicker().AddTicker() 注册的定时回调
// 例如:动画更新、网络心跳、定时任务等
FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime());
// 处理操作系统消息队列(Windows 消息循环)
// 包括:鼠标移动/点击、键盘输入、窗口大小变化、窗口关闭请求等
FSlateApplication::Get().PumpMessages();
// 驱动 Slate 的一帧更新:布局计算、动画插值、渲染提交
FSlateApplication::Get().Tick();
// 固定休眠 10ms,防止 CPU 空转导致占用率 100%
// 注意:此处与下方的自适应休眠共同控制帧率
FPlatformProcess::Sleep(0.01f);
// 推进全局帧计数器,部分系统(如渲染、动画)依赖此计数器判断帧边界
GFrameCounter++;
// 增量垃圾回收:利用帧间剩余时间分批回收 UObject 内存
// 参数1 true:允许在此帧执行 GC
// 参数2:本帧剩余时间预算(最少保证 2ms),避免 GC 占用过多帧时间
IncrementalPurgeGarbage(true, FMath::Max<float>(0.002f, DefaultWaitTime - (FPlatformTime::Seconds() - CurrentTime)));
LastTime = CurrentTime;
// 自适应休眠:用完目标帧时间的剩余部分,实现帧率上限控制
// 若本帧已超时(Dt > DefaultWaitTime),则 Sleep(0) 立即进入下一帧
FPlatformProcess::Sleep(FMath::Max<double>(0.0f, DefaultWaitTime - (FPlatformTime::Seconds() - CurrentTime)));
}
// =====================================================================
// 阶段五:退出清理(顺序不可颠倒)
// =====================================================================
// 1. 广播全局退出事件,通知所有订阅者(各模块、子系统)执行自身清理
// 例如:保存用户配置、关闭网络连接、释放外部资源等
FCoreDelegates::OnExit.Broadcast();
// 2. 关闭 Slate 应用:销毁所有 SWindow、释放渲染资源、卸载字体缓存
// 必须在 UnloadModulesAtShutdown 之前调用,因为 Slate 依赖部分模块
FSlateApplication::Shutdown();
// 3. 按依赖顺序逆序卸载所有动态加载的模块
// 每个模块的 ShutdownModule() 会在此时被调用
FModuleManager::Get().UnloadModulesAtShutdown();
// 4. 引擎预退出:广播 PreExit 事件,执行各引擎子系统的预清理工作
// 例如:停止后台线程、刷新异步 IO、清理 TaskGraph
GEngineLoop.AppPreExit();
// 5. 引擎最终退出:执行内存、日志等最底层资源的释放
GEngineLoop.AppExit();
return 0;
}
#undef LOCTEXT_NAMESPACE
主入口.h
对于现在的空白窗体,没有什么导入的头文件,如果后续需要使用一些UI控件可以如下导入
cpp
#pragma once
#include "CoreMinimal.h"
// 数值输入框(带上下箭头)
#include "Widgets/Input/SSpinBox.h"
// 滑动条
#include "Widgets/Input/SSlider.h"
// 多行可编辑文本框
#include "Widgets/Input/SMultiLineEditableTextBox.h"
// 进度条
#include "Widgets/Notifications/SProgressBar.h"
3.总结
对于一个空白的Slate程序,我们只需要按照如上进行配置即可,基本上这些写法是固定的不必太过纠结