本文涉及UE4引擎打开项目时,如何选择本地多个引擎目录并判断是否对项目编译的原理介绍

原理

uproject

项目文件通过t的EngineAssociation属性与引擎Appname比较判断项目是否是使用当前引擎

流程

  1. WinMain

  2. Main
    UnrealVersionSelector 入口

  3. SwitchVersion
    版本选择入口

  4. FWindowsPlatformInstallation::SelectEngineInstallation
    弹出select对话框

    bool FWindowsPlatformInstallation::SelectEngineInstallation(FString &Identifier)
    {
       FSelectBuildDialog Dialog(Identifier);
       if(!Dialog.DoModal(NULL))
       {
          return false;
       }
    
       Identifier = Dialog.Identifier;
       return true;
    }
  5. FDesktopPlatformWindows::EnumerateEngineInstallations

    1. 读取Launch的引擎路径
    2. 读取Innov配置的引擎路径
    3. 读取注册表中的注册的引擎路径

    三类引擎的路径都存入OutInstallations

    void FDesktopPlatformWindows::EnumerateEngineInstallations(TMap<FString, FString> &amp;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, &amp;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, &amp;ValueNameLength, NULL, &amp;ValueType, (BYTE*)&amp;ValueData[0], &amp;ValueDataSize);
             if(Result == ERROR_SUCCESS)
             {
                int32 ValueDataLength = ValueDataSize / sizeof(TCHAR);
                if(ValueDataLength > 0 &amp;&amp; ValueData[ValueDataLength - 1] == 0) ValueDataLength--;
    
                FString NormalizedInstalledDirectory(ValueDataLength, ValueData);
                FPaths::NormalizeDirectoryName(NormalizedInstalledDirectory);
                FPaths::CollapseRelativeDirectories(NormalizedInstalledDirectory);
    
                if(IsValidRootDirectory(NormalizedInstalledDirectory) &amp;&amp; !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);
       }
    }
  6. FDesktopPlatformBase::EnumerateInnovEngineInstallations

    1. 读取InnovInstall 路径
    2. 存入OutInstallations
      void FDesktopPlatformBase::EnumerateInnovEngineInstallations(TMap<FString, FString> &amp;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());
         }
      }
  7. FDesktopPlatformBase::ReadInnovInstallationList()

    1. 从 RootDirectory / TEXT(“Engine/Build/InnovInstalled.dat”) InnovInstallEngine路径
    2. 将读取到的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) &amp;&amp; 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 &amp;&amp; InstallLocation.Len() > 0)
               {
                  FPaths::NormalizeDirectoryName(InstallLocation);
                  // 2. 将读取到的InnovInstallEngine路径存入InnovInstallationList
                  InnovInstallationList.Add(AppName, InstallLocation);
               }
            }
         }
      }
  8. 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;

使用

  1. 在Programs下找到UnrealVersionSelector项目进行编译

  2. 查看Engine\Binaries\Win64是否存在UnrealVersionSelector.exe,不是UnrealVersionSelector-Win64-Shipping

    1. UnrealVersionSelector-Win64-Shipping 使用的是UE4 install 版本的程序
    2. 我们需要使用我们改过多那个版本
  3. 我们编译的UnrealVersionSelector.exe生成后,双击即可将其注册为默认UnrealVersionSelector,(也可以使用 RegisterUnrealVersionSelector.bat 注册当前引擎版本的UnrealVersionSelector,之后就可以直接打开uproject)

  4. 查看注册表计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Unreal.ProjectFile\DefaultIcon,是否已经是自己引擎中UnrealVersionSelector.exe

    构建

  5. 构建Install版本时,需要执行Engine\Build\Graph\Innov\AddVersionSelectorToInstall.xml 将UnrealVersionSelector复制到binary中

  6. InnovUE4版本名称在 Engine\Build\InnovInstalled.dat 中配置

总结

UnrealVersionSelector 用于打开项目前的引擎选择,还有其他可能可以继续改进或添加新的特性