Setting up Unreal Engine 4 CMake environment for CLion

This time we’re going to set up our system for later use. I must say that hardware requirements are a bit higher than stated on Unreal Engine website. From my personal point of view I’d recommend having Mac with 16Gb RAM, Core i7 CPU, discrete GPU and SSD drive.

First thing you should know is that you have to build Unreal Engine from source if you want to use any IDE other than Xcode on your Mac, in my case CLion, because CMake file generator is Linux-only and is yet not perfect. Epic team says they have CMake support in development. After registering on UE website you have to follow tutorial and retrieve sources for UE4.

See tutorial for building UE on Mac here.

Let’s try to create simple empty C++ project with name ‘Game’. Wait for project files to be generated. The next step is to review project folder. In project root you will see two project files: `Game.uproject` and `Game.xcodeproject`. But in order to open your game in CLion you need `CMakeLists.txt` file.

You can rebuild project files manually using this command:

cd /path/to/UE4/Engine/Build/BatchFiles/Mac
./GenerateProjectFiles.sh -cmakefile -game -project “/path/to/Game.uproject"

If you look into generated `CMakeLists.txt` you’ll notice that source folders refer to Linux code and build targets also have Linux architecture.

Now, let’s change UnrealBuildTool a bit. Open project at `${UE4_ROOT}/Engine/Source/Programs/UnrealBuildTool/UnrealBuildTool_Mono.csproj` and find `System/CMakefileGenerator.cs`. After exploring module source we see the root of the problem.

We’ll have to determine host architecture and generate `CMakeLists.txt` file according to it.

Add

bool isMac = BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac;
bool isLinux = BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux;
String HostArchitecture = null;
if (isLinux) HostArchitecture = "Linux";
if (isMac) HostArchitecture = "Mac";

before `if (!String.IsNullOrEmpty (GameProjectName))` in `WriteCMakeLists()` method.

Then replace all “Linux” string entries with “{}” and add HostArchitecture as argument to `String.Format` calls.

Next, move directory filter to separate functions:

private bool IsLinuxFiltered( String SourceFileRelativeToRoot ) {
  return
    !SourceFileRelativeToRoot.Contains ("Source/ThirdParty/") &&
    !SourceFileRelativeToRoot.Contains ("/Windows/") &&
    !SourceFileRelativeToRoot.Contains ("/Mac/") &&
    !SourceFileRelativeToRoot.Contains ("/IOS/") &&

    !SourceFileRelativeToRoot.Contains ("/iOS/") &&

    !SourceFileRelativeToRoot.Contains ("/VisualStudioSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/XCodeSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/WmfMedia/") &&

    !SourceFileRelativeToRoot.Contains ("/IOSDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/AppleMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/MacGraphicsSwitching/") &&

    !SourceFileRelativeToRoot.Contains ("/Apple/") &&

    !SourceFileRelativeToRoot.Contains ("/WinRT/");

}


private bool IsMacFiltered( String SourceFileRelativeToRoot ) {

  return

    !SourceFileRelativeToRoot.Contains ("Source/ThirdParty/") &&

    !SourceFileRelativeToRoot.Contains ("/Windows/") &&

    !SourceFileRelativeToRoot.Contains ("/Linux/") &&

    !SourceFileRelativeToRoot.Contains ("/VisualStudioSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/WmfMedia/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/WinRT/");
}



And then replace condition on line 109 with

if ((isLinux && IsLinuxFiltered(SourceFileRelativeToRoot)) || (isMac && IsMacFiltered(SourceFileRelativeToRoot)))

Second problem is that there are no include directories, Let’s fix it. Add

private string GetIncludeDirectory(string IncludeDir, string ProjectDir)
{
  string FullProjectPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath);
  string FullPath = "";
  if (IncludeDir.StartsWith("/") && !IncludeDir.StartsWith(FullProjectPath))
  {
    // Full path to a folder outside of project
    FullPath = IncludeDir;
  }
  else
  {
    FullPath = Path.GetFullPath(Path.Combine(ProjectDir, IncludeDir));
    FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath);
    FullPath = FullPath.TrimEnd('/');
  }
  return FullPath;
}

Finally, print include directories to CMake file.

var IncludeDirectoriesList = "include_directories( \n";
List<String> IncludeDirectories = new List<String>();
foreach (var CurProject in GeneratedProjectFiles) {
  foreach (var CurPath in CurProject.IntelliSenseIncludeSearchPaths) {
    string IncludeDirectory = GetIncludeDirectory(CurPath, Path.GetDirectoryName(CurProject.ProjectFilePath));
    if (IncludeDirectory != null && !IncludeDirectories.Contains (IncludeDirectory)) 
    {
      IncludeDirectories.Add(IncludeDirectory);
    }
  }
}
foreach (string IncludeDirectory in IncludeDirectories) 
{
  IncludeDirectoriesList += ("\t\"" + IncludeDirectory + "\"\n");
}
IncludeDirectoriesList += CMakeSectionEnd;
CMakefileContent.Append (IncludeDirectoriesList);

After all code changes rebuild UnrealBuildProject and run `GenerageProjectFiles.sh` again. You should now have valid CMake file for your Mac, but there if you open the project in CLion and wait for project to parse, you’ll see that most macros are red. Is’s necessary to add preprocessor definitions to CMakeLists.txt file. This is done in the same way as include directories are added.

List<String> PreprocessorDefinitions = new List<String>();
foreach (var CurProject in GeneratedProjectFiles) {
  foreach (var CurPath in CurProject.IntelliSenseIncludeSearchPaths) {
    ... // already added code
  }
  foreach (var CurDefinition in CurProject.IntelliSensePreprocessorDefinitions)
  {
    string Definition = CurDefinition;
    string AlternateDefinition = Definition.Contains("=0") ? Definition.Replace("=0", "=1") : Definition.Replace("=1", "=0");
    if (Definition.Equals("WITH_EDITORONLY_DATA=0") || Definition.Equals("WITH_DATABASE_SUPPORT=1"))
    {
      Definition = AlternateDefinition;
    }
    if (!PreprocessorDefinitions.Contains(Definition) && !PreprocessorDefinitions.Contains(AlternateDefinition) && !Definition.StartsWith("UE_ENGINE_DIRECTORY") && !Definition.StartsWith("ORIGINAL_FILE_NAME"))
    {
      PreprocessorDefinitions.Add(Definition);
    }
  }
}
foreach (string PreprocessorDefinition in PreprocessorDefinitions) 
{
  PreprocessorDefinitionsList += ("\t-D" + PreprocessorDefinition + "\n");
}
PreprocessorDefinitionsList += ("\t-DMONOLITHIC_BUILD=1\n");
PreprocessorDefinitionsList += CMakeSectionEnd;
CMakefileContent.Append (PreprocessorDefinitionsList);

That’s it. Rebuild UnrealBuildTool and generate CMakeLists.txt file again. Now you are ready to start developing your game in CLion.

Please note, that it may happen that includes are not recognised and remain red. This behaviour is known and there is an issue on JetBrains bug tracker. Temporary workaround for this is to create fake build target in CMakeLists.txt

add_executable(FakeTarget $(SOURCE_FILES))

Hello world

So it begins. My biggest dream since i had started programming was to become a game developer. Now, i’m making serious attempt and it’s in my plans to develop simple side-scroller game and to publish it in several stores. Moreover, some years ago my primary language was C++ but for some reasons I had to abandon using it and I bet it’s time to bring it back.

I’m going to use following tools for code development:
1) Unreal Engine 4. Several months ago Epic Inc. changed their licensing policy and they completely removed monthly fee, leaving only 5% royalty after $3000 revenue made in quarter. In addition, UE4 allows you to make pure-C++ games.

2) CLion. I do love JetBrains products. I’ve been waiting for their C++ IDE for more than a year and have started using CLion during private EAP. It’s not perfect but it’s growing very fast.

3) Xamarin Studio (previously MonoDevelop). Sometimes I will make some changes in UE4 tools code and because of they are written in C#, mono is the only solution on OS X.

Later I will try to cover some topics about creating projects and preparing the environment. Stay tuned.