Implementing a native code Construction Script

2021/01 02 13:01

Within Blueprint, a Construction Script is an Event Graph that runs any time a property is changed on the object it is attached to – whether it’s being dragged in the editor viewport or changed via a direct entry in a Details panel. Construction Scripts allow the object in question to rebuild itself based on its new location, for instance, or to change the components it contains based on user-selected options. When coding in C++ with Unreal Engine, the equivalent concept is the OnConstruction function.

How to do it…

  1. Create a new Actor called OnConstructionActor based on StaticMeshActor:
  1. Update the header file to the following: 
#pragma once

#include "CoreMinimal.h"
#include "Engine/StaticMeshActor.h"
#include "OnConstructionActor.generated.h"

UCLASS()
class CHAPTER_09_API AOnConstructionActor : public AStaticMeshActor
{
GENERATED_BODY()

public:
AOnConstructionActor();

virtual void OnConstruction(const FTransform& Transform) override;

UPROPERTY(EditAnywhere)
bool ShowStaticMesh;

};
  1. Go to the implementation file (OnConstructionActor.cpp) and implement the class constructor:
#include "OnConstructionActor.h"
#include "ConstructorHelpers.h"

AOnConstructionActor::AOnConstructionActor()
{
// Set this actor to call Tick() every frame. You can turn
// this off
to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;

auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(
TEXT("StaticMesh'/Engine/BasicShapes/Cone.Cone'"));

UStaticMeshComponent * SM = GetStaticMeshComponent();

if (SM != nullptr)
{
if (MeshAsset.Object != nullptr)
{
SM->SetStaticMesh(MeshAsset.Object);
SM->SetGenerateOverlapEvents(true);
}
SM->SetMobility(EComponentMobility::Movable);
}

// Default value of property
ShowStaticMesh = true;
}
  1. Implement OnConstruction:
void AOnConstructionActor::OnConstruction(const FTransform& Transform) 
{ 
  GetStaticMeshComponent()->SetVisibility(ShowStaticMesh); 
} 
  1. Compile your code and launch the editor.
  2. Drag an instance of your class into the game world, and verify that toggling the Boolean value for ShowStaticMesh toggles the visibility of the mesh in the editor viewport:
  1. OnConstruction does not currently run for C++ actors that are placed in a level if they are moved.
  2. To test this, place a breakpoint in your OnConstruction function, and then move your actor around the level.

To place a breakpoint, place your cursor on the desired line and hit F9 in Visual Studio.

  1. You’ll notice that the function doesn’t get called, but if you toggle the ShowStaticMesh Boolean, it does, causing your breakpoint to trigger.

To see why, take a look at the beginning of the AActor::PostEditMove function: 

void AActor::PostEditMove(bool bFinished)
{
if ( ReregisterComponentsWhenModified() && !FLevelUtils::IsMovingLevel())
{
UBlueprint* Blueprint = Cast<UBlueprint>(GetClass()->ClassGeneratedBy);
if (bFinished || bRunConstructionScriptOnDrag || (Blueprint && Blueprint->bRunConstructionScriptOnDrag))
{
FNavigationLockContext NavLock(GetWorld(), ENavigationLockReason::AllowUnregister);
RerunConstructionScripts();
}
}

// ....

The top line here casts UClass for the current object to UBlueprint, and will only run the construction scripts and OnConstruction again if the class is a Blueprint.

How it works…

We create a new Actor based on StaticMeshActor for easy access to a visual representation via the Static Mesh.

UPROPERTY is added to give us a property to change, which causes PostEditChangeProperty events to be triggered.

OnConstruction is a virtual function that’s defined in Actor.

As a result, we override the function in our class.

Within our class constructor, we initialize our mesh as usual, and set the default state of our bool property to match the visibility of the component that it controls.

Inside OnConstruction, the actor rebuilds itself using any properties that are required to do so.

For this simple example, we set the visibility of the mesh to match the value of our ShowStaticMesh property.

This could also be extended to changing other values based on the value of the ShowStaticMesh variable.

You’ll note that we don’t explicitly filter on a particular property being changed, like the previous recipe does with PostEditChangeProperty.

The OnConstruction script runs in its entirety for every property that gets changed on the object.

It has no way of testing which property was just edited, so you need to be judicious about placing computationally intensive code within it.