In part 2, we spawned multiple Instanced components in alternating locations using a map of ISMC.
In this part, we are going to add a FRandomStream. Also, we will mitigate the issues we faced in last section.
NOTE: Also, there were some unresolved errors in last part. I had to revisit my archived projects to fix them. I have discussed them later.
This is the content list for this part:
Library.h
.We have to make a new C++ class(empty class) and add these functions. We only need a header file. You may delete the source(.cpp) file. The two functions in question are...
We used this function to get rid of components that were leftover every time we updated
Note: When we create something with NewObject and register it, they don't get removed automatically.
They are probably merely invisible because we forgot to "Attach" them to our Actor/Component.
The following function will make sure that every such components are destroyed properly.
NOTE: If we have ActorComponent with their own components, this function would have to be called for them individual basis.
// cleanup function
while (this->GetComponentByClass(UInstancedStaticMeshComponent::StaticClass()) != NULL)
{
UActorComponent *cls = this->GetComponentByClass(UInstancedStaticMeshComponent::StaticClass());
cls->UnregisterComponent();
cls->DestroyComponent();
cls->SetActive(false);
}
// Add this in Library.h
/** Searches all Components "type" for given Actor "any" and destroys them */
template <class any, typename type>
static void cleanComponentsByType(any* reference) {
while (reference->GetComponentByClass(type::StaticClass()) != NULL) {
class UActorComponent* cls = reference->GetComponentByClass(type::StaticClass());
cls->DestroyComponent();
cls->SetActive(false);
}
return;
}
📒Explanation:
Component
class/subclass to remove (e.g.- UStaticMeshComponent)This function would initialize the component. Consider removing lines you would not require based on notes below.
plug-in macro : uinit_ismc
// Original function
// Declaring ISMC in anywhere other than constructor (uinit_ismc)
My_ISMC = NewObject<UInstancedStaticMeshComponent>(this);
My_ISMC->RegisterComponent();
My_ISMC->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
My_ISMC->SetStaticMesh(My_Mesh);
// Full implementation in Library.h
// Initialize single ISM
template <class any>
static void ISM_Initialize(ISMC* ref, any* parent, USceneComponent* SceneRoot, SM* sm = NULL) {
ref->SetFlags(RF_Transactional);
ref->CreationMethod = EComponentCreationMethod::UserConstructionScript;
ref->SetMobility(EComponentMobility::Type::Static);
ref->SetStaticMesh(sm);
ref->IsRegistered() ? true : ref->RegisterComponent();
// Use this line in { 4.22, 4.23 }. 4.24 use the lower one.
// ref->AttachToComponent(SceneRoot,
// FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
ref->AttachToComponent(SceneRoot, FAttachmentTransformRules::KeepRelativeTransform);
parent->AddInstanceComponent(ref);
}
📒Explanation:
this->RootComponent
)Assuming our actor class is AActor and we want to remove SplineComponent and InstancedStaticMeshComponent, we can use it like this:
Now these functions may be called as(after including your headers, of course)
// To complete our task
WW_Library::cleanByType<AActor, USplineComponent>(this);
WW_Library::cleanByType<AActor, UInstancedStaticMeshComponent>(this);
// Examplw with different actor class
WW_Library::cleanByType<AWW_Brick_Out_01, UPointLightComponent>(this);
// Initializing ISMs (mapping MeshMap --> ISMC_Map)
ISM_Initialize(ISMC_Array[i], this, SceneRoot/RootComponent, MeshMap[i];
We will use the code we had from last part. We add the following code for a seeded random number stream.
NOTE: Full procedural generation has its own places. But we want more control.
More about RandomStream in UE4 (You can read later)
The way I implement it, we would expect the designer to put a random int and fire the generator manually. For our designer, we will have a switch which automatically generates this number. Also, a switch to lock the integer so we don't accidentally lose it via misclick.
For same integer, result will always be same. This is useful when generating houses and such.
// PseudoRandomISM.h
#include "Kismet/KismetMathLibrary.h"
UPROPERTY(VisibleAnywhere, Category = "Parameters")
int CurrentSeed = 451;
UPROPERTY(EditAnywhere, Category = "Parameters")
bool GenerateNewSeed = false; // A switch
// Advanced display will keep the prop in a dropdown. Damage prevention 101.
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Parameters")
bool LockSeed = false; // A switch
UPROPERTY(EditAnywhere, Category = "Parameters")
bool RequestUpdate = false; // A switch
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Parameters")
int NumberOfMesh;
private:
// Description not provided
UFUNCTION(BlueprintCallable, Category = "C++")
void IndependentFunction();
// Description not provided
UFUNCTION(BlueprintCallable, Category = "C++")
void ResetMapContainers();
FRandomStream rnd; // I kept it private. Exposing to BP is your choice.
// PseudoRandomISM.cpp
void ASRandTestActor::OnConstruction(const FTransform &Transform)
{
if (!LockSeed && GenerateNewSeed)
{
CurrentSeed = FMath::Rand();
GenerateNewSeed = false; // This is a switch
RegenerateStream(CurrentSeed);
}
// switch to generate the structure
if (RequestUpdate == true)
{
IndependentFunction();
RequestUpdate = false;
}
// switch to update map sizes
if (UpdateContainers == true)
{
ResetMapContainers();
UpdateContainers = false;
}
}
void ASRandTestActor::IndependentFunction()
{
// Not implemented
}
void ASRandTestActor::ResetMapContainers()
{
// Not implemented
}
There are 2 switches, one boolean for flow control, and one exposed parameter for number of meshes.
This time around, we will not save the mesh references temporarily like we did in 2nd part. Code is simpler to convey in this way.
However, the source code for the 4th example should have everything implemented for reference.
At this point, you should have an UI like the above 👆.
uprop_ismc_ro
)Also, we added 2 private functions corresponding to two switches. We discussed about utility of switches in last part.
IndependentFunction()
evaluates the integer seed, meshmap values and generates the structure.RequestUpdate
switchResetMapContainers()
evaluates NumberOfMesh and sets the map for input.UpdateContainers
Next up we implement the functions.
We demonstrate picking between 2 values via RandomStream. Expanding this to multiple meshes is a simple math problem.
(The extra example will evaluate the mesh based on weight we assign to it)
// PseudoRandomISM::IndependentFunction()
// Utility fuc to safely free dangling components
WW_Library::cleanByType<APseudoRandomISM, UInstancedStaticMeshComponent>(this);
// Cleans arrays
WW_Library::cleanMap<UInstancedStaticMeshComponent>(ISMC_Map);
// WW_Library::cleanMap<UStaticMesh>(MeshMap) wont work. They are not Registered// MeshMap.Empty(); // This is enough to cler references
// Utility func to safely initialize ISM maps.
WW_Library::ISM_RefreshByNumber(ISMC_Map, NumberOfMesh, this, SceneRoot);
// Maps our meshes to their respective containers
WW_Library::Map_MeshToISMC(ISMC_Map, MeshMap);
for (int i = 0; i < ISMC_Map.Num(); i++)
{
ISMC_Map[i]->AddInstance(FTransform(FVector(100.0f * i, 0, 0)));
}
📒Explanation:
uinit_ismc
to generate this code.Map_MeshToISMC
maps the meshes 1 to 1.
The function would account for size mismatch and ArrayIndexOutOfBounds errors.// PseudoRandomISM::ResetMapContainers()
// Adding dummy values to MeshMap
MeshMap.Empty();
for (int i = 0; i < NumberOfMesh; i++)
{
MeshMap.Add(i);
}
📒Explanation : We observed in previous part that we can't increment mesh references from the editor. This is the straight-forward implementation in C++.
The post is getting too long, so I am going to continue this in the next part. Add 4 meshes into the array to get the following.
Just for you to follow, the steps are --> Set NumberOfMesh to 4 and Update Containers. Now, Request update should spawn the meshes in a line.
We are going to use the RandomStream in next part instead.
Make sure you followed through how the rest of the system is set to work. You can innovate your own approach from now, I already covered most common pitfalls.
See you later... 🖐
I upload weekly and revamp bi-monthly. You can support my work at Patreon | Paypal | Marketplace | Gumroad