本文涉及UE4引擎打开项目时,如何选择本地多个引擎目录并判断是否对项目编译的原理介绍
原理
uproject
项目文件通过t的EngineAssociation属性与引擎Appname比较判断项目是否是使用当前引擎
流程
WinMain
Main
UnrealVersionSelector 入口SwitchVersion
版本选择入口FWindowsPlatformInstallation::SelectEngineInstallation
弹出select对话框bool FWindowsPlatformInstallation::SelectEngineInstallation(FString &Identifier) { FSelectBuildDialog Dialog(Identifier); if(!Dialog.DoModal(NULL)) { return false; } Identifier = Dialog.Identifier; return true; }
FDesktopPlatformWindows::EnumerateEngineInstallations
- 读取Launch的引擎路径
- 读取Innov配置的引擎路径
- 读取注册表中的注册的引擎路径
三类引擎的路径都存入OutInstallations
void FDesktopPlatformWindows::EnumerateEngineInstallations(TMap<FString, FString> &OutInstallations) { // Enumerate the binary installations EnumerateLauncherEngineInstallations(OutInstallations); EnumerateInnovEngineInstallations(OutInstallations); // Enumerate the per-user installations HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, InstallationsSubKey, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) { // Get a list of all the directories TArray<FString> UniqueDirectories; OutInstallations.GenerateValueArray(UniqueDirectories); // Enumerate all the installations TArray<FString> InvalidKeys; for (::DWORD Index = 0;; Index++) { TCHAR ValueName[256]; TCHAR ValueData[MAX_PATH]; ::DWORD ValueType = 0; ::DWORD ValueNameLength = sizeof(ValueName) / sizeof(ValueName[0]); ::DWORD ValueDataSize = sizeof(ValueData); LRESULT Result = RegEnumValue(hKey, Index, ValueName, &ValueNameLength, NULL, &ValueType, (BYTE*)&ValueData[0], &ValueDataSize); if(Result == ERROR_SUCCESS) { int32 ValueDataLength = ValueDataSize / sizeof(TCHAR); if(ValueDataLength > 0 && ValueData[ValueDataLength - 1] == 0) ValueDataLength--; FString NormalizedInstalledDirectory(ValueDataLength, ValueData); FPaths::NormalizeDirectoryName(NormalizedInstalledDirectory); FPaths::CollapseRelativeDirectories(NormalizedInstalledDirectory); if(IsValidRootDirectory(NormalizedInstalledDirectory) && !UniqueDirectories.Contains(NormalizedInstalledDirectory)) { OutInstallations.Add(ValueName, NormalizedInstalledDirectory); UniqueDirectories.Add(NormalizedInstalledDirectory); } else { InvalidKeys.Add(ValueName); } } else if(Result == ERROR_NO_MORE_ITEMS) { break; } } // Remove all the keys which weren't valid for(const FString InvalidKey: InvalidKeys) { RegDeleteValue(hKey, *InvalidKey); } RegCloseKey(hKey); } }
FDesktopPlatformBase::EnumerateInnovEngineInstallations
- 读取InnovInstall 路径
- 存入OutInstallations
void FDesktopPlatformBase::EnumerateInnovEngineInstallations(TMap<FString, FString> &OutInstallations) { //see:FDesktopPlatformBase::EnumerateLauncherEngineInstallations // Cache the Innov install list if necessary //1. 读取InnovInstall 路径 ReadInnovInstallationList(); for(TMap<FString, FString>::TConstIterator Iter(InnovInstallationList); Iter; ++Iter) { FString AppName = Iter.Key(); //2. 存入OutInstallations OutInstallations.Add(AppName, Iter.Value()); } }
FDesktopPlatformBase::ReadInnovInstallationList()
- 从 RootDirectory / TEXT(“Engine/Build/InnovInstalled.dat”) InnovInstallEngine路径
- 将读取到的InnovInstallEngine路径存入InnovInstallationList
//引擎根路径 const FString RootDirectory = FPaths::RootDir(); //1. 从 RootDirectory / TEXT("Engine/Build/InnovInstalled.dat") InnovInstallEngine路径 const FString InstalledListFile = RootDirectory / TEXT("Engine/Build/InnovInstalled.dat"); // Read the installation manifest FString InstalledText; if (FFileHelper::LoadFileToString(InstalledText, *InstalledListFile)) { // Deserialize the object TSharedPtr< FJsonObject > RootObject; const TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(InstalledText); if (FJsonSerializer::Deserialize(Reader, RootObject) && RootObject.IsValid()) { // Parse the list of installations TArray< TSharedPtr<FJsonValue> > InstallationList = RootObject->GetArrayField(TEXT("InstallationList")); for(int32 Idx = 0; Idx < InstallationList.Num(); Idx++) { const TSharedPtr<FJsonObject> InstallationItem = InstallationList[Idx]->AsObject(); FString AppName = InstallationItem->GetStringField(TEXT("AppName")); //使用当前引擎作为Innov引擎 FString InstallLocation = RootDirectory; if(AppName.Len() > 0 && InstallLocation.Len() > 0) { FPaths::NormalizeDirectoryName(InstallLocation); // 2. 将读取到的InnovInstallEngine路径存入InnovInstallationList InnovInstallationList.Add(AppName, InstallLocation); } } } }
DesktopPlatformWindows.cpp
引擎默认是使用Install目录的UnrealVersionSelector.exe, 将注册的UnrealVersionSelector的路径改为当前目录的路基
// Defer to UnrealVersionSelector.exe in a launcher installation if it's got the same version number or greater.
// FString InstallDir;
// if (FWindowsPlatformMisc::QueryRegKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\EpicGames\\Unreal Engine"), TEXT("INSTALLDIR"), InstallDir) && (InstallDir.Len() > 0))
// {
// FString NormalizedInstallDir = InstallDir;
// FPaths::NormalizeDirectoryName(NormalizedInstallDir);
// FString InstalledExecutableFileName = NormalizedInstallDir / FString("Launcher/Engine/Binaries/Win64/UnrealVersionSelector.exe");
// if(GetShellIntegrationVersion(InstalledExecutableFileName) == GetShellIntegrationVersion(ExecutableFileName))
// {
// ExecutableFileName = InstalledExecutableFileName;
// }
// }
//change UnrealVersionSelector.exe in a launcher installation to UnrealVersionSelector.exe in InnovUE4 installation
FString InstalledExecutableFileName = ExecutableFileName;
使用
在Programs下找到UnrealVersionSelector项目进行编译
查看Engine\Binaries\Win64是否存在UnrealVersionSelector.exe,不是UnrealVersionSelector-Win64-Shipping
- UnrealVersionSelector-Win64-Shipping 使用的是UE4 install 版本的程序
- 我们需要使用我们改过多那个版本
我们编译的UnrealVersionSelector.exe生成后,双击即可将其注册为默认UnrealVersionSelector,(也可以使用 RegisterUnrealVersionSelector.bat 注册当前引擎版本的UnrealVersionSelector,之后就可以直接打开uproject)
查看注册表计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Unreal.ProjectFile\DefaultIcon,是否已经是自己引擎中UnrealVersionSelector.exe
构建
构建Install版本时,需要执行Engine\Build\Graph\Innov\AddVersionSelectorToInstall.xml 将UnrealVersionSelector复制到binary中
InnovUE4版本名称在 Engine\Build\InnovInstalled.dat 中配置
总结
UnrealVersionSelector 用于打开项目前的引擎选择,还有其他可能可以继续改进或添加新的特性