尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

UE5游戏开发:从零构建带记忆功能的画面设置菜单

UE5游戏开发:从零构建带记忆功能的画面设置菜单 1. 项目概述为什么需要一个“带记忆”的画面设置菜单做游戏开发尤其是用UE5画面设置菜单几乎是每个项目都绕不开的环节。新手可能会觉得不就是拖几个滑块、放几个下拉框然后绑定几个变量吗但真正上手后你会发现坑一个接一个玩家调好的分辨率重启游戏后怎么又变回默认了垂直同步开关、抗锯齿选项这些设置怎么保存下来更头疼的是不同硬件配置的玩家能开的特效等级天差地别菜单怎么动态地、友好地适配这些差异这就是“带记忆功能的游戏画面设置菜单”要解决的核心问题。它不仅仅是一个UI界面更是一个连接玩家偏好、硬件能力与游戏渲染管道的桥梁。一个健壮的设置菜单能显著提升玩家的游戏体验和满意度。这次我们不依赖任何插件或复杂的C模块就只用UE5内置的控件蓝图Widget Blueprint和UMG系统从零开始搭建一个功能完整、数据持久化的设置菜单。我会带你走过从界面布局、逻辑绑定到数据保存、读取再到一些高级的适配性处理的完整流程过程中遇到的坑和技巧都会毫无保留地分享给你。2. 核心思路与架构设计在动手拖控件之前我们必须先想清楚整个系统的数据流和架构。一个带记忆功能的设置菜单其核心在于“状态管理”和“数据持久化”。2.1 数据流设计单向绑定与中央状态管理一个常见的错误是让UI控件直接去修改游戏的各种CVar控制台变量或项目设置。这会导致状态分散难以管理和保存。我们的设计思路是引入一个“中央状态管理者”。核心思路创建一个专门用于管理所有画面设置数据的游戏实例GameInstance蓝图或游戏单例GameSingleton。所有UI控件只与这个中央数据源交互。流程如下初始化游戏启动时中央数据源从磁盘如保存的游戏数据加载上次的设置。UI绑定设置菜单UI打开时其内部控件如滑块、复选框的“值”或“状态”被绑定到中央数据源对应的变量上。用户交互玩家操作UI控件改变的是中央数据源里变量的值。应用设置当玩家点击“应用”按钮时中央数据源将当前变量值同步到游戏的实际渲染设置如控制台命令、后期处理体积、可缩放性设置等。保存数据当玩家点击“确认”或退出菜单时中央数据源将当前变量值序列化并保存到磁盘。这样做的好处是逻辑清晰数据只有一个“真相来源”保存和加载变得非常简单。我们这次选择使用GameInstance蓝图作为中央数据源因为它生命周期贯穿整个游戏非常适合管理这类全局数据。2.2 界面功能模块拆解一个完整的画面设置菜单通常包含以下几个模块我们将为每个模块设计对应的UI控件和后台变量显示设置Display分辨率Resolution下拉框列出当前显示器支持的所有分辨率。窗口模式Window Mode下拉框选项为“全屏”、“窗口化全屏”、“窗口化”。垂直同步VSync复选框。帧率限制Frame Rate Limit滑块或数字输入框。画面质量Graphics Quality质量预设Quality Preset下拉框选项如“低”、“中”、“高”、“极高”、“自定义”。这是核心选择预设应自动填充下方各项细节设置。细节设置Custom Settings当预设为“自定义”时以下滑块/下拉框可调抗锯齿Anti-Aliasing后期处理Post Processing阴影Shadows全局光照Global Illumination反射Reflections纹理Textures视觉效果Effects植被Foliage着色器Shading硬件适配功能一个“自动检测”按钮点击后根据硬件性能推荐一个质量预设。高级/其他设置Advanced/Others视野FOV滑块。运动模糊Motion Blur复选框或滑块。胶片颗粒Film Grain复选框或滑块。操作按钮Action Buttons应用Apply将当前设置应用到游戏但不保存到磁盘或可设置为临时保存。确认OK/Confirm应用当前设置并保存到磁盘然后关闭菜单。取消Cancel放弃未应用的更改恢复为打开菜单时的设置并关闭菜单。恢复默认Restore Defaults将所有设置重置为引擎默认值。2.3 技术选型为什么用控件蓝图和GameInstance控件蓝图Widget BlueprintUE5的UMG系统是构建UI的官方标准可视化程度高与蓝图逻辑集成无缝是快速开发复杂UI的不二之选。对于设置菜单这种以交互和数据绑定为主的UI控件蓝图完全够用且更直观。GameInstance蓝图作为非关卡绑定的全局管理器它独立于任何关卡加载和卸载是存储全局设置数据的理想位置。相比放在玩家控制器或游戏模式里它的生命周期更稳定。数据持久化我们将使用UE5的SaveGame系统。它可以轻松地将蓝图变量包括结构体、数组序列化到本地文件非常适合保存玩家设置这类小型、结构化数据。3. 核心实现步骤详解接下来我们进入实操环节。我会假设你已经在UE5中创建了一个空白项目。3.1 第一步创建中央数据管理器GameInstance在内容浏览器中右键 - 蓝图类 - 搜索并选择GameInstance命名为GI_GameSettings。双击打开GI_GameSettings。在“我的蓝图”面板创建以下变量来存储我们的设置SavedSettings(类型SaveGame对象引用)用于指向我们即将创建的保存游戏对象。CurrentGraphicsSettings(类型结构体我们需要新建一个)这是一个自定义结构体包含所有画面设置变量。右键点击变量类型区域 - 创建结构体命名为S_GraphicsSettings。编辑S_GraphicsSettings结构体添加成员变量对应我们之前拆解的所有设置项。例如ScreenResolution(Vector2D): 存储分辨率的宽和高。WindowMode(整数 或 枚举): 0全屏1窗口化全屏2窗口化。bUseVSync(布尔值)FrameRateLimit(浮点数)QualityPreset(整数 或 枚举): 0低1中2高3极高4自定义。CustomAntiAliasing(整数) ... 等等所有自定义质量选项。FieldOfView(浮点数)bEnableMotionBlur(布尔值)... 其他你需要的设置。回到GI_GameSettings将CurrentGraphicsSettings的类型设置为S_GraphicsSettings。注意使用结构体而不是一堆分散的变量是为了数据管理的整洁性。保存和加载时只需处理这一个结构体变量即可非常方便。3.2 第二步创建保存游戏对象SaveGame在内容浏览器中右键 - 蓝图类 - 搜索并选择SaveGame命名为SG_GraphicsSettings。打开SG_GraphicsSettings添加一个变量GraphicsSettings类型为S_GraphicsSettings我们刚才创建的结构体。这样我们的保存数据就封装好了。GameInstance会加载一个SG_GraphicsSettings对象并将其中的GraphicsSettings结构体读取到自己的CurrentGraphicsSettings中供UI使用。3.3 第三步构建设置菜单控件蓝图在内容浏览器中右键 - 用户界面 - 控件蓝图命名为WBP_SettingsMenu。打开WBP_SettingsMenu进入设计器Designer选项卡。我们先进行布局。使用画布面板Canvas Panel作为根容器方便绝对定位。添加一个垂直框Vertical Box作为主要区域。在垂直框内添加多个水平框Horizontal Box或缩放框Scale Box来组织每一行设置左侧是文本标签Text Block右侧是对应的控件。分辨率下拉框拖入一个ComboBox (String)。我们需要在游戏运行时用蓝图动态填充它的选项列表Add Option列表来源是RHI渲染硬件接口获取的可用分辨率列表。窗口模式下拉框拖入一个ComboBox (String)可以静态添加“全屏”、“窗口化全屏”、“窗口化”三个选项。垂直同步拖入一个Check Box。质量预设下拉框拖入一个ComboBox (String)静态添加“低”、“中”、“高”、“极高”、“自定义”。各项细节质量滑块拖入多个Slider。关键点将每个滑块的最小值、最大值设置为0-3对应UE的可缩放性等级Low, Medium, High, Epic步长设置为1。这样滑块的值整数可以直接对应到引擎的质量等级。视野FOV滑块最小值设为70最大值设为120步长设为1。操作按钮拖入多个Button。布局时善用锚点和边距让UI在不同分辨率下能自适应。可以暂时将编辑器预览窗口调整到目标分辨率如1920x1080来检查布局。3.4 第四步编写菜单控件蓝图逻辑Graph切换到图表Graph选项卡这是编写功能逻辑的地方。3.4.1 事件构造Event Construct这是控件创建时调用的函数是初始化UI状态的绝佳位置。获取GameInstance使用Get Game Instance节点然后Cast To GI_GameSettings进行类型转换成功后将返回的实例存储到一个局部变量中比如叫MyGI。初始化控件状态从MyGI中读取CurrentGraphicsSettings结构体的各个值并设置到对应的UI控件上。例如设置分辨率下拉框这是一个难点。你需要 a. 清除ComboBox现有选项Clear Options。 b. 使用Get Supported Screen Resolutions节点获取所有分辨率返回一个分辨率信息结构体数组。 c. 遍历这个数组将每个分辨率如1920x1080格式化为字符串添加到ComboBox的选项中Add Option。 d. 将MyGI.CurrentGraphicsSettings.ScreenResolution也格式化为字符串使用Set Selected Option来让下拉框选中当前保存的分辨率。设置滑块和复选框相对简单直接使用Set Value或Set Is Checked节点将结构体中的值赋给控件。绑定质量预设根据MyGI.CurrentGraphicsSettings.QualityPreset的值设置预设下拉框的选中项。同时要根据这个预设值来显示或禁用下面的自定义质量滑块组。如果预设不是“自定义”则应该禁用所有自定义滑块并可能将它们的值更新为预设对应的值这一步也可以在玩家选择预设时实时处理。3.4.2 控件事件绑定我们需要为每个可交互控件绑定事件当玩家操作时更新MyGI中的CurrentGraphicsSettings。下拉框OnSelectionChanged事件触发时将选中的字符串或索引转换并赋值给MyGI.CurrentGraphicsSettings中对应的变量如WindowMode。滑块OnValueChanged事件触发时将Value直接赋值给MyGI.CurrentGraphicsSettings中对应的浮点或整数变量。复选框OnCheckStateChanged事件触发时将Is Checked赋值给MyGI.CurrentGraphicsSettings中对应的布尔变量。质量预设下拉框这个控件的事件处理是关键。除了更新QualityPreset变量还需要 a. 判断新选中的预设值0-3。 b. 根据这个预设值调用UE5内置的Execute Console Command节点执行sg.ResolutionQuality、sg.ViewDistanceQuality等命令是不对的因为我们希望先更新中央数据再统一应用。更好的做法是根据预设枚举直接设置MyGI.CurrentGraphicsSettings中所有自定义质量滑块对应的变量为预设值如“高”预设对应所有值为2。 c. 同时更新UI上所有自定义滑块的值使其与中央数据同步。如果预设不是“自定义”则**禁用Set Is Enabled**所有自定义滑块防止玩家误操作如果是“自定义”则启用它们。3.4.3 按钮功能实现应用Apply按钮调用一个自定义函数ApplyCurrentSettingsToEngine()。在这个函数里根据MyGI.CurrentGraphicsSettings中的每一个值去执行相应的控制台命令或调用蓝图函数。分辨率 窗口模式使用Execute Console Command执行r.SetRes [Width]x[Height] [WindowMode]。例如r.SetRes 1920x1080 f表示全屏。注意窗口模式参数w是窗口化f是全屏windowedfullscreen是窗口化全屏。更稳健的做法是使用蓝图节点Set Window Mode。垂直同步执行r.VSync [0/1]。帧率限制执行t.MaxFPS [Value]。各项画面质量执行sg.[QualitySettingName] [Value]。例如sg.ShadowQuality 3。这里有个重要技巧UE5提供了Set Scalability Settings节点你可以将我们结构体中的一堆质量等级0-3打包成一个Scalability Struct直接传入比执行一堆命令行更优雅、高效。视野FOV通常需要设置玩家控制器或相机管理器的FOV属性这取决于你的游戏架构。应用后可以给玩家一个简单的视觉反馈比如让“应用”按钮变灰几秒钟。确认OK按钮先调用ApplyCurrentSettingsToEngine()。调用MyGI中的一个自定义函数SaveSettingsToDisk()这个函数我们将在GameInstance中实现。调用Remove From Parent节点关闭当前菜单控件。取消Cancel按钮关键步骤从磁盘重新加载设置到MyGI.CurrentGraphicsSettings调用MyGI的LoadSettingsFromDisk函数并确保不应用只更新数据。根据重新加载的数据刷新当前UI上所有控件的状态可以简单粗暴地调用Event Construct中的初始化逻辑或者写一个专门的RefreshUIFromData函数。调用Remove From Parent关闭菜单。恢复默认Restore Defaults按钮将MyGI.CurrentGraphicsSettings中的所有变量重置为引擎的默认值。你可以硬编码一套默认值或者调用Get Default Scalability Settings来获取画面质量的默认值。用重置后的数据刷新UI控件。可选询问玩家是否立即应用这些默认设置。3.5 第五步完善GameInstance的数据持久化逻辑回到GI_GameSettings蓝图。初始化加载Event InitGameInstance初始化时自动加载保存的设置。使用Does Save Game Exist节点检查存档文件指定一个唯一的槽位名如GraphicsSettings是否存在。如果存在使用Load Game From Slot节点加载并Cast To SG_GraphicsSettings。加载成功后将返回的SG_GraphicsSettings对象的GraphicsSettings赋值给本地的CurrentGraphicsSettings变量。如果不存在第一次运行则创建一个新的SG_GraphicsSettings对象并用一套默认的S_GraphicsSettings结构体数据初始化它然后调用保存函数。创建保存函数SaveSettingsToDisk输入无或一个S_GraphicsSettings结构体参数从UI传来。逻辑将CurrentGraphicsSettings赋值给SavedSettings对象SG_GraphicsSettings类型的GraphicsSettings变量。使用Save Game To Slot节点将SavedSettings对象保存到指定的槽位如GraphicsSettings。可选添加一个OnSaveCompleted委托打印日志用于调试。创建加载函数LoadSettingsFromDisk逻辑与Event Init中的加载部分类似但加载后只更新CurrentGraphicsSettings不自动应用。供“取消”按钮调用。3.6 第六步项目配置与菜单调用配置GameInstance在项目设置Project Settings- 地图和模式Maps Modes- GameInstance类中选择我们创建的GI_GameSettings。创建菜单调用入口在你的主界面或暂停菜单控件蓝图中添加一个按钮如“设置”。为该按钮的OnClicked事件编写逻辑Create Widget节点类选择WBP_SettingsMenu。Add to Viewport节点通常添加到最顶层如ZOrder设为最大值。可选Set Input Mode UI Only节点确保玩家操作UI时不会误操作游戏角色。4. 高级功能与优化技巧一个基础的带记忆菜单已经完成但要做得专业还需要考虑以下问题。4.1 动态分辨率列表与当前分辨率高亮获取分辨率列表时Get Supported Screen Resolutions返回的数组可能包含重复项或非标准比例。我们需要处理去重在遍历添加选项前可以先构建一个Set集合来过滤重复的字符串格式分辨率。排序可以按分辨率面积宽x高从大到小排序让玩家优先看到高分辨率选项。高亮当前分辨率在Event Construct中设置选中项后很多引擎的ComboBox控件可能没有视觉高亮。一个技巧是在填充完所有选项后手动触发一次Set Selected Option并确保选项字符串的格式如“1920 x 1080”与列表中的某一项完全匹配。4.2 “自动检测”硬件性能功能这是一个提升用户体验的亮点功能。在UI上添加一个“自动检测”按钮。按钮逻辑调用Run Hardware Benchmark节点。这个节点会运行一系列测试并自动将可缩放性设置调整到适合你硬件的水平。问题是它直接修改了引擎的CVar没有更新我们的CurrentGraphicsSettings数据。所以在基准测试运行后我们需要反向同步。使用Get Scalability Settings节点获取基准测试后引擎当前的质量等级一个Scalability Struct。将这个结构体中的各个等级0-3赋值给MyGI.CurrentGraphicsSettings中对应的自定义质量变量。将QualityPreset设置为“自定义”因为自动检测的结果是混合等级。刷新UI让滑块显示新的值。可选弹出一个提示框告诉玩家“已根据您的硬件推荐了画面设置点击‘应用’生效”。4.3 防止无效设置组合与实时预览组合限制某些设置是互斥或依赖的。例如在“窗口化”模式下某些分辨率可能不可用开启“垂直同步”时“帧率限制”可能无效。你需要在相应控件的事件处理函数中添加判断逻辑并禁用或重置关联的控件。实时预览谨慎使用玩家每调整一个滑块就立即调用一次ApplyCurrentSettingsToEngine()可以实现实时预览但这会带来严重的性能问题和操作卡顿尤其是调整分辨率。不推荐这样做。更好的做法是仅对“视野FOV”、“运动模糊强度”等少数几个对性能影响极小、且视觉反馈立竿见影的设置在OnValueChanged事件中立即应用。对于分辨率、抗锯齿等级等“重型”设置严格放在“应用”按钮中统一处理。可以提供“重置”按钮让玩家快速回到应用前的状态。4.4 多语言与无障碍支持文本本地化不要将UI上的标签文字如“分辨率”直接写在Text Block里。应该使用FText类型并为其设置一个“键Key”然后在项目的本地化表格中为每种语言提供翻译。这样你的设置菜单就能轻松支持多语言。无障碍功能为所有交互控件设置清晰的Tooltip Text工具提示文本说明其功能。确保UI在仅使用键盘Tab键切换焦点时也能完全操作。可以考虑支持高对比度主题。5. 常见问题与调试技巧在实际操作中你几乎一定会遇到下面这些问题。5.1 分辨率切换后UI布局错乱或崩溃问题切换到非16:9分辨率如21:9超宽屏或较低分辨率时UI元素可能跑出屏幕外。解决使用锚点Anchors而非绝对位置在设计UI时为关键容器和元素设置相对于父容器的锚点。例如将主垂直框的锚点设置为“居中”并给予左右和上下的边距Margin这样它在任何分辨率下都会保持居中并留白。使用尺寸框Size Box和缩放框Scale Box对于需要保持宽高比或相对尺寸的元素用这些容器来约束。在Event Construct或OnInitialized中动态调整布局获取当前视图port的大小然后根据比例调整某些元素的尺寸或位置。测试在编辑器中使用不同的预览窗口尺寸进行测试。5.2 保存的数据不生效或丢失问题点击“确认”后重启游戏设置又变回了默认值。排查检查槽位名确保Save Game To Slot和Load Game From Slot使用的槽位名Slot Name完全一致且不与其他系统冲突。检查保存时机确保“确认”按钮的逻辑是先“应用”再“保存”。并且保存函数确实被调用到了添加Print String节点调试。检查变量赋值在保存函数中确认是将CurrentGraphicsSettings赋值给了SavedSettings对象的变量而不是反过来。保存的是SavedSettings这个SaveGame对象。检查文件路径保存的游戏文件通常位于项目文件夹/Saved/SaveGames/下。可以去这个目录看看文件是否生成以及修改时间。5.3 “应用”按钮点击后游戏无反应或报错问题控制台命令执行失败或者画面没有变化。排查命令拼写错误r.SetRes、r.VSync、sg.ShadowQuality等命令必须完全正确。建议直接从官方文档复制。参数格式错误r.SetRes的参数是WidthxHeight WindowMode中间是字母x且分辨率必须受支持。窗口模式参数是f、w等。使用蓝图节点替代对于分辨率切换优先使用Set Window Mode和Set Screen Resolution这两个更稳定的蓝图节点而不是执行控制台命令。延迟应用某些渲染设置如部分sg.命令可能在下一帧才生效。可以尝试在应用逻辑后加一个小的延迟0.1秒再给玩家反馈。命令执行权限确保在正确的上下文中执行命令。在控件蓝图的事件图表中执行通常是没问题的。5.4 控件事件绑定混乱或值不同步问题滑动滑块中央数据没更新或者改了数据UI没刷新。解决确保绑定成功在控件的事件图表中检查每个交互控件的OnValueChanged、OnSelectionChanged等事件是否都正确绑定了函数并且函数内部确实有对MyGI.CurrentGraphicsSettings的赋值操作。使用数据绑定Data Binding对于显示类控件如一个用于显示当前FOV数值的Text Block可以将其Text属性绑定到一个函数上。这个函数返回MyGI.CurrentGraphicsSettings.FieldOfView的字符串格式。这样当中央数据变化时显示会自动更新。但注意对于可交互控件直接的事件处理通常更可控。手动刷新在修改了CurrentGraphicsSettings后比如通过“恢复默认”按钮显式地调用一个RefreshAllWidgets函数这个函数遍历所有UI控件并重新用中央数据设置它们的值。5.5 在打包后设置不生效问题在编辑器中运行一切正常但打包成可执行游戏后画面设置无法保存或应用。排查文件读写权限确保打包后游戏对Saved/SaveGames/目录有写入权限。通常这是默认的。绝对路径与相对路径Save Game To Slot使用的是相对项目保存目录的路径打包后会自动适应一般没问题。命令在打包版本中的可用性绝大多数r.和sg.命令在打包版本中仍然是可用的。但极少数开发命令可能被禁用。如果怀疑是命令问题尝试用蓝图节点如Set Scalability Settings替代。首次运行逻辑确保你的Event Init中对于找不到存档文件的情况有创建默认存档并保存的逻辑。否则打包后玩家第一次运行由于没有存档所有设置都是变量的初始值可能是0导致画面质量最低。整个流程下来虽然步骤不少但每一步的逻辑都是清晰、环环相扣的。最重要的是理解“中央数据管理”这个核心思想。一旦这个框架搭好后续要增加新的设置项比如光追开关、DLSS选项就只需要在结构体里加变量在UI上拖控件并绑定然后在应用函数里添加对应的设置逻辑即可扩展性非常好。希望这篇超详细的指南能帮你避开我当年踩过的那些坑顺利搭建出属于你自己的、专业级的游戏设置菜单。
返回列表