joezhehuang
5 posts
Don't wanna be here? Send us removal request.
Text
How to navigate UE4 engine source code
Preliminary knowledge:
Understand common UE4 macros: UClass(), UPROPERTY(), UFUNCTION()
Understand common specifiers: BlueprintType, EditAnywhere, VisibleAnywhere, BlueprintCallable
Unreal c++ naming convention: AActor, UActorComponent, TArray
Some c++ features: virtual fucntions, override, inheritance, include header files rules, pointer
I use Visual Studio 2019 and Visual Assit plugin for most of my code navigation and here are some handy shortcut keys:
Alt + O: Toggle between .h and .cpp files
Alt + G: Go to implementation
Alt + Shift + O: Search file in solution
Alt + Shift + S: Search simbol in solution
Side mouse buttons: Jump back and forth to the positon you have visted
Now Visual Assit also provides you with intellisense of UE4 c++ API but it's not free. If you don't want to use it you can still navigate through the code base using Visual Studio's native shortcut:
F12: Go to implementation
Ctrl+K Ctrl O: Toggle header and source file
Ctrl + Shift + F: search for symbol or files, can scope to a specific engine module
Ctrl + -: jump backward
Ctrl + Shift + -: jump forward
Ctrl + Q: Search for a file or a symbol
Ctrl-K Ctrl-K: Set a bookmark
Ctrl-K Ctrl-N or Ctrl-K Ctrl-P: Jump back and forth between bookmarks
Example 1: Find a use case of UE_LOG() in engine code
Hit ctrl + shift + f and type UE_LOG(). You can scope the searching directory to Engine/Runtime to speed up the process. Hit Find All and I got 3816 results. The code snippet I'm showing here is from Actor.cpp which resides in Source/Runtime/Engine/Private
bool AActor::Destroy( bool bNetForce, bool bShouldModifyLevel ) { // It's already pending kill or in DestroyActor(), no need to beat the corpse if (!IsPendingKillPending()) { UWorld* World = GetWorld(); if (World) { World->DestroyActor( this, bNetForce, bShouldModifyLevel ); } else { UE_LOG(LogSpawn, Warning, TEXT("Destroying %s, which doesn't have a valid world pointer"), *GetPathName()); } } return IsPendingKillPending(); }
Example 2: Find a material node in engine code
Still the same workflow but need to use more judgement this time since the results contain all the symbols that contain "WorldPostion" which means it could be a class name, functin name, object name or even comments. I know this material node is a function and should live in a material expression class so I finally found it in Source/Runtime/Engine/Private/Materials/MaterialExpression.cpp
// UMaterialExpressionWorldPosition UMaterialExpressionWorldPosition::UMaterialExpressionWorldPosition(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITORONLY_DATA // Structure to hold one-time initialization struct FConstructorStatics { FText NAME_Coordinates; FConstructorStatics() : NAME_Coordinates(LOCTEXT( "Coordinates", "Coordinates" )) { } }; static FConstructorStatics ConstructorStatics; MenuCategories.Add(ConstructorStatics.NAME_Coordinates); bShaderInputData = true; #endif WorldPositionShaderOffset = WPT_Default; }
Example 3: Find a use case of NewObject() in engine code
Usually the use case of a function can be found in .cpp files because that's where functions are called. After I found the function I can hit F12 to jump to the definition and see what does each parameter mean.
// Note we aren't copying the the RF_ArchetypeObject flag. Also note the result is non-transactional by default. NewActorComp = NewObject( this, TemplateData->ComponentTemplateClass, InName, EObjectFlags(TemplateData->ComponentTemplateFlags) & ~(RF_ArchetypeObject | RF_Transactional | RF_WasLoaded | RF_Public | RF_InheritableComponentTemplate) );
Example 4: Use blueprint to undetstand the code
Here I present you with LineTraceSingleForObjects(), a function with 12 parameters. But by reading the code along with the blueprint node you will have a much esier time understanding what does this functin do.
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "LineTraceForObjects", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast")) static bool LineTraceSingleForObjects( const UObject* WorldContextObject, const FVector Start, const FVector End, const TArray > & ObjectTypes, bool bTraceComplex, const TArray& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f );
0 notes
Video
youtube
A very raw prototype of a machine gun
Such a primary looking machine gun actually requires many knowledge on blueprint scripting to implement. For example, controller for the machine gun (a pawn), line trace for hit event, timeline for gun animation, extra logic to handle continuously firing and set the source and target location of smoke trace fx in runtime. All very cool stuff :)
0 notes
Video
youtube
Race condition in UE4
When multiple actors are placed in the world, their beginplay() execution order are unpredictable. Gotta find a way to avoid race condition especially when one actor depends on another. Can use a simple delay node or put the initialization code to construction script. The goal here is to ensure that when an actor tries to read the data from another actor, that data has already been initialized.
Another interesting observation is that it seems like the latterly compiled actor will also execute latterly, as if there is a queue that stores all the actors in the world, and once an actor gets updated, it gets moved to the end of the queue. But these are all my assumptions :)
0 notes
Video
youtube
Cube Particle effects
Each Cube is an actor created by a spawner blueprint at a random location on a sphere. A cube array was defined to store all the references of cubes. Cube will shrink every frame and get removed from the cube array once size is smaller than a threshold.
0 notes
Video
youtube
Simple boids simulation in UE4
Each bird will move towards the attracter which is reparented by a white sphere. The movement behavior of a bird is implemented using the homing ability in projectile component.
0 notes