[{"data":1,"prerenderedAt":543},["ShallowReactive",2],{"page-Config-1":3,"page-count-Config":193},[4,224],{"id":5,"title":6,"body":7,"date":199,"description":13,"extension":200,"meta":201,"navigation":204,"path":216,"seo":217,"stem":218,"tags":219,"__hash__":223},"blogs\u002F_legacy\u002F2017\u002F2017-10-14-ue4-config-setting.md","UE4的配置界面写入",{"type":8,"value":9,"toc":192},"minimark",[10,14,17,26,31,34,37,40,43,54,57,61,64,70,73,76,79,107,110,117,122,125,128,132,135,138,161,164,167,170,173,179,182],[11,12,13],"p",{},"UE4的Config系统使用起来很方便，但是如果要让制作出来的插件\u002F项目更方便的让其他人设置，就需要将其写到编辑器的配置列表中。",[11,15,16],{},"当前使用的UE4版本为4.18.0 P4。",[11,18,19,20,25],{},"这里的内容是以",[21,22,24],"a",{"href":23},"\u002F2017-06-06-ue4-config-usage\u002F","Conifg系统","的使用为前提的，只是简略的记录了插件制作过程中，将Conifg注入到编辑器界面的过程。",[27,28,30],"h2",{"id":29},"udevelopersettings","UDeveloperSettings",[11,32,33],{},"让配置类继承自UDeveloperSettings是最简单的配置实现方式。",[11,35,36],{},"不过在测试和使用的过程中遇到过很多次无法增量编译编辑器的情况，需要对项目进行重新生成。",[11,38,39],{},"目前还不是很确定问题是出在UDeveloperSettings这边还是由于4.18的预览bug引起的。",[11,41,42],{},"类似于这样的定义就可以让配置出现在项目配置中",[44,45,50],"pre",{"className":46,"code":48,"language":49},[47],"language-text","UCLASS(config = ElLog, defaultconfig, meta = (DisplayName = \"ElLog\"))\nclass UElLogSettings : public UDeveloperSettings\n","text",[51,52,48],"code",{"__ignoreMap":53},"",[11,55,56],{},"不过这样的方式有一个缺点，那就是他只能出现在配置的“引擎”分类中，并没有看到能够调整目录的地方。",[27,58,60],{"id":59},"isettingsmodule","ISettingsModule",[11,62,63],{},"其实UE4本身有提供配置的注册接口，只要通过这个接口就能将配置类注册到设定UI中去了：",[44,65,68],{"className":66,"code":67,"language":49},[47],"void RegisterSettings()\n{\n  UE_LOG(LogTemp,Log,TEXT(\"[NS_ELLOG] RegisterSettings()\"));\n  if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr\u003CISettingsModule>(\"Settings\"))\n  {\n    SettingsModule->RegisterSettings(\"Project\", \"Plugins\", \"ELLog\",\n      LOCTEXT(\"TileSetEditorSettingsName\", \"EasyLog Settings\"),\n      LOCTEXT(\"TileSetEditorSettingsDescription\", \"Configure the setting of easylog plugin.\"),\n      GetMutableDefault\u003CUElLogSettings>());\n    UE_LOG(LogTemp, Log, TEXT(\"[NS_ELLOG] RegisterSettings(): Stp\"));\n  }\n}\n\nvoid UnregisterSettings()\n{\n    if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr\u003CISettingsModule>(\"Settings\"))\n    {\n        SettingsModule->UnregisterSettings(\"Project\", \"Plugins\", \"ELLog\");\n    }\n}\n",[51,69,67],{"__ignoreMap":53},[11,71,72],{},"由于这里使用的时候是在插件中，直接在StartupModule()和ShutdownModule()中进行注册和解注册就可以了。",[11,74,75],{},"注意这里的配置是会覆盖的，如果使用了引擎内部相同的配置名称可能会发生奇怪的现象。",[11,77,78],{},"RegisterSettings函数中的各个参数的意义都比较明显，直接看注释就能明白了。",[80,81,82],"blockquote",{},[83,84,85,89,92,95,98,101,104],"ul",{},[86,87,88],"li",{},"@param ContainerName 配置的分类，目前只有Editor和Project两个，分别对应编辑器设置和项目设置两个界面",[86,90,91],{},"@param CategoryName 配置的目录，这个是在上面两种界面中的目录，比如引擎、游戏等。",[86,93,94],{},"@param SectionName 配置的名称，就是显示在左侧目录树上的名称",[86,96,97],{},"@param DisplayName 配置的显示名称",[86,99,100],{},"@param Description 配置的描述",[86,102,103],{},"@param SettingsObject 实际承载这个配置的配置类",[86,105,106],{},"@return 会返回注册后的配置展示类，失败的话会返回Nullptr",[11,108,109],{},"上面的代码注册后就能在项目设置中看到了",[11,111,112],{},[113,114],"img",{"alt":115,"src":116},"image","\u002Fwp-content\u002Fuploads\u002F2017\u002F10\u002Fimage_thumb.png",[11,118,119],{},[113,120],{"alt":115,"src":121},"\u002Fwp-content\u002Fuploads\u002F2017\u002F10\u002Fimage_thumb-1.png",[11,123,124],{},"需要注意的是，ISettingModule这个类型是编辑器使用的，在Shipping模式下无法使用，所以在相关的代码附近注意加上WITH_EDITOR的宏来进行差分。",[11,126,127],{},"通常情况下这样就可以了，不过由于插件的类型原因，这里还是有一个问题。",[27,129,131],{"id":130},"loading-phase","Loading Phase",[11,133,134],{},"UE4中的模块都有载入时机的概念，在uplugin文件中可以进行配置。",[11,136,137],{},"目前可用的载入时机有",[80,139,140,143,146,149,152,155,158],{},[11,141,142],{},"PostConfigInit：引擎初始化阶段，在配置系统初始化完成后",[11,144,145],{},"PreLoadingScreen：引擎初始化阶段，可以在这里挂入LoadingScreen的注册",[11,147,148],{},"PreDefault：引擎初始化阶段，在Default阶段之前",[11,150,151],{},"Default：引擎初始化阶段，此时所有的游戏模块加载已经完成",[11,153,154],{},"PostDefault：引擎初始化阶段，在Default阶段之后",[11,156,157],{},"PostEngineInit：引擎初始化完成后",[11,159,160],{},"None：不会自动加载",[11,162,163],{},"这几个加载阶段有的在文档中描述不是很明确，由于没有实际使用到，所以就没有深入看过。当遇到相应需求的时候，再进行判断比较合适。",[11,165,166],{},"这里由于ELLog本身是Log的插件，所以为了尽可能早的加载完成，就选择了PostConfigInit阶段。",[11,168,169],{},"但是在这个阶段，SettingModule其实并没有完成加载，所以无法成功完成注入到配置系统中的工作。",[11,171,172],{},"因此需要借助额外的引擎接口才行",[44,174,177],{"className":175,"code":176,"language":49},[47],"\u002F\u002FNS_ELLOG::RegisterSettings();\nFCoreDelegates::OnFEngineLoopInitComplete.AddStatic(&NS_ELLOG::RegisterSettings);\n",[51,178,176],{"__ignoreMap":53},[11,180,181],{},"在StartupModule()中不直接进行注册，而是注册到引擎初始化完成的Delegate上，这样就可以在SettingModule加载完成之后执行注册了。",[11,183,184,185,191],{},"以上的代码都可以在",[21,186,190],{"href":187,"rel":188},"https:\u002F\u002Fgithub.com\u002FArisego\u002FELLog",[189],"nofollow","ELLog插件","中找到，因此这里就不多贴代码了。",{"title":53,"searchDepth":193,"depth":194,"links":195},2,3,[196,197,198],{"id":29,"depth":193,"text":30},{"id":59,"depth":193,"text":60},{"id":130,"depth":193,"text":131},"2017-10-14","md",{"layout":202,"status":203,"published":204,"author":205,"author_login":207,"author_email":208,"wordpress_id":209,"wordpress_url":210,"date_gmt":211,"excerpt":212},"post","publish",true,{"display_name":206,"login":207,"email":208,"url":53},"风铃","flinkor","flinkor@foxmail.com",1972,"\u002F\u002F?p=1972","2017-10-14 11:05:31 +0000",{"type":8,"value":213},[214],[11,215,13],{},"\u002F2017-10-14-ue4-config-setting",{"title":6,"description":13},"_legacy\u002F2017\u002F2017-10-14-ue4-config-setting",[220,221,222],"UE4","Config","Setting","vf1l0LywvIQwcofjPXDbGiu2fQQJnhd7FsD0aYqDQKs",{"id":225,"title":226,"body":227,"date":528,"description":231,"extension":200,"meta":529,"navigation":204,"path":538,"seo":539,"stem":540,"tags":541,"__hash__":542},"blogs\u002F_legacy\u002F2017\u002F2017-06-06-ue4-config-usage.md","UE4中Config的使用",{"type":8,"value":228,"toc":521},[229,232,235,238,241,244,279,282,324,327,336,339,342,348,351,357,360,366,369,372,375,381,392,395,398,404,407,410,430,437,440,443,446,449,452,455,458,464,467,470,476,479,485,488,491,494,500,503,506,509,512],[11,230,231],{},"UE4中本身有一套非常成熟的INI文件配置机制，对于简单的属性配置，可以直接进行借用。",[11,233,234],{},"当前使用的UE4版本为4.15.2。",[27,236,237],{"id":237},"引擎配置",[11,239,240],{},"整个Config机制在引擎中最常见的地方就是引擎的配置本身了，项目配置与引擎配置，Localize的配置……这些都是通过这个机制进行的。",[11,242,243],{},"引擎自带的配置分类有以下这些：",[80,245,246,249,252,255,258,261,264,267,270,273,276],{},[11,247,248],{},"Compat（兼容性）",[11,250,251],{},"DeviceProfiles（设备概述文件）",[11,253,254],{},"Editor（编辑器）",[11,256,257],{},"EditorGameAgnostic（编辑器游戏不可知论）",[11,259,260],{},"EditorKeyBindings（编辑器按键绑定）",[11,262,263],{},"EditorUserSettings（编辑器用户设置）",[11,265,266],{},"Engine(引擎)",[11,268,269],{},"Game(游戏)",[11,271,272],{},"Input（输入）",[11,274,275],{},"Lightmass",[11,277,278],{},"Scalability（可扩展性）",[11,280,281],{},"配置文件是有加载优先的，按照官方文档提供的顺序，越靠后的值会覆盖前面的值。",[80,283,284,287,290,301,307,316],{},[11,285,286],{},"Engine\u002FConfig\u002FBase.ini",[11,288,289],{},"Engine\u002FConfig\u002FBaseEngine.ini",[11,291,292,293,297,298,300],{},"Engine\u002FConfig\u002F",[294,295,296],"span",{},"Platform","\u002F",[294,299,296],{},"Engine.ini",[11,302,303,306],{},[294,304,305],{},"ProjectDirectory","\u002FConfig\u002FDefaultEngine.ini",[11,308,309,311,312,297,314,300],{},[294,310,305],{},"\u002FConfig\u002F",[294,313,296],{},[294,315,296],{},[11,317,318,320,321,323],{},[294,319,305],{},"\u002FSaved\u002FConfig\u002F",[294,322,296],{},"\u002FEngine.ini",[11,325,326],{},"这个加载次序是以Engine配置为基础的。",[11,328,329,330,335],{},"关于这部分的详细可以参照[",[21,331,334],{"href":332,"rel":333},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FProgramming\u002FBasics\u002FConfigurationFiles\u002Findex.html",[189],"官方文档","]。",[27,337,338],{"id":338},"自主调用",[11,340,341],{},"使用GConfig就可以对这个配置系统中的值进行查询和写入，要对配置进行读取的话可以使用：",[44,343,346],{"className":344,"code":345,"language":49},[47],"if(!GConfig) return;\n\nFString ValueReceived;\n  GConfig->GetString(\n  TEXT(\"\u002FScript\u002FEngine.WorldInfo\"),\n  TEXT(\"GlobalDefaultGameType\"),\n  ValueReceived,\n  GGameIni\n);\n",[51,347,345],{"__ignoreMap":53},[11,349,350],{},"而写入的话，可以使用：",[44,352,355],{"className":353,"code":354,"language":49},[47],"if (!GConfig) return;\nGConfig->SetInt(\n  TEXT(\"sectionname\"),\n  TEXT(\"key\"),\n  100,\n  GGameUserSettingsIni\n  );\nGConfig->Flush(false, GGameUserSettingsIni);\n",[51,356,354],{"__ignoreMap":53},[11,358,359],{},"这里使用的Get\u002FSet是一系列函数，对应引擎内部的不同值，第四个参数是配置的名称，与上面给出的配置分类是对应的，应该，没有仔细的考证过。可以看到GGameUserSettingsIni附近有很多这样的定义：",[44,361,364],{"className":362,"code":363,"language":49},[47],"FString                GEngineIni;                                                    \u002F* Engine ini filename *\u002F\n\n\u002F** Editor ini file locations - stored per engine version (shared across all projects). Migrated between versions on first run. *\u002F\nFString                GEditorIni;                                                    \u002F* Editor ini filename *\u002F\nFString                GEditorKeyBindingsIni;                                        \u002F* Editor Key Bindings ini file *\u002F\nFString                GEditorLayoutIni;                                            \u002F* Editor UI Layout ini filename *\u002F\nFString                GEditorSettingsIni;                                            \u002F* Editor Settings ini filename *\u002F\n\n\u002F** Editor per-project ini files - stored per project. *\u002F\nFString                GEditorPerProjectIni;                                        \u002F* Editor User Settings ini filename *\u002F\n\nFString                GCompatIni;\nFString                GLightmassIni;                                                \u002F* Lightmass settings ini filename *\u002F\nFString                GScalabilityIni;                                            \u002F* Scalability settings ini filename *\u002F\nFString                GHardwareIni;                                                \u002F* Hardware ini filename *\u002F\nFString                GInputIni;                                                    \u002F* Input ini filename *\u002F\nFString                GGameIni;                                                    \u002F* Game ini filename *\u002F\nFString                GGameUserSettingsIni;                                        \u002F* User Game Settings ini filename *\u002F\n",[51,365,363],{"__ignoreMap":53},[11,367,368],{},"其实这种用法，使用起来相对有些麻烦，而且会把自己的配置和引擎的配置混在一起，其实还有更加简单的使用这个配置的方法：",[27,370,371],{"id":371},"扩展使用",[11,373,374],{},"这个特性是可以扩展给自己定义的类使用的，在UClass定义时制定Config即可，像是这样的感觉：",[44,376,379],{"className":377,"code":378,"language":49},[47],"UCLASS(config=GameUserSettings, configdonotcheckdefaults)\nclass ENGINE_API UGameUserSettings : public UObject\n",[51,380,378],{"__ignoreMap":53},[11,382,383,384,387,388,391],{},"其实上面那些自带的引擎配置就是在引擎中预先定义好的",[294,385,386],{},"名称","-",[294,389,390],{},"配置文件名","对应组。",[11,393,394],{},"这里面在定义时，config=custom，这样自己定义配置文件名称也是可以的。如果不想自己的配置和引擎的配置混在一起变得乱七八糟的话，这是一个很好的选择。",[11,396,397],{},"这样的好处是所有标记为UProperty的属性加上config标记都会被自动载入，在执行SaveConfig后就可以直接保存，不需要和配置的逻辑打交道。定义时就像这样的感觉：",[44,399,402],{"className":400,"code":401,"language":49},[47],"UPROPERTY(config, EditAnywhere, Category=\"MouseProperties”)\nuint32 bEnableFOVScaling:1;\n",[51,403,401],{"__ignoreMap":53},[11,405,406],{},"在文件名的分配上，如果使用custom的话，配置文件就会被保存到custom.ini的命名规则下。如果一时之间找不到配置文件，可以使用这个文件名进行搜索。",[11,408,409],{},"这一系列的配置还有其他几个选项：",[80,411,412,415,418,421,424,427],{},[11,413,414],{},"perObjectConfig",[11,416,417],{},"使得配置变为针对每个Object的实例，而不是每个Class存一个配置块。",[11,419,420],{},"configdonotcheckdefaults",[11,422,423],{},"配置了这个的话，config在读取的时候就不会去读取default配置。",[11,425,426],{},"defaultconfig",[11,428,429],{},"配置只保存在default中",[11,431,432,433,436],{},"这里有一个概念就是Default，Default配置的读取是比较靠前的，通常保存在项目目录Config\u002F",[294,434,435],{},"Custom","Default.ini中。这里有一个在测试时需要注意的，如果SaveConfig的值与Default值相同的话，那么就会不进行保存，该行配置会消失，因为与默认值一样了。",[11,438,439],{},"在进行配置文件操作和设定时，可以使用UpdateDefaultConfigFile()来写入到default值，而通常的配置可以使用SaveConfig()进行保存，因为SaveConfig是会自动进行Flush的，而另一个UpdateGlobal ConfigFile则局限性较强，但它可以确保写入路径是指向用户配置的。",[11,441,442],{},"要注意的是，如果在Shipping打包模式下，那么可以让用户和我们自己进行配置的ini文件将不会出现在项目目录中个，而是在\"用户目录\\AppData\\Local\\rg_unreal\\Saved\\Config\\WindowsNoEditor[custom].ini\"中。",[11,444,445],{},"如果觉得这样有些Low的话，我们就需要绕回去：",[27,447,448],{"id":448},"自定义路径",[11,450,451],{},"仔细查看Config系统的源码，我们可以看到，ini的加载是在UObject中进行指定的。如果没有定义PerObjectConfig的话，配置是在CDO阶段就完成了的。如果定义了PerObject的话则会在UObject:: PostLoad中进行配置加载。",[11,453,454],{},"但是系统本身的GetConfigName是无法被重载的，好在引擎有暴露其他的接口，我们要对其使用只要自己在BeginPlay之类的事件中自己进行读写就可以了。",[11,456,457],{},"其实要使用的函数在上面已经用到了，只是在调用的时候使用的是默认参数罢了。",[44,459,462],{"className":460,"code":461,"language":49},[47],"SaveConfig(CPF_Config, *ConfPath);\n",[51,463,461],{"__ignoreMap":53},[11,465,466],{},"这样调用的话就可以将当前类中的配置保存到指定的路径去了，不用指定后缀名Ini也是可以的。",[11,468,469],{},"不过，出于保险起见，最好是在Save之后，刷新一下缓存",[44,471,474],{"className":472,"code":473,"language":49},[47],"GConfig->Flush(false, ConfPath)\n",[51,475,473],{"__ignoreMap":53},[11,477,478],{},"要读取的话也是同理，先刷新缓存",[44,480,483],{"className":481,"code":482,"language":49},[47],"GConfig->Flush(true, ConfPath)\n",[51,484,482],{"__ignoreMap":53},[11,486,487],{},"其实如果配置的读写都是在引擎内完成的话，是没必要在读取的时候刷缓存的。",[11,489,490],{},"但如果有在引擎外部修改配置文件，并希望配置“实时”的反映到读取中的话，就需要在读取前刷新一下缓存。",[11,492,493],{},"读取的话使用",[44,495,498],{"className":496,"code":497,"language":49},[47],"ReloadConfig(NULL, *ConfPath)\n",[51,499,497],{"__ignoreMap":53},[11,501,502],{},"就可以了，这里使用到的ConfPath都是FString。",[27,504,505],{"id":505},"总结",[11,507,508],{},"基本上配置存取使用官方的配置类就可以了，这里有一点很强大的是，似乎所有可以UPROPERTY化的引擎定义变量都可以写入到Config中，已经测试过TArray和TMap可以正常的进行读写。",[11,510,511],{},"但是像是UObject这类的并没有进行测试……不过引擎的配置中有大量的使用UAsset的引用，应当是毫无问题的。",[11,513,514,515,520],{},"不过，配置文件虽然有读写功能，但是它的定位本身并不适合用来存档。如果有这方面的需求请使用[",[21,516,519],{"href":517,"rel":518},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FSaveGame\u002Findex.html",[189],"SaveGame类","]，蓝图与C++皆可使用。",{"title":53,"searchDepth":193,"depth":194,"links":522},[523,524,525,526,527],{"id":237,"depth":193,"text":237},{"id":338,"depth":193,"text":338},{"id":371,"depth":193,"text":371},{"id":448,"depth":193,"text":448},{"id":505,"depth":193,"text":505},"2017-06-06",{"layout":202,"status":203,"published":204,"author":530,"author_login":207,"author_email":208,"wordpress_id":531,"wordpress_url":532,"date_gmt":533,"excerpt":534},{"display_name":206,"login":207,"email":208,"url":53},1777,"\u002F\u002F?p=1777","2017-06-06 12:12:22 +0000",{"type":8,"value":535},[536],[11,537,231],{},"\u002F2017-06-06-ue4-config-usage",{"title":226,"description":231},"_legacy\u002F2017\u002F2017-06-06-ue4-config-usage",[220,221],"5PE0D-8QY1nKGtyz-G-TzQvrRF7I9D4LT4XxU3vOAks",1788763179901]