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:
- Only modify
Config.ini
: don't need any re-compile (0 min); - Modify
.cpp
or.usf
: need to re-compile the single file, and rebuild the module/target, maybe relink (1 min); - 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); - 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 aTMap
mapping to map eachF_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);
}
}