While our game may be running without any issues in the editor or even in…
Construction Script based on C++
Each time you make a change to a Blueprint (for example you drag it to a different position on your map) it’s construction script runs. This script contains code which is responsible for certain initialization operations. Using the construction script can accelerate certain operations, for example you can create an actor which generates random foliage around a point in your map, thus helping you or your level designer.
In this post, I will create a construction script in C++ which will spawn a given static mesh based on given values around a placed actor. Here is the end result:
Preparing an Actor
Add a new C++ class based on the Actor class and inside the header file, type in the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/*The static mesh of our actor*/ UPROPERTY(VisibleAnywhere) UStaticMeshComponent* SM; /*The random location of our component will get generated based on a given X and Y from within our editor.*/ /*The max x of the location vector that the static mesh will be spawned*/ UPROPERTY(EditAnywhere, Category="Construction") float XThreshold; /*The max y of the location vector that the static mesh will be spawned*/ UPROPERTY(EditAnywhere, Category = "Construction") float YThreshold; /*The number of meshes/components that will get spawned*/ UPROPERTY(EditAnywhere, Category = "Construction") int32 NumToSpawn; /*A random seed. Editing this, the editor will generate new random locations*/ UPROPERTY(EditAnywhere, Category = "Construction") int32 RandomSeed; /*An array containing references of the spawned components This will be used in order to delete old components in case we decide to tinker with the NumToSpawn parameter*/ TArray<UStaticMeshComponent*> SMArray; |
Switch to your source file and inside your constructor type in the following code:
1 2 3 |
//Create a Static Mesh for our Actor SM = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh")); SetRootComponent(SM); |
Compile your code an create a Blueprint based on the class you’ve added above. Then, don’t forget to assign a static mesh to the SM paremeter. Now we are ready to create the actual construction script.
Creating a construction script
Go to the header file of your C++ class and add the following declaration:
1 |
virtual void OnConstruction(const FTransform& Transform) override; |
After that, switch to your source file and type in the following implementation of the above function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
void AConstructionActor::OnConstruction(const FTransform & Transform) { //Empty the array and delete all it's components for (auto It = SMArray.CreateIterator(); It; It++) { (*It)->DestroyComponent(); } SMArray.Empty(); //Register all the components RegisterAllComponents(); //The base name for all our components FName InitialName = FName("MyCompName"); for (int32 i = 0; i < NumToSpawn; i++) { //Create a new Component //The first parameter is the "parent" of the our new component UStaticMeshComponent* NewComp = NewObject<UStaticMeshComponent>(this, InitialName); //Add a reference to our array SMArray.Add(NewComp); //Change the name for the next possible item FString Str = "MyCompName" + FString::FromInt(i+1); //Convert the FString to FName InitialName = (*Str); //If the component is valid, set it's static mesh, relative location and attach it to our parent if (NewComp) { GLog->Log("Registering comp..."); //Register the new component NewComp->RegisterComponent(); //Set the static mesh of our component NewComp->StaticMesh = SM->StaticMesh; //Set a random location based on the values we enter through the editor FVector Location; //Set the random seed in case we need to change our random values FMath::SRandInit(RandomSeed); Location.X += FMath::RandRange(-FMath::Abs(XThreshold), FMath::Abs(XThreshold)); Location.Y += FMath::RandRange(-FMath::Abs(YThreshold), FMath::Abs(YThreshold)); NewComp->SetWorldLocation(Location); //Attach the component to the root component NewComp->AttachTo(GetRootComponent(), NAME_None, EAttachLocation::KeepRelativeOffset); } } } |
Please note that you may have to change the name of the class, otherwise you will get a compile error!
That’s it! Go to the UE4 Editor, place the blueprint of your actor and temper with the values we exposed. Of course, you can create an improved version of the above script so the static meshes won’t overlap with each other!
I use : virtual void PostInitializeComponents(); . I do not know if it’s a good option. Is the same or different?
The lifecycle of an actor is as follows: the constructor is first, then the PostInitializeComponents and after the BeginPlay the PostBeginPlay.
-Orfeas
Hi Orfeas, nice tutorial!
I’m experiencing a little issue when adding/removing components like you do in the tutorial: it looks like the Details panel for the Actor doesn’t get properly updated after such changes.
For example, every time I move the Actor or change one of its properties, the OnConstruction function gets called, and I see the Actor’s components named like “Unnamed”. Then, if I deselect and reselect the Actor, everything looks fine.
Does it happen to you as well?
Hello,
This tutorial was written using an old version of the engine (4.12 maybe?) which at the time was working fine. I was able to replicate your issue using the 4.14.3 version of the engine. I’m assuming it’s a bug in the current version of the engine.
-Orfeas
It looks like AttachTo is about to be deprecated. They suggest you use AttachToComponent.
as far as I can tell this:
FAttachmentTransformRules AttachmentRules(EAttachmentRule::KeepRelative, false);
NewComp->AttachToComponent(GetRootComponent(), AttachmentRules, NAME_None);
Should be the same as this:
NewComp->AttachTo(GetRootComponent(), NAME_None, EAttachLocation::KeepRelativeOffset);
But it is not.
Attach to still works as shown in the tutorial but AttachToComponent does not produce any new static meshes.