You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #ROBOMERGE-OWNER: ryan.vance #ROBOMERGE-AUTHOR: mitchell.wilson #ROBOMERGE-SOURCE: CL 4860421 in //UE4/Main/... #ROBOMERGE-BOT: DEVVR (Main -> Dev-VR) [CL 4860484 by mitchell wilson in Dev-VR branch]
315 lines
13 KiB
Plaintext
315 lines
13 KiB
Plaintext
INTSourceChangelist:3782314
|
||
Availability: Public
|
||
Title:C++中的控制台变量
|
||
Crumbs:%ROOT%, Engine, Programming, Programming/Basics
|
||
Description:控制台管理器的总览与创建控制台变量的实现细节。
|
||
Version: 4.16
|
||
Parent:Programming/Development/Tools
|
||
Order:
|
||
topic-image:consoleVar_topicImage.png
|
||
|
||
[TOC(start:2)]
|
||
|
||
|
||
|
||
|
||
**控制台命令** 是一条用户输入字符串,其可被发送到引擎,而引擎能以某种方式进行回应(如控制台/日志响应,修改内部状态)。
|
||
**控制台变量** 将额外保存一些状态,可通过控制台进行修改。在控制台管理器中注册控制台命令和变量即可得到自动完成和列举,以便获取所有控制台对象的列表(控制台命令Help或DumpConsoleVariables)。因此需要避免使用老旧的Exec接口。中间点中的控制台管理器将控制所有内容和更多内容(如用户输入历史)。
|
||
|
||
|
||
(#whatisaconsolevariable?)
|
||
|
||
## 什么是控制台变量?
|
||
|
||
控制台变量是一种简单的数据类型(如浮点、整数、字符串)。其拥有引擎层面的状态,使用者可对其状态进行读取和设置。
|
||
控制台变量拥有命名,使用者在控制台中输入命名时将自动完成拼写。举例而言:
|
||
|
||
|**用户控制台输入**|**控制台输出**|**描述**|
|
||
| --- | --- | --- |
|
||
|`MyConsoleVar`|`MyConsoleVar = 0`|变量的当前状态将被打印到控制台中。|
|
||
|`MyConsoleVar 123`|`MyConsoleVar = 123 LastSetBy: Constructor`|变量的状态已改变,新状态将被打印到控制台中。|
|
||
|`MyConsoleVar ?`|`Possibly multi line help text.`|将控制台变量帮助文本打印到控制台中。|
|
||
|
||
|
||
|
||
(#creating/registeringaconsolevariable)
|
||
|
||
## 创建/注册控制台变量
|
||
|
||
引擎创建时需要尽早注册变量。此例显示了最佳操作方法(在任意C++文件中):
|
||
|
||
static TAutoConsoleVariable<int32> CVarRefractionQuality(
|
||
TEXT("r.RefractionQuality"),
|
||
2,
|
||
TEXT("Defines the distortion/refraction quality, adjust for quality or performance.\n")
|
||
TEXT("<=0: off (fastest)\n")
|
||
TEXT(" 1: low quality (not yet implemented)\n")
|
||
TEXT(" 2: normal quality (default)\n")
|
||
TEXT(" 3: high quality (e.g. color fringe, not yet implemented)"),
|
||
ECVF_Scalability | ECVF_RenderThreadSafe);
|
||
|
||
我们在此处注册了一个类型为int32的控制台变量,命名为r.RefractionQuality、默认值为2,并包括一些多行帮助文本和一些标签。
|
||
标签有很多个,而最重要的则是ECVF_Cheat。其在IConsoleManager.h中有详细讲解。
|
||
使用者在控制台变量后使用“?”即可显示帮助文本。
|
||
|
||
如有需要,也可以在函数中生成一个控制台变量:
|
||
|
||
IConsoleManager::Get().RegisterConsoleVariable(TEXT("r.RefractionQuality"),
|
||
2,
|
||
TEXT("Defines the distortion/refraction quality, adjust for quality or performance.\n")
|
||
TEXT("<=0: off (fastest)\n")
|
||
TEXT(" 1: low quality (not yet implemented)\n")
|
||
TEXT(" 2: normal quality (default)\n")
|
||
TEXT(" 3: high quality (e.g. color fringe, not yet implemented)"),
|
||
ECVF_Scalability | ECVF_RenderThreadSafe);
|
||
|
||
`IConsoleManager::Get()` 是全局访问点。可在此处注册控制台变量,或寻找现有的控制台变量。第一个参数是控制台变量的命名。
|
||
第二个参数是默认值。此常量的类型不同,创建的控制台变量类型也有所不同:整数、浮点或字符串(!FString)。
|
||
下个参数将定义控制台变量帮助文本。
|
||
|
||
也可以注册一个对现有变量的引用。这种方法方便快速但会绕过多项功能(如线程安全、回调、sink、cheat),因此不建议使用此方法。请参考下例:
|
||
|
||
FAutoConsoleVariableRef CVarVisualizeGPUSimulation(
|
||
TEXT("FX.VisualizeGPUSimulation"),
|
||
VisualizeGPUSimulation,
|
||
TEXT("Visualize the current state of GPU simulation.\n")
|
||
TEXT("0 = off\n")
|
||
TEXT("1 = visualize particle state\n")
|
||
TEXT("2 = visualize curve texture"),
|
||
ECVF_Cheat
|
||
);
|
||
|
||
其类型可由变量类型推断而出。
|
||
|
||
|
||
|
||
(#gettingthestateofaconsolevariable)
|
||
|
||
## 获取控制台变量的状态
|
||
|
||
获取以 **RegisterConsoleVariableRef** 创建的控制台变量的状态十分便利,使用其注册时使用的变量即可。举例而言:
|
||
|
||
// 只在未处于相同cpp文件中时需要
|
||
extern TAutoConsoleVariable<int32> CVarRefractionQuality;
|
||
// 获取游戏线程上的值
|
||
int32 MyVar = CVarRefractionQuality.GetValueOnGameThread();
|
||
|
||
使用Getter函数(即!GetInt()、!GetFloat()、!GetString())来确定控制台变量状态会导致实现速度稍慢(虚拟函数调用、可能的缓存丢失等)。
|
||
要获得最佳性能,应使用用于注册变量的相同类型。为获取变量的指针,可保存注册函数的返回参数或在需要变量之前调用 **FindConsoleVariable**。范例如下:
|
||
|
||
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("TonemapperType"));
|
||
int32 Value = CVar->GetInt();
|
||
|
||
此处的静态将确保命名搜索(实现为地图)只在此代码首次调用时完成。
|
||
这是正确的,因为变量从不会移动,只会在引擎关闭时被销毁。
|
||
|
||
|
||
(#howtotrackconsolevariablechanges)
|
||
|
||
## 如何追踪控制台变量修改
|
||
|
||
如果希望在控制台变量变化时执行一些自定义代码,可在三个方法中进行选择。
|
||
|
||
通常最简单的方法即是最佳方法:可将旧状态保存在子系统中,并每帧检查其是否有所不同。可在此处自由控制其发生时间(如渲染线程或游戏线程、流送线程、tick或渲染前后)。
|
||
检测到差异时复制控制台变量状态并自定义代码。范例如下:
|
||
|
||
void MyFunc()
|
||
{
|
||
int GBufferFormat = CVarGBufferFormat.GetValueOnRenderThread();
|
||
|
||
if(CurrentGBufferFormat != GBufferFormat)
|
||
{
|
||
CurrentGBufferFormat = GBufferFormat;
|
||
|
||
// 自定义代码
|
||
}
|
||
}
|
||
|
||
也可注册一个控制台变量sink。范例如下:
|
||
|
||
static void MySinkFunction()
|
||
{
|
||
bool bNewAtmosphere = CVarAtmosphereRender.GetValueOnGameThread() != 0;
|
||
|
||
// 默认假定状态为true
|
||
static bool GAtmosphere = true;
|
||
|
||
if (GAtmosphere != bNewAtmosphere)
|
||
{
|
||
GAtmosphere = bNewAtmosphere;
|
||
|
||
// 自定义代码
|
||
}
|
||
}
|
||
|
||
FAutoConsoleVariableSink CMyVarSink(FConsoleCommandDelegate::CreateStatic(&MySinkFunction));
|
||
|
||
sink将在渲染前主线程上的一个指定点处调用。函数不会获取控制台变量命名/指针,因为这通常会导致错误行为。
|
||
如多个控制台变量(如r.SceneColorFormat、r.GBufferFormat)皆会触发修改,则最好在所有修改完成后调用代码,而非一个接一个调用。
|
||
|
||
最后一种方法是使用回调,建议尽量不使用。如不谨慎使用,可能会引起问题:
|
||
|
||
* 循环可能出现死锁(可以防止死锁出现,但使用哪个回调则不明确)。
|
||
* 回调可在 **!Set()** 被调用中的任意时间处返回。代码必须在所有情况下(在初始化中、在序列化中)均能使用。
|
||
可假定其固定处于主线程一侧。
|
||
|
||
不建议使用此方法,除非用上述其他方法已无法解决。
|
||
|
||
范例:
|
||
|
||
void OnChangeResQuality(IConsoleVariable* Var)
|
||
{
|
||
SetResQualityLevel(Var->GetInt());
|
||
}
|
||
|
||
CVarResQuality.AsVariable()
|
||
->SetOnChangedCallback(FConsoleVariableDelegate::CreateStatic(&OnChangeResQuality));
|
||
|
||
|
||
|
||
(#intendedconsolevariablebehaviorandstyle)
|
||
|
||
## 控制台变量预期行为和风格
|
||
|
||
* 控制台输入应反映用户输入,并不一定反映系统的状态(例如部分平台可能不支持!MotionBlur 0/1)。
|
||
变量状态不应由代码修改。否则变量不含用户指定的状态时用户会无端怀疑自己错误输入,
|
||
又或者用户可能因其他变量的状态而无法对控制台变量进行修改。
|
||
* 请务必提供解释,说明变量的用途和恰当的值。
|
||
* 多数控制台变量仅适用于开发,因此建议尽早指定 `ECVF_Cheat` 标签。
|
||
能够使用定义来编译出功能则更佳(如#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST))。
|
||
* 变量命名应尽量简短,描述性强,不应使用否定含义。
|
||
(举例而言,较差的命名有!EnableMotionBlur、!MotionBlurDisable、MBlur、!HideMotionBlur)。
|
||
使用大小写,使命名统一、易读(如!MotionBlur)。
|
||
* 为便于缩进,可假定为固定的宽度字体(非比例)输出。
|
||
* 在引擎初始化中注册变量十分重要,这样自动完成、!DumpConsoleCommands和!Help才能正常工作。
|
||
|
||
请阅读 `IConsoleManager.h` 了解这部分详情。
|
||
|
||
|
||
(#loadingconsolevariables)
|
||
|
||
## 加载控制台变量
|
||
|
||
引擎启动时,可从文件 **Engine/Config/ConsoleVariables.ini** 加载控制台变量的状态。此位置预留给本地开发者,不应用于项目设置。
|
||
文件自身中有更多详情:
|
||
|
||
; 利用此文件当前可在引擎启动时设置控制台变量(排序未定义)。
|
||
; 当前无其他文件覆盖此文件。
|
||
; 此文件应在源码管理数据库中(便于进行注释和了解在何处进行查找)
|
||
; 变量中不应含空格。
|
||
; 开发者可在本地对其进行修改,避免输入重复的控制台变量设置,
|
||
; 以节约时间。须将变量放在名为[Startup]的部分中。
|
||
; 之后可能会有多个命名部分被部分命名所引用。
|
||
; 这会实现平台或关卡特定的覆盖。
|
||
; 命名对比不区分大小写;如果变量不存在,其便会被略过。
|
||
;
|
||
; 文本内容范例:
|
||
;
|
||
; [Startup]
|
||
; FogDensity = 0.9
|
||
; ImageGrain = 0.5
|
||
; FreezeAtPosition = 2819.5520 416.2633 75.1500 65378 -25879 0
|
||
|
||
[Startup]
|
||
|
||
可将设定放在 **Engine/Config/BasEngine.ini** 中。范例如下:
|
||
|
||
[SystemSettings]
|
||
r.MyCvar = 2
|
||
|
||
[SystemSettingsEditor]
|
||
r.MyCvar = 3
|
||
|
||
设置也可来自 **Script/Engine.RendererSettings**。这些项目设置公开如下:
|
||
|
||
UPROPERTY(config, EditAnywhere, Category=Optimizations, meta=(
|
||
ConsoleVariable="r.EarlyZPassMovable",DisplayName="Movables in early Z-pass",
|
||
ToolTip=”是否在早Z阶段渲染可移动对象。需要重新加载关卡!"))
|
||
uint32 bEarlyZPassMovable:1;
|
||
|
||
这些设置可在编辑器UI中修改。项目设置不应与可延展性设置混合(避免出现优先级问题)。
|
||
|
||
其他设置可来自可延展性功能。查看 **Config/BaseScalability.ini** 或可延展性文档了解更多信息。
|
||
|
||
(#commandline)
|
||
|
||
## 命令行
|
||
|
||
命令行允许设置控制台变量、调用控制台命令或exec命令。范例如下:
|
||
|
||
UE4Editor.exe GAMENAME -ExecCmds="r.BloomQuality 12;vis 21;Quit"
|
||
|
||
可在此处执行3个命令。注意:以此方式设置控制台变量需要省略掉ini文件中需要的“=”。
|
||
|
||
(#priority)
|
||
|
||
## 优先级
|
||
|
||
控制台变量可从多个源处覆盖,如用户/编辑器/项目设置、命令行、consolevariables.ini等为能够重新指定部分设置(如可在编辑器UI中修改的项目设置)而保持指定的覆盖(如从命令行),我们加入了优先级。
|
||
现在所有设置均能按任意排序来应用。
|
||
|
||
参见IConsoleManager.h:
|
||
|
||
//最低优先级(控制台变量创建后为默认)
|
||
ECVF_SetByConstructor = 0x00000000,
|
||
// 来自Scalability.ini
|
||
ECVF_SetByScalability = 0x01000000,
|
||
//(在游戏UI或来自文件)
|
||
ECVF_SetByGameSetting = 0x02000000,
|
||
// 项目设置
|
||
ECVF_SetByProjectSetting = 0x03000000,
|
||
// 逐设备设置
|
||
ECVF_SetByDeviceProfile = 0x04000000,
|
||
// 逐项目设置
|
||
ECVF_SetBySystemSettingsIni = 0x05000000,
|
||
// consolevariables.ini(针对多个项目)
|
||
ECVF_SetByConsoleVariablesIni = 0x06000000,
|
||
// 减法命令,如-VSync
|
||
ECVF_SetByCommandline = 0x07000000,
|
||
// 用处不大,就像是hack一样,也许找到正确的SetBy更好
|
||
ECVF_SetByCode = 0x08000000,
|
||
// 编辑器UI或游戏/编辑器中的控制台
|
||
ECVF_SetByConsole = 0x09000000,
|
||
|
||
一些情况下可能会出现此日志:
|
||
|
||
Console variable 'r.MyVar' wasn't set (Priority SetByDeviceProfile < SetByCommandline)
|
||
|
||
这可能是正常情况(如命令行强制进行用户设置),也可能是因一些代码问题而造成。
|
||
优先级也有所帮助,可以查看最后设置变量的是谁。获取控制台变量状态时即可获得此信息。范例如下:
|
||
|
||
> r.GBuffer
|
||
|
||
r.GBuffer = "1" LastSetBy: Constructor
|
||
|
||
|
||
|
||
|
||
(#future)
|
||
|
||
## 未来更新
|
||
|
||
* 现版本中控制台变量只能在C++中创建,但未来情况可能会不同。
|
||
* 我们考虑过添加列举和布尔类型,但存在诸多问题。现在我们建议使用整数,必要时使用字符串。
|
||
* 帮助文本虽然方便,但为了节约可执行文件大小并增加作弊的难度,我们考虑在未来加入一个定义,防止帮助文本进入可执行文件。
|
||
|
||
|
||
(#unregisteringconsolevariables)
|
||
|
||
## 取消注册控制台变量
|
||
|
||
用 **UnregisterConsoleVariable** 方法可移除控制台变量。至少从使用者角度而言是如此。
|
||
变量仍被保留(带未注册的标签),使指针访问数据时不会发生崩溃。如新变量以相同命名注册,旧变量将被恢复,并从新变量复制标签。
|
||
这样DLL加载和卸载便能正常工作,变量状态也不会丢失。
|
||
注意:这无法用于控制台变量引用。
|
||
[COMMENT:none]
|
||
为进行简化,下方的句子已废弃。
|
||
[/COMMENT]
|
||
[COMMENT:none]
|
||
放弃一项即可修复:不保存指针、不取消注册或不使用引用。
|
||
[/COMMENT]
|
||
|
||
|
||
|
||
|