DreamGUI
快速上手

安装

环境要求、把插件克隆进 Plugins/、装完后值得马上决定的两项设置(键盘布局用的游戏视口客户端、Slate 输入源),以及各项设置存在哪里。

DreamGUI 的装法和别的项目插件一样:克隆进项目的 Plugins/,重新生成工程文件,编译。对一个新项目来说, 这就是全部。后面两节讲的是装完之后值得马上决定的两件事 —— 都不是必需的,但不做的话, 一个会让非美式键盘打错字,一个会让 UI 在引擎自己的 UI-only 输入模式下听不到任何输入。

引擎Unreal Engine 5.8(EngineVersion 5.8.0),源码引擎和 Epic 启动器安装版都行
平台Win64 是唯一构建并运行过的平台(见平台)
依赖插件EnhancedInput,.uplugin 里自己启用
运行时模块DreamGUIRenderer、DreamGUI、DreamGUIInput、DreamGUIControls、DreamGUIExtensions、DreamGUISamples(PostConfigInit),DreamTween(Default)
只在未烹饪环境加载DreamGUIEditor、DreamGUIK2Nodes(UncookedOnly)
测试DreamGUITests(Editor,PostEngineInit)

要求

引擎 5.8,启动器安装版也可以

插件只用引擎的公开头文件编译:渲染器通过公开的 scene-texture API 读取场景深度,静态检查 engine-private-path 会让任何指向 Runtime/Renderer/Private 或 Internal 的 include 路径直接失败。 所以不需要源码引擎,也没有 5.7 的分支。

自带的 msdfgen

字形光栅器把 msdfgen(Viktor Chlumsky,MIT)编译进它自己的一个 翻译单元,用来从字形轮廓生成多通道距离场。以前这要求源码引擎:上游不提供单文件发行版,而是用 all-in-one/generate.py 生成一对 msdfgen.h / msdfgen.cpp,启动器安装版的 Engine/Source/ThirdParty/msdfgen/ 下只有一个 msdfgen.tps。

现在插件自己带着生成好的那一对,放在 ThirdParty/msdfgen-single-file/,已经提交进仓库。所以普通的 git clone、甚至 GitHub 的 zip 下载(不带子模块内容)都能直接编译,构建时不需要 Python。 ThirdParty/msdfgen 子模块只是 Tools/UpdateMsdfgen.ps1 重新生成这对文件时的来源,平时不编译、不包含; Tools/UpdateMsdfgen.ps1 -Check 在提交的那对文件和子模块对不上时以退出码 1 结束。

这份拷贝被包在 DreamMsdfgen 命名空间里(MSDFGEN_PARENT_NAMESPACE)。SlateCore 也编译了一份自己的 msdfgen,单体构建里两份符号会冲突 —— 两份"看起来差不多"的库比一个链接错误更糟,所以这层命名空间不是可选的。

安装

git clone https://github.com/TypeDreamMoon/DreamGUI.git Plugins/DreamGUI

重新生成工程文件,编译。不需要源码引擎、不需要引擎私有头文件,也没有要拷贝的设置。

1.0.0 不带任何 CoreRedirects。如果项目里有按 LGUI、LexUI 保存的资产,或者 1.0.0 之前的构建按后来改掉的名字保存的资产, 要借 2.1.0 的 redirect 段把它们重存一次:步骤见从开发版升级,从 LGUI、LexUI 过来的项目从迁移开始。

确认装上了

编辑器起来以后:

  • 内容浏览器右键菜单里有 DreamUI 分类,其中有 DreamUI Widget Blueprint;
  • Project Settings ▸ Plugins 下有 Dream GUI 和 DreamUI 两页(还有一页 DreamGUI Editor);
  • 关卡编辑器的 Tools 菜单里有 Open DreamUI Workspace (VSCode) 和 Rebuild DUI。

键盘布局:把游戏视口客户端交给 DreamGUI

DreamGUI 的文本框不是 Slate 控件,所以它永远不在键盘焦点路径上;而游戏里平台的字符事件在引擎中唯一的 落脚点是虚函数 UGameViewportClient::InputChar —— 它没有可以订阅的委托。没人接管这个函数时, UUITextInput 退回到自己的 FKey → 字符对照表,而这张表只在 US QWERTY 下是对的: AZERTY、QWERTZ、Dvorak、西里尔字母、死键和 AltGr 打出来的都是错字。(IME 用户不受影响 —— 组字结果走 TSF,不走这张表。)

两种办法任选其一。两种都没做时,文本框在第一次被编辑时打一条警告。

一、直接用插件的视口客户端

在项目的 Config/DefaultEngine.ini:

[/Script/Engine.Engine]
GameViewportClientClassName=/Script/DreamGUIInput.DreamGameViewportClient

二、保留你自己的视口客户端

要么让它从 UDreamGameViewportClient 派生,而不是 UGameViewportClient;要么保留原来的基类,在它的 InputChar 重写里把字符交给 DreamGUI(模块 DreamGUIInput,头文件 Interaction/DreamUITextInputTarget.h)—— 在控制台之后、在基类之前:

#include "Interaction/DreamUITextInputTarget.h"

bool UMyGameViewportClient::InputChar(FViewport* InViewport, int32 ControllerId, TCHAR Character)
{
    FString CharacterString;
    CharacterString += Character;
    // 控制台开着时,它拿走每一个字符。
    if (ViewportConsole && ViewportConsole->InputChar(FInputDeviceId::CreateFromInternalId(ControllerId), CharacterString))
    {
        return true;
    }
    // 必须在基类之前:PIE 视口里基类对每个字符都回答 true,排在它后面的文本框在那里一个字符也收不到。
    if (!IgnoreInput() && DreamUITextInputRouter::RouteViewportCharacter(this, ControllerId, Character))
    {
        return true;
    }
    return Super::InputChar(InViewport, ControllerId, Character);
}

顺序为什么是这样:PIE 视口里,基类会把每个字符都吞掉(返回 true),免得游戏输入漏到编辑器自己的窗口; 先问基类再看它的返回值,就永远到不了文本框 —— PIE 里打字退回对照表,而打包后的游戏里基类返回 false, 又能拿到真字符,两边行为不一致。

DreamUITextInputRouter::RouteViewportCharacter 就是全部契约:它把字符交给打字的那个玩家(ControllerId 对应的玩家)正在编辑的文本框;没有文本框要,就作为一个按键字符交给这个玩家当前聚焦的东西;返回两者中是否有一个收下了。 从第一个这样到达的字符起,FKey 对照表在这个世界里就不再合成可打印字符,两条路不会重复打字。 项目里需要的模块依赖是 DreamGUIInput。

UDreamGameViewportClient 还做两件事:在 DreamGUI 自己的 UI-only 输入模式(UDreamUIInputModeLibrary::SetInputModeUIOnly) 下把引擎会忽略的输入照常送给 DreamGUI;在 DreamGUI 有焦点时,不让 Slate 自己的导航(Tab、方向键)把键盘焦点 从视口带进 UMG。自己写视口客户端时,后一件由世界的输入子系统通过基类的 OnNavigationOverride 兜着。

任何输入模式下都有输入:Slate 输入源

默认情况下,DreamGUI 通过它的预设事件系统 Actor 在玩家控制器上的绑定来听输入,所以在引擎自己的 UI-only 输入模式下它什么也听不到:SetInputMode(FInputModeUIOnly()) 让游戏视口忽略输入,控制器根本收不到。

打开 Project Settings ▸ Plugins ▸ Dream GUI ▸ Input ▸ Use Slate Input Source(bUseSlateInputSource), DreamGUI 就改从 Slate 本身听鼠标、触摸、按键和摇杆 —— 一个输入预处理器,排在游戏视口之前 —— 在任何输入模式下都有效。 开着它时,预设 Actor 自动让位,同一个输入不会到两次。

Slate Input Consume Policy(SlateInputConsumePolicy)决定 UI 从游戏那里扣下什么:

取值UI 扣下的
Never(默认)什么都不扣,游戏照常听到一切
WhenOverUI落在 DreamGUI UI 上的按下、松开和滚轮,以及 UI 收下的任何按键
WhenHandled只有落在"会处理按下"的控件上的按下,以及 UI 收下的按键

正在编辑的文本框里打的键,无论哪种策略都会扣下。同一组设置里还有 SlateInputStickScrollSpeed(默认 1500): 右摇杆滚动聚焦内容的速度,单位是画布单位每秒(推满时)。

Slate 输入源目前默认关闭,会在以后的某个版本里变成默认。

设置存在哪里

页面类写到
Project Settings ▸ Plugins ▸ Dream GUIUDreamGUISettings项目的 Config/DefaultDreamGUI.ini,[/Script/DreamGUI.DreamGUISettings]
Project Settings ▸ Plugins ▸ DreamUIUDreamUISettings项目的 Config/DefaultEngine.ini(config=Engine)
Project Settings ▸ Plugins ▸ DreamGUI EditorUDreamUIEditorSettings项目的 Config/DefaultEditor.ini(config=Editor)

两个运行时设置类都是在用到的地方读,所以运行时改了一个值,从下一帧起生效。

插件的 Config/Game.ini 把 /DreamGUI 放进 DirectoriesToAlwaysCook,所以按显式包列表烹饪(-map=、chunk)时 插件的默认资产也会被烹进去。

下一步

本页目录