Creating & Linking Static Libraries And Make Your Own Blueprint Node With VS 2017 & UE4
Overview
The goal of this tutorial is to illustrate in detail about how to create and link a static library into UE 4.17 using the Visual Studio 2017, and make your own blueprint node with that library. This tutorial is mainly based on the great tutorial of Bob Gneu.
I wrote this because I want to give some help to newbies like me, who have difficulty following Bob's tutorial, and those who want to link their static library to UE 4.17 using Visual Studio 2017. My tutorial focuses more on trouble shooting which was the most difficult part for me. In addition to it, I will explain how to make your own blueprint node using your static library. If you are familiar with UE4 and trouble shooting with UE4 c++ system, you can just follow Bob's wonderful tutorial.
For examples sake, similar to the Bob's tutorial, I will be creating a library called YJMagicLib and linking into a UE4 project called UE4Magic.
Scope & Notes
- You will be able to link in a library of your own design and make blueprint Node using your library by the end
- I expect you to have, at the very least, a more than cursory level of knowledge about C++, C# and MS Visual Studio 2017
- For the linking library part, I will follow the flow of Bob's tutorial, but in more detailed way.
Visual Studio 2017 - Library Configuration
It is important that you compile out your static libraries with the same version of MS Visual Studio as you are using to work with UE4. This is because each edition of Visual Studio comes with a different compiler, leading to incompatibilities in the binaries. Rest assured if you cross into this realm you will get a warning when you attempt to compile the library in.
Creating the static library
This step is based on the tutorial of MSDN.Following and understanding my tutorial doesn't require to read MSDN's tutorial. However, if you are interested, you can follow the link and read it.
Creating a static library project
To create a static library project
- On the menu bar, choose File, New, Project
- In the left pane of the New Project dialog box, expand Installed, Visual C++, and then select Windows Desktop.
- In the center pane, select Win32 Desktop Wizard.
- Specify a name for the project-for example, YJMagicLib-in the Name box. Specify a name for the solution-for example, YJMagicLib-in the Solution Name box. Choose the OK button. Then Windows Desktop Project panel will appear.
- On the panel Windows Desktop Project panel, under Application type, select Static library.
- On the panel Windows Desktop Project panel, under Additional options, clear the Precompiled header check box.
- Choose the Okay button to create the project.
Adding a class to the static library
To add a class to the static library
- To create a header file for a new class, open the shortcut menu for the YJMagicLib project in Solution Explorer, and then choose Add, New Item. In the Add New Item dialog box, in the left pane, under Visual C++, select Code. In the center pane, select Header File (.h). Specify a name for the header file—for example, YJMagic.h—and then choose the Add button. A blank header file is displayed.
- Add a class named YJMagic to do addition. The code should resemble this:
inside YJMagic.h
#pragma once
// YJMagic.h
namespace YJMagic
{
class YJMagic
{
public:
// Returns a + b
static double Add(double a, double b);
};
}
- To create a source file for the new class, open the shortcut menu for the YJMagicLib project in Solution Explorer, and then choose Add, New Item. In the Add New Item dialog box, in the left pane, under Visual C++, select Code. In the center pane, select C++ File (.cpp). Specify a name for the source file—for example,YJMagic.cpp—and then choose the Add button. A blank source file is displayed.
- Use this source file to implement the functionality for YJMagic. The code should resemble this:
inside YJMagic.cpp
// YJMagic.cpp
#include "YJMagic.h"
#include <stdexcept>
using namespace std;
namespace YJMagic
{
double YJMagic::Add(double a, double b)
{
return a + b;
}
}
Customizations for Targeting UE4 Modules
Before compiling our static library, we will need to make a few other modifications to ensure that our libraries are able to be linked in easily.
Targeting 64 bit platforms
With a standard Static Library project we will be targeting x86 (32 bit) machines, which wont work for the UE4 toolset.
- Open the Project Properties page.
- Choose Configuration Manager
- Open the Active Solution Platform drop down list and select <New...>
- Type in or select x64 if it is there
- Save it by clicking OK
You should see it in the drop down at the top of Visual Studio.
Multi Threaded DLL
The unreal build system expects you to be providing a static library that is then linked into a DLL, so we have to ensure that our project does this as well.
- Open the Project Properties Page
- Filter through to Configuration Properties > C/C++ > Code Generation > Runtime Library
- From the drop down list select Multi-threaded DLL (/MD)
- Save it by clicking OK
Building in Release Mode
I think this is very important step. UE4 is compatible with libraries built in Release Mode, unless you will get error linking it to UE4.
- Click the Solution Configuration drop down on the menu bar
- From the drop down list select Release
- Now you can compile the static library by selecting Build, Build Solution on the menu bar. This creates a static library that we can use.
Making UE4 C++ Project & Dealing with errors
Make UE 4.17 c++ project. Name the project as UE4Magic. We will link the static library we made above to this UE4 project.
Here, you might have several problems compiling the UE4 project. I also spent a lot of time dealing with these problems. Here are list of problems & solutions I went through.
For those who had both VS 2015 Community & VS 2017 Community installed
This was the exact case for me. I installed both old version of UE4, and newest version (4.17) of UE4. The old version only works with VS 2015 and the latest version works with VS 2017. So I had the old VS and new VS at the same time.
It seems that there exists compatibility problem between VS 2015 and VS 2017. I found solution here and it worked for me.
- Remove VS 2015 community completely with this program. I used Release5. Don't try to manually remove VS 2015 by yourself because there are many dependency files.
- Open Visual Studio 2017 installer and install Game development with C++ with option : C++ profiling tools, Windows 10 SDK(I installed every available version of it), Windows 8.1 SDK and UCRT SDK. Also, install the .NET Framework. This step will solve the error like 'Visual Studio 2015 must be installed in order to build this target.', 'Windows SDK v8.1 must be installed in order to build this target.' You might wonder the reason why I suggest you to install VS 2017 functions to solve this 'VS 2015 should be installed' error. I'm not sure but VS 2017 is internally called as VS 2015, so the error is using the VS 2017 and VS 2015 as same term.
Error complaining 'hexadecimal value 0x00 is an invalid character...'
I found the solution to this problem here.
- Go to 'C:\Users\user\AppData\Roaming\Unreal Engine\UnrealBuildTool' (AppData folder is usually hidden, so you should first unhide the AppData folder.) and remove the BuildConfiguration.xml file.
For those who had only installed VS 2017, not VS 2015
If you get errors like 'Visual Studio 2015 must be installed in order to build this target.', 'Windows SDK v8.1 must be installed in order to build this target.', open your VS 2017 installer and install Game development with C++ with option : C++ profiling tools, Windows 10 SDK(I installed every available version of it), Windows 8.1 SDK and UCRT SDK. Also, install the .NET Framework.
Third Party Directory
Your lib file(s) should be in Your Project Root Directory/x64/Release, unless you changed them before hand. Your h file(s) should be in Your Project Root Directory/Your Project Name.
If you followed my tutorial, the path would be following:
- Lib file(s) path : ".../YJMagicLib/x64/Release"
- H file(s) path : ".../YJMagicLib/YJMagicLib"
We will be moving them from here into our UE4 project directory, for ease of reference and later packaging.
Open up a new explorer window and navigate to your Project directory. Once there, create a new path for your library - /ThirdParty/YJMagicLib
Within this directory we will be adding two new folders - Libraries and Includes - to house their respective files. Includes are the header files that come with your library, used to define the API (Application Programming Interface) for you to reference in UE4. You should copy your *.h file(s) into the Includes directory, and your *.lib file(s) into the Libraries directory. Here, it is important to change the name of your YJMagicLib.lib to YJMagicLib.x64.lib. Unless, you will get 'can't link to YJMagicLib.x64.lib.' error.
Open your Project Properties and go to Configuration Properties -> VC++ Directories section. Add ..\..\ThirdParty\YJMagicLib\Includes to Include Directories tab.
Note: This is an arbitrary directory. It is only important to note its path so you can reference it in the build system later.
UE4 - Build System
Now we are going to step into the UnrealEngine and actually address the binding of our library to the engine.
The build system requires some C# code, but you shouldn't fret. Our example is going to be pretty simple and so you should be alright to copy and paste the majority of the following code.
Extending the Base ModuleRules class
For anyone doing this as more than a one off you can, and probably should, add in a couple of properties to the ModuleRules class. The two I will be noting here are convenience properties to ensure that we always know where our Module and ThirdParty directories are. These two properties will leverage the System.IO namespace, to utilize a couple of the Path static methods.
inside /Source/UE4Magic/UE4Magic.Build.cs
using System.IO;
using UnrealBuildTool;
public class UE4Magic : ModuleRules
{
private string ModulePath
{
get { return ModuleDirectory; }
}
private string ThirdPartyPath
{
get { return Path.GetFullPath( Path.Combine( ModulePath, "../../ThirdParty/" ) ); }
}
[Constructor]
}
Here, If you don't add using System.IO; you will get 'Path does not exist in the current context' error. Don't forget to add it!
Loading Our Libraries
Next we will dive into our modules constructor, where we will create a new method to be called to configure the build system to load our libraries.
inside /Source/UE4Magic/UE4Magic.Build.cs
public class UE4Magic : ModuleRules
{
[Convenience Properties]
public UE4Magic(TargetInfo Target)
{
[Standard Module Initialization]
LoadYJMagicLib(Target);
}
}
LoadYJMagicLib accepts a TargetInfo object, which holds a number of configuration elements for the build system, allowing you to customize the build at compilation time. It is passed in to allow us to load in the libs for windows targeting, and can be used later to target other platforms. It also allows you to see if 64 or 32 bit platforms are being targeted, which is gold for our circumstances. If you remember, above I mentioned that we will be targeting x64 machines, but you can use this opportunity to compile out 32bit (x86) libraries as well.
Next we will define a constant to be passed in with the name of the library to link.
inside /Source/UE4Magic/UE4Magic.Build.cs
public class UE4Magic : ModuleRules
{
[Convenience Properties]
[Constructor]
public bool LoadYJMagicLib(TargetInfo Target)
{
bool isLibrarySupported = false;
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
{
isLibrarySupported = true;
string PlatformString = (Target.Platform == UnrealTargetPlatform.Win64) ? "x64" : "x86";
string LibrariesPath = Path.Combine(ThirdPartyPath, "YJMagicLib", "Libraries");
/*
test your path with:
using System; // Console.WriteLine("");
Console.WriteLine("... LibrariesPath -> " + LibrariesPath);
*/
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "YJMagicLib." + PlatformString + ".lib"));
}
if (isLibrarySupported)
{
// Include path
PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "YJMagicLib", "Includes"));
}
Definitions.Add(string.Format("WITH_YJ_MAGIC_LIB_BINDING={0}", isLibrarySupported ? 1 : 0));
return isLibrarySupported;
}
}
This is an exceptionally straight forward setup, and not likely to be representative of a fully cross platform build. What we have done is to create the constant WITH_YJ_MAGIC_LIB_BINDING to be passed in at compile time, it is set to 1 (or true).
Full code of our Base ModuleRules class
The full code of our Base ModuleRules class is following:
using System.IO;
using UnrealBuildTool;
public class UE4Magic : ModuleRules
{
// convenience properties
private string ModulePath
{
get { return ModuleDirectory; }
}
private string ThirdPartyPath
{
get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
}
// constructor
public UE4Magic(TargetInfo Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
LoadYJMagicLib(Target);
}
public bool LoadYJMagicLib(TargetInfo Target)
{
bool isLibrarySupported = false;
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
{
isLibrarySupported = true;
string PlatformString = (Target.Platform == UnrealTargetPlatform.Win64) ? "x64" : "x86";
string LibrariesPath = Path.Combine(ThirdPartyPath, "YJMagicLib", "Libraries");
/*
test your path with:
using System; // Console.WriteLine("");
Console.WriteLine("... LibrariesPath -> " + LibrariesPath);
*/
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "YJMagicLib." + PlatformString + ".lib"));
}
if (isLibrarySupported)
{
// Include path
PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "YJMagicLib", "Includes"));
}
Definitions.Add(string.Format("WITH_YJ_MAGIC_LIB_BINDING={0}", isLibrarySupported ? 1 : 0));
return isLibrarySupported;
}
}
Visual Studio 2017 - Linking Our Library
All of the UE4 goodies are taken care of at this point. You can now include your header files and start executing code.
Making Blueprint Function Library Class
Now we are ready to use YJMagicLib and to make our own blueprint node. Let's make a blueprint node that does simple 'addition' operation.
In your contents browser of your UE4 editor, right-click the mouse and make new c++ class. Choose the parent class as Blueprint Function Library, and name it YJLibrary. VS 2017 will automatically open and YJLibrary.h & YJLibrary.cpp will show up. Modify those codes as follows:
inside /Source/UE4Magic/YJLibrary.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "YJLibrary.generated.h"
UCLASS()
class UE4MAGIC_API UYJLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Adds floats", Keywords = "Float Add"), Category = YJLibrary)
static float AddFloats(float A, float B);
};
inside /Source/UE4Magic/YJLibrary.cpp
#include "YJLibrary.h"
#include "YJMagic.h"
float UYJLibrary::AddFloats(float A, float B)
{
YJMagic::YJMagic *PointerToYJMagic;
PointerToYJMagic = new YJMagic::YJMagic();
return PointerToYJMagic->Add(A, B);
}
Note: I referenced this tutorial for this section.
Using your Blueprint Node in UE4 Editor
Open Level Blueprint Editor. Right-click your mouse, and search for YJLibrary. AddFloats node will show up. We will make simple blueprint that adds two floats when keyboard 'A' is pressed, and show the results on the gameplay screen. Connect the nodes as shown in the figure. In this example, I gave 2.0 and 3.0 as input to the AddFloats node.
Compile the Level Blueprint script, and hit the play button. You will see 5.0 on the screen whenever you press 'A' key.
More Information
You have finished your long journey. Congratulations. You will be able to add any third party library to UE4 as you wish from now on.
You can read more about me on my User Page. Have a nice day :).
- Mintchococookie