Skip to main content

📚 Lesson 8.2: Integrating Third-Party Libraries

Sometimes the code you need already exists — a physics solver, an analytics SDK, a JSON parser, a networking library. Linking external C++ into Unreal is a superpower Blueprint simply doesn't have, and it's all done through the .Build.cs you now understand.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Distinguish header-only, static, and dynamic (DLL) libraries
  • Add include paths and link libraries via .Build.cs
  • Stage a runtime DLL so it ships with the game
  • Wrap third-party headers to avoid Unreal warnings-as-errors
  • Decide where third-party code lives in the project

Estimated Time: 60 minutes

Engine Version: Unreal Engine 5.8

In This Lesson

Three Kinds of Library

How you integrate a library depends on how it's distributed. There are three forms, in increasing integration effort:

graph TD H["Header-only
(just .h files)"] --> He["Add include path. Done."] S["Static library
(.lib / .a)"] --> Se["Include path + link the lib"] D["Dynamic library
(.dll / .so / .dylib)"] --> De["Include path + link import lib + SHIP the runtime binary"]
Figure 1: The three library forms. Header-only is trivial; static links into your binary; dynamic must also be staged alongside the packaged game so it loads at runtime.

📖 Where third-party code lives

Convention is a ThirdParty/ folder — either under your project's Source/ or in a dedicated module/plugin. Inside, keep the library's include/ headers and its lib/ binaries organized by platform. Keeping it self-contained makes the .Build.cs paths clean and the dependency obvious to teammates.

Header-Only Libraries

The easiest case — a library that's entirely in headers (many modern C++ libs are). You just tell the build where the headers are.

public class MyModule : ModuleRules
{
    public MyModule(ReadOnlyTargetRules Target) : base(Target)
    {
        PublicDependencyModuleNames.AddRange(new string[]
        { "Core", "CoreUObject", "Engine" });

        // Point the compiler at the header-only library's include folder.
        PublicIncludePaths.Add(
            Path.Combine(ModuleDirectory, "ThirdParty", "somelib", "include"));
    }
}
// Then just include and use it in your .cpp:
#include "somelib/somelib.hpp"

void UMyClass::DoThing()
{
    somelib::Widget w;   // the third-party API, now available
}

✅ ModuleDirectory keeps paths portable

Use Path.Combine(ModuleDirectory, ...) rather than an absolute path — ModuleDirectory resolves to wherever the module is on any machine, so the project builds for every teammate and on CI without edits. Absolute paths are a classic "works on my machine" trap.

Static & Dynamic Libraries

A precompiled library adds a linking step: you point at the headers and the library binary. For a static lib, that's all — it gets baked into your module's binary.

public MyModule(ReadOnlyTargetRules Target) : base(Target)
{
    // ... dependencies ...

    string ThirdParty = Path.Combine(ModuleDirectory, "ThirdParty", "physicslib");

    // 1) Where the headers are:
    PublicIncludePaths.Add(Path.Combine(ThirdParty, "include"));

    // 2) The library to link (platform-specific):
    if (Target.Platform == UnrealTargetPlatform.Win64)
    {
        PublicAdditionalLibraries.Add(
            Path.Combine(ThirdParty, "lib", "Win64", "physicslib.lib"));
    }
}

⚠️ Match the platform and the runtime

Precompiled libraries are platform- and often toolchain-specific. A Win64 .lib won't link on Mac; a lib built with a different MSVC runtime can cause obscure link errors. Branch on Target.Platform and use libraries built for the same compiler Unreal uses. This is the fiddliest part of third-party integration — expect to consult the library's build docs.

Shipping a DLL

A dynamic library (DLL) needs three things: the headers, an import library to link against, and — critically — the DLL itself staged next to the packaged game so it loads at runtime. Miss the last step and it works in the editor but crashes in a packaged build.

if (Target.Platform == UnrealTargetPlatform.Win64)
{
    string Bin = Path.Combine(ThirdParty, "bin", "Win64");

    // Link against the import library at build time:
    PublicAdditionalLibraries.Add(Path.Combine(ThirdParty, "lib", "Win64", "netlib.lib"));

    // Tell Unreal about the DLL that must load at runtime:
    string Dll = Path.Combine(Bin, "netlib.dll");
    PublicDelayLoadDLLs.Add("netlib.dll");

    // Stage the DLL into the packaged build's binaries:
    RuntimeDependencies.Add(
        "$(BinaryOutputDir)/netlib.dll", Dll);
}

📖 Why RuntimeDependencies matters

RuntimeDependencies.Add tells the packaging system to copy the DLL into the shipped game. Without it, the editor finds the DLL (it's in your source tree) but the packaged game doesn't — the "works in editor, crashes when packaged" bug. PublicDelayLoadDLLs plus loading the DLL explicitly (via FPlatformProcess::GetDllHandle) gives you control over exactly when it loads. This is the single most-missed step in DLL integration.

Taming Warnings

Unreal compiles your code with strict warnings (often warnings-as-errors). Third-party headers rarely meet Unreal's bar, so including them raw can fail the build. Wrap them in Unreal's third-party include guards.

// Wrap noisy third-party includes so their warnings don't break your build:
#include "Windows/AllowWindowsPlatformTypes.h"   // (Windows-specific headers)
THIRD_PARTY_INCLUDES_START
#include "somelib/somelib.hpp"
THIRD_PARTY_INCLUDES_END
#include "Windows/HideWindowsPlatformTypes.h"

✅ The general pattern

THIRD_PARTY_INCLUDES_START/_END temporarily relax Unreal's warning settings around external headers. The Windows platform-types guards additionally prevent clashes between Windows API macros (like TEXT, GetObject) and Unreal's. Wrap third-party includes this way and a lot of mysterious build failures simply disappear. Keep third-party usage in .cpp files where possible so these includes don't leak into your headers.

Hands-on Exercise & Quiz

🏋️ Exercise: Plan an integration

Objective: Map out integrating a hypothetical analytics SDK shipped as a Win64 DLL + headers.

  1. Where do the headers and binaries go in the project?
  2. List the .Build.cs additions needed (include path, link, runtime).
  3. What extra step makes it work in a packaged build, not just the editor?
  4. How would you include the SDK header without triggering warnings-as-errors?
✅ Answers
  • 1. A ThirdParty/analytics/ folder with include/ and bin/lib per-platform.
  • 2. PublicIncludePaths.Add(include), PublicAdditionalLibraries.Add(.lib), PublicDelayLoadDLLs.Add("analytics.dll").
  • 3. RuntimeDependencies.Add(...) to stage the DLL into the packaged binaries.
  • 4. Wrap the include in THIRD_PARTY_INCLUDES_START/END (plus Windows platform-type guards).

🎯 Quick Quiz

Question 1: A header-only library needs only:

Question 2: A DLL integration works in editor but crashes when packaged. Most likely you forgot:

Question 3: Third-party headers fail Unreal's warnings-as-errors. The fix:

Summary

🎉 Key Takeaways

  • Integration effort scales with form: header-only (include path only) → static (+ link) → dynamic/DLL (+ stage at runtime).
  • Add headers with PublicIncludePaths and libs with PublicAdditionalLibraries, using Path.Combine(ModuleDirectory, ...) for portability.
  • For DLLs, RuntimeDependencies.Add stages the binary into the packaged game — the most-missed step ("works in editor, crashes packaged").
  • Branch on Target.Platform; match the library's platform and toolchain.
  • Wrap third-party includes in THIRD_PARTY_INCLUDES_START/END (+ platform-type guards) to survive warnings-as-errors; keep usage in .cpp.

📚 Additional Resources

🚀 What's Next?

You can organize your modules and pull in external code. The module finale packages your work for reuse: writing a C++ plugin — a self-contained bundle you can drop into any project or share.

🎉 Lesson complete!

External code links cleanly. Now let's package your own for reuse.