当前位置: 首页 > news >正文

UEC++学习(十八)使用TAutoConsoleVariable<T> / FAutoConsoleCommand自定义控制台变量/命令

UE常用的添加控制台变量/命令的方式。利用C++的RAII(资源获取即初始化)机制,在构造时自动向控制台管理器注册自己,在析构时自动注销,无需手动管理生命周期。还有一种是使用IConsoleManager手动注册,但是需要手动管理取消注册,否则可能会导致内存泄露问题。

(一)使用TAuroConsoleVariable<T>创建控制台变量

目的暴露一个可实时修改的配置变量到控制台。它本质上是一个变量,其值可以通过控制台获取和修改,并且当值改变时,可以自动触发一个回调函数

要注意TAuroConsoleVariable定义的变量会自动记录当前值

(1)定义控制台变量:

//GameInstance.CPP中// 声明自定义控制台变量
static TAutoConsoleVariable<int32> CVarShowDebugUI(TEXT("ui.ShowDebugUI"),// 控制台变量名称0,							// 默认值TEXT("Show or hide debug UI\n")TEXT("0: Hide (default)\n")TEXT("1: Show"),			// 帮助文本ECVF_Cheat);			// 标志位static TAutoConsoleVariable<FString> CVarSpawnDebugActor(TEXT("test.SpawnActor"),TEXT(""),TEXT("生成Actor\n")TEXT("Format: ClassName"),ECVF_Cheat);

也可以使用FAutoConsoleVariableRef绑定现有变量:


int32 MyBoundVariable = 50;
float MyBoundFloat = 5.0f;// 将控制台变量绑定到现有整数变量
FAutoConsoleVariableRef CVarRefMyVar(TEXT("MyModule.BoundVar"),MyBoundVariable, // 绑定到现有变量TEXT("绑定到现有整数变量的控制台变量。"),ECVF_Default
);// 将控制台变量绑定到现有浮点数变量
FAutoConsoleVariableRef CVarRefMyFloat(TEXT("MyModule.BoundFloat"),MyBoundFloat,TEXT("绑定到现有浮点数变量的控制台变量。"),ECVF_Default
);
// 现在MyBoundVariable的值会随着控制台命令"MyModule.BoundVar"的修改而自动改变

定义控制台变量时可以使用的常见标志位:

标志位描述
ECVF_Default默认值,无特殊标志
ECVF_Cheat仅在作弊模式下有效,发行版本中通常无效
ECVF_RenderThreadSafe可在渲染线程中安全访问
ECVF_Scalability与画面缩放质量相关的设置
ECVF_Unregistered未注册的变量,不会在控制台中显示
ECVF_ScalabilityGroup标识为可伸缩性组别的一部分

(2)绑定回调函数

void UTestAdbGameInstance::Init()
{Super::Init();// 注册控制台命令//有两种方式://(1)使用控制台变量Sink注册:会在渲染前的主线程调用//例如要每次获取CVarShowDebugUI变量值的变化FAutoConsoleVariableSink CVarShowDebugUISink(FConsoleCommandDelegate::CreateStatic(&OnSinkBindFunc));//(2)使用SetOnChangedCallback,但是这样方法容易导致死锁问题,在任何线程都会调用CVarShowDebugUI->SetOnChangedCallback(FConsoleVariableDelegate::CreateUObject(this, &UTestAdbGameInstance::OnShowDebugUIChanged));CVarSpawnDebugActor->SetOnChangedCallback(FConsoleVariableDelegate::CreateUObject(this, &UTestAdbGameInstance::OnSpawnDebugActorChanged));}void UTestAdbGameInstance::Shutdown()
{Super::Shutdown();// 取消注册控制台命令CVarShowDebugUI->SetOnChangedCallback(FConsoleVariableDelegate());CVarSpawnDebugActor->SetOnChangedCallback(FConsoleVariableDelegate());}void UTestAdbGameInstance::OnShowDebugUIChanged(IConsoleVariable* Var)
{//获取变量的值int32 ShowDebugUI = Var->GetInt();UE_LOG(LogTemp, Display, TEXT("ShowDebugUI = %d"), ShowDebugUI);
}void UTestAdbGameInstance::OnSpawnDebugActorChanged(IConsoleVariable* Var)
{FString ActorClassStr = Var->GetString();if (!ActorClassStr.IsEmpty()){UE_LOG(LogTemp, Display, TEXT("ActorClass: %s"), *ActorClassStr);}else{UE_LOG(LogTemp, Display, TEXT("ActorClass: Default"));}// 重置变量值// Var->Set(TEXT(""));
}//静态函数
void UTestAdbGameInstance::OnSinkBindFunc()
{int32 Value = CVarShowDebugUI->GetInt();UE_LOG(LogTemp, Display, TEXT("ShowDebugUI = %d"), Value);
}

如果需要在其他地方获取这个变量的值,可以使用:

void UTestAdbGameInstance::TestGetConsoleVar()
{// 通用获取方式(自动检测线程)int32 Value1 = CVarShowDebugUI->GetInt();//自动检测线程int32 AnyThreadValue = CVarShowDebugUI.GetValueOnAnyThread();// 在游戏线程中读取int32 GameThreadValue = CVarShowDebugUI.GetValueOnGameThread();// 在渲染线程中读取int32 RenderThreadValue = CVarShowDebugUI.GetValueOnRenderThread();//使用FindConsoleVariable查找int32 Value = IConsoleManager::Get().FindConsoleVariable(TEXT("ui.ShowDebugUI"))->GetInt();UE_LOG(LogTemp, Display, TEXT("Value1: %d , AnyThreadValue: %d , Value: %d"), Value1,AnyThreadValue,Value);
}

(3)使用控制台命令

在游戏中按下 ~ ,输入:ui.ShowDebugUI 0/1 或者 test.SpawnActor 要生成的actor


(二)使用FAutoConsoleCommand创建控制台命令

目的注册一个执行特定操作的命令。它不持有状态,而是在用户输入命令时执行一个动作。

类型委托类型参数传递适用场景
无参数命令FConsoleCommandDelegate执行简单操作,如打印信息、触发简单事件
带简单参数的命令FConsoleCommandWithArgsDelegateconst TArray<FString>& Args需要传递一个或多个字符串参数的操作
带世界和参数的命令FConsoleCommandWithWorldAndArgsDelegateconst TArray<FString>& Args, UWorld* World需要访问世界上下文和参数的操作
带世界、参数和输出设备的命令FConsoleCommandWithWorldArgsAndOutputDeviceDelegateconst TArray<FString>& Args, UWorld* World, FOutputDevice& Ar需要世界上下文、参数并控制输出信息的复杂操作

(1)定义

(1)无参数命令
//不使用Lamdba
static FAutoConsoleCommand CVarMySimpleCommand111(TEXT("MyGame.SimpleCommand111"), // 控制台命令名称TEXT("这是一个简单的无参数命令,用法: test.SimpleCommand"), // 命令帮助说明FConsoleCommandDelegate::CreateStatic(&UTestAdbGameInstance::TestFunc)
);void UTestAdbGameInstance::TestFunc()
{UE_LOG(LogTemp, Display, TEXT("SimpleCommand111"));
}//使用Lambda
static FAutoConsoleCommand CVarMySimpleCommand(TEXT("MyGame.SimpleCommand"), // 控制台命令名称TEXT("这是一个简单的无参数命令,用法: test.SimpleCommand"), // 命令帮助说明FConsoleCommandDelegate::CreateLambda([]() // 使用Lambda表达式处理命令{UE_LOG(LogTemp, Display, TEXT("SimpleCommand"));})
);(2)带简单参数命令
static FAutoConsoleCommand CVarMyCommandWithArgs(TEXT("MyGame.CommandWithArgs"),TEXT("这是一个带参数的命令。用法: MyGame.CommandWithArgs [Param1] [Param2]"),FConsoleCommandWithArgsDelegate::CreateLambda([](const TArray<FString>& Args){if (Args.Num() == 0){UE_LOG(LogTemp, Warning, TEXT("请提供参数! 用法: MyGame.CommandWithArgs [Param1] [Param2]"));return;}// 遍历并打印所有参数for (int32 i = 0; i < Args.Num(); ++i){UE_LOG(LogTemp, Log, TEXT("参数[%d]: %s"), i, *Args[i]);}// 示例:根据参数设置某个变量if (Args.Num() >= 1){// 尝试将第一个参数转换为整数int32 MyValue = FCString::Atoi(*Args[0]);// ... 这里用MyValue做一些事情 ...}})
);(3)带世界和参数的命令
static FAutoConsoleCommandWithWorldAndArgs CVarMyCommandWithWorldAndArgs(TEXT("MyGame.CommandWithWorld"),TEXT("这是一个需要世界上下文和参数的命令。"),FConsoleCommandWithWorldAndArgsDelegate::CreateLambda([](const TArray<FString>& Args, UWorld* InWorld){if (InWorld){UE_LOG(LogTemp, Log, TEXT("当前世界: %s"), *InWorld->GetName());}else{UE_LOG(LogTemp, Warning, TEXT("世界上下文为空"));}})
);(4)带世界、参数和输出设备的命令
static FAutoConsoleCommand CVarMyComplexCommand(TEXT("MyGame.ComplexCommand"),TEXT("这是一个复杂的命令,接收参数、世界上下文并能控制输出。"),FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateLambda([](const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar){// 使用Ar来直接输出信息(会显示在控制台)Ar.Logf(TEXT("开始执行MyGame.ComplexCommand..."));if (Args.Num() < 1){Ar.Logf(TEXT("错误:需要至少一个参数。"));return;}//})
);

获取已注册的命令:

IConsoleManager::Get().FindConsoleObject(TEXT("MyGame.SimpleCommand"))

(2)使用

(1)无参数命令

(2)带简单参数命令

(3)带世界和参数的命令

(4)带世界、参数和输出设备的命令

(三)使用IConsoleManager

(1)定义控制台变量

void UTestAdbGameInstance::TestFunc()
{// 注册一个整数变量IConsoleVariable* CVarDynamicInt = IConsoleManager::Get().RegisterConsoleVariable(TEXT("MyModule.DynamicInt"),0,TEXT("动态注册的整数变量。"),ECVF_Default);  
}    

(2)定义控制台命令

void UTestAdbGameInstance::TestFunc()
{IConsoleCommand* MyDynamicCmd = IConsoleManager::Get().RegisterConsoleCommand(TEXT("MyModule.DynamicCmd"),TEXT("动态注册的无参数命令。"),FConsoleCommandDelegate::CreateLambda([](){UE_LOG(LogTemp, Log, TEXT("动态命令被执行了!"));}),ECVF_Default);// 注册一个带参数命令IConsoleCommand* MyDynamicCmdWithArgs = IConsoleManager::Get().RegisterConsoleCommand(TEXT("MyModule.DynamicCmdWithArgs"),TEXT("动态注册的带参数命令。"),FConsoleCommandWithArgsDelegate::CreateLambda([](const TArray<FString>& Args){UE_LOG(LogTemp, Log, TEXT("收到了 %d 个参数"), Args.Num());}),ECVF_Default);// 当你不再需要这个命令时(例如模块卸载时),记得注销它// ConsoleManager.UnregisterConsoleObject(MyDynamicCmd);// ConsoleManager.UnregisterConsoleObject(MyDynamicCmdWithArgs);
}

(3)查找与控制控制台变量/命令

void UTestAdbGameInstance::TestFunc()
{// 查找一个控制台变量IConsoleVariable* FoundCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ScreenPercentage"));if (FoundCVar){// 获取当前值int32 CurrentValue = FoundCVar->GetInt();UE_LOG(LogTemp, Log, TEXT("r.ScreenPercentage 当前值为: %d"), CurrentValue);// 设置新值(谨慎操作,可能影响其他系统)FoundCVar->Set(120); // 设置为120%}//查找一个控制台命令对象(较少用于直接执行,通常直接输入命令名)IConsoleObject* ConsoleObj = IConsoleManager::Get().FindConsoleObject(TEXT("MyModule.MyCommand"));if (ConsoleObj && ConsoleObj->AsCommand()){// 获取命令信息等}
}

(四)总结

类别方法关键特点适用场景
控制台变量TAutoConsoleVariable<T>类型安全,静态初始化,常用声明全局配置参数、功能开关
IConsoleManager::RegisterConsoleVariable动态注册,返回指针在运行时根据需要动态创建变量
FAutoConsoleVariableRef绑定到现有变量,值自动同步(但会绕过线程安全等功能)快速将现有变量暴露给控制台
控制台命令FAutoConsoleCommand静态初始化,支持无参数、带参数、世界上下文等多种委托创建执行特定操作或调试的命令
IConsoleManager::RegisterConsoleCommand动态注册,返回指针,需手动管理生命周期在插件或模块中动态创建命令,并可卸载
查找与交互IConsoleManager::FindConsoleVariable查找已存在的CVar获取或修改引擎或其他模块定义的变量值
IConsoleManager::FindConsoleObject查找任何控制台对象(变量或命令)检查命令或变量是否存在
响应变量变化轮询检查 (Polling)简单,可控(例如在Tick中检查)简单的游戏逻辑响应
FAutoConsoleVariableSink在主线程特定点(如渲染前)集中处理多个CVar变化响应引擎关键设置(如渲染质量)变化,需集中处理
IConsoleVariable::SetOnChangedCallback (谨慎使用)变化立即回调,但需注意线程安全和调用时机需要立即响应的特殊情况下,并确保线程安全


文章转载自:

http://RtaWhuqv.bwrbm.cn
http://1lJyo4M2.bwrbm.cn
http://qBnba6HC.bwrbm.cn
http://IXrOeofX.bwrbm.cn
http://vRS2Zfr4.bwrbm.cn
http://tzdGx0gB.bwrbm.cn
http://4rJrNYcm.bwrbm.cn
http://nhwNmMI5.bwrbm.cn
http://I2hlHLbU.bwrbm.cn
http://LoPNzvys.bwrbm.cn
http://n2PdfdIb.bwrbm.cn
http://m376j6DW.bwrbm.cn
http://zkWM3L6r.bwrbm.cn
http://YTTIWFQr.bwrbm.cn
http://Jz5Kwnsa.bwrbm.cn
http://1me6LJpu.bwrbm.cn
http://lhOFHxnv.bwrbm.cn
http://aYzyvPY9.bwrbm.cn
http://9ILQaX3E.bwrbm.cn
http://wNkXZbfW.bwrbm.cn
http://NdHgRmtD.bwrbm.cn
http://F8UCGSSh.bwrbm.cn
http://NcFfbijJ.bwrbm.cn
http://NU09Mdx5.bwrbm.cn
http://oWxa9gJh.bwrbm.cn
http://WC7GGHQu.bwrbm.cn
http://6UAQqCFr.bwrbm.cn
http://ouFdlebh.bwrbm.cn
http://gomEnocU.bwrbm.cn
http://owN4CJTb.bwrbm.cn
http://www.dtcms.com/a/378381.html

相关文章:

  • 2.9Vue创建项目(组件)的补充
  • MasterGo蒙版
  • 一次.dockerignore设置错误导致的docker build排查
  • 第六节,探索 ​​CSS 的高级特性、复杂布局技巧、性能优化以及与现代前端工作流的整合​​
  • Flink on YARN 实战问题排查指南(精华版)
  • Java全栈学习笔记34
  • 进程控制(1)
  • 操作系统进程管理——同步与互斥的基本概念
  • 灰色关联分析笔记
  • CAD文件坐标系未知?用Bigemap Pro自动计算中央子午线,准确定位!
  • 项目管理核心八项(软件篇)
  • 创新驱动:医养照护与管理实训室建设方案构建
  • C++ 之 cli窗口交互程序测试DLL
  • openEuler系统远程管理方案:cpolar实现安全高效运维
  • Spring常用注解介绍
  • 《秋鳞小故事——编译器》
  • 【前端Vue】如何优雅地在vue中引入ace-editor编辑器
  • 架构深解:英伟达Rubin CPX如何通过专用预填充加速器与解耦架构重塑AI推理效率与成本
  • 线下小店悄然增长:两个关键模式与它们的运营启示
  • 开发安全利器:detect-secrets 敏感信息扫描工具实战指南
  • 中间件架构设计与实践:构建高性能分布式系统的核心基石
  • 错误于make.names(vnames, unique = TRUE): invalid multibyte string 9 使用 R 语言进行数据处理时
  • 前端基础标签
  • 深度学习基本模块:ConvTranspose2D 二维转置卷积层
  • 多模态数据治理新范式:衡石Agentic BI如何统一结构化与非结构化数据?
  • Gopeed下载器本地部署指南:cpolar实现远程任务管理
  • App 苹果 上架全流程解析 iOS 应用发布步骤、App Store 上架流程
  • unity UGUI 鼠标画线
  • ALBEF(Align Before Fuse)
  • redis 集群——redis cluster(去中心化)