Danta1ion
Danta1ion
发布于 2024-06-20 / 22 阅读
0
0

Modify Unreal Source Code without Full Rebuild

Overview

In many cases, we need to modify Unreal Engine's source code. This could come for many reasons:

  • We need to perform a bug fix
  • Add custom features
  • merging or rebasing multiple repos
  • pull new commits from upstream

However, the compile time of Unreal Engine's source code is annoying and always keep us waiting. This could be split into many circumstances:

  1. Only modify Config.ini: don't need any re-compile (0 min);
  2. Modify .cpp or .usf: need to re-compile the single file, and rebuild the module/target, maybe relink (1 min);
  3. Modify .h in plugins or .ush: need to full-rebuild the plugin or shaders, considering the dependency graph of modules, the compile time may vary (1-5 min);
  4. Modify .h in core or mono modules: need to full-rebuild the whole engine (15-20 min).

Decrease the compile time of header file in core module

So what may a header file contain?

  • A variable which follows the object's lifetime;
  • Changing a function's signature;
  • Exposing a new function to object's user
  • (waiting to be investigated)

If we want to store a status variable inside an object, which the object itself only exists several copies in game, we may consider using Subsystem at higher lifetime dependency to store it.

For example, to store a value for UWorld instance (which only has one instance at a time), we can store them in UEngineSubsystem. Now we can only modify the .cpp file to quickly inspect the changing.

// World.h
class UWorld {
    FVector CustomGravity = {0, 0, -980};
};

// World.cpp
FVector UWorld::GetGravity() {
    return CustomGravity;
}

Can be changed into

// CustomGravityEngineSubsystem.h
class UCustomGravityEngineSubsystem : public UEngineSubsystem {
public:
    FVector CustomGravity = {0, 0, -980};
};

// World.cpp
#include "CustomGravityEngineSubsystem.h"
FVector UWorld::GetGravity() {
    check(GEngine);
    if (UCustomGravityEngineSubsystem* Subsystem = GEngine->GetEngineSubsystem<UCustomGravityEngineSubsystem>()) {
        return Subsystem->CustomGravity;
    }
    
    return FVector{};
}

Now, we avoid changing the World.h mono header file.

(Danger) Subsystem method for F_Classes

:::tip F-Classes (non-UObject) cannot use this method.
F-classes are not garbage collected, there lifecycle should be managed manually.
If you still want to use this method (for demo only, you must refactor them in release build!!!), you can create a TMap mapping to map each F_Class instance with the given value.

// BodyInstance.h
class FBodyInstance {
    FDelegateHandle DelegateHandle;
}

// BodyInstance.cpp
void FBodyInstance::DoSomething() {
    FWorldDelegates::OnGravityChanged.Remove(DelegateHandle);
}

Can be changed into:

// BodyInstanceSubsystem.h
class UBodyInstanceSubsystem : public UEngineSubsystem {
public:
    TMap<FBodyInstance*, FDelegateHandle> BI2DH_Mapping;
}

// BodyInstance.cpp
#include "BodyInstanceSubsystem.h"

static TMap<FBodyInstance*, FDelegateHandle>& GetMappingChecked() {
    check(GEngine);
    UBodyInstanceSubsystem* Subsystem = GEngine->GetSubsystem<UBodyInstanceSubsystem>();
    check(Subsystem);
    return Subsystem->BI2DH_Mapping;
}

void FBodyInstance::FBodyInstance() {
    GetMappingChecked().Add(this, FDelegateHandle{});
}

void FBodyInstance::~FBodyInstance() {
    GetMappingChecked().Remove(this);
}

void FBodyInstance::DoSomething() {
    if (FDelegateHandle* DelegateHandlePtr = GetMappingChecked().Find(this)) {
        FWorldDelegates::OnGravityChanged.Remove(*DelegateHandlePtr);
    }
}

评论