Download

Having trouble adding components from Construction Script

Hello fellow UE4 programmers,

I am using 4.9.1 and having trouble adding components to my Actor derived Blueprint in Construction Script as desired.

The components I am trying to add (and attach) are of a custom SceneComponent derived (C++) class. This class is supposed to contain an optional MeshComponent attached as a child.

Brief description and overview of the problem and the questions:

  1. Adding StaticMeshComponents with Add Component node works as expected.
  2. Adding SceneComponents with Add Component node and manually attaching StaticMeshComponents created with Add Component nodes to the SceneComponents works as expected.
  3. Custom SceneComponents created with Add Component node and manually attaching StaticMeshComponents created with Add Component nodes to them does not work. While the meshes are visible in the level editor viewport and Play Standalone Game, they aren’t visible in Play In Editor. This problem occured after I added PostDuplicate to the custom SceneComponent class.
  4. Adding custom SceneComponents with Add Component node and specifying instanced StaticMeshComponents automatically attached aren’t visible in Play In Editor and don’t have the intended hierarchy. Why and how do I fix this?

I’ve uploaded a small zip file to my Dropbox which contains the Blueprint asset, test level, source code, config and uproject: https://dl.dropboxusercontent.com/u/16914081/UE4/ComponentTrouble.zip
Would be nice if you could have a look. You can comment PostDuplicate in both header and source and see for yourself (note though that in the editor the attachment hierarchy will not be as intended anymore but once PIE or Play Standalone Game has had BeginPlay called).

Detail information:
Here is the code for the custom SceneComponent class
.h


#pragma once

#include "Components/SceneComponent.h"
#include "MySceneComponent.generated.h"


/**
 * basic SceneComponent that comes with a customizable MeshComponent attached to it
 */
UCLASS(Blueprintable, ClassGroup = (Custom), EditinlineNew, meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UMySceneComponent : public USceneComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UMySceneComponent();

	// Called when the game starts
	virtual void BeginPlay() override;

	virtual void PostDuplicate(bool bDuplicateForPIE) override;

	UPROPERTY(BlueprintReadWrite, Category = "Mesh", EditAnywhere, Instanced)
	UMeshComponent* MeshComponent;
	
};


.cpp


#include "MyProject.h"
#include "MySceneComponent.h"


UMySceneComponent::UMySceneComponent()
	: Super()
{
	bWantsBeginPlay = true;

	MeshComponent = nullptr;
}

void UMySceneComponent::BeginPlay()
{
	Super::BeginPlay();

	if (MeshComponent)
	{
		//ensure it is both registered and attached if it isn't already attach
		MeshComponent->RegisterComponent();
		if (!(MeshComponent->IsAttachedTo(this)))
		{
			MeshComponent->AttachTo(this);
		}
	}
}

void UMySceneComponent::PostDuplicate(bool bDuplicateForPIE)
{
	//does not make a difference whether this is called first or last
	Super::PostDuplicate(bDuplicateForPIE);

	if (MeshComponent)
	{
		//registration would fail without a valid owner (and world)
		if (MeshComponent->GetOwner())
		{
			MeshComponent->RegisterComponent();
		}
		MeshComponent->AttachTo(this);
	}
}


What I started with in Blueprint is a simple Add Component node for my custom SceneComponent, then an Add Component node for a StaticMeshComponent with the basic Floor StaticMesh assigned and finally the StaticMeshComponent is attached to the custom SceneComponent. This happens in a loop where the SceneComponents are manually attached to the DefaultSceneRoot later. Here’s a screenshot for the Blueprint graph:
4663260483c74ccbcc9e9488419c29dc9c599be7.png

This works as expected, except in Play In Editor the meshes aren’t visible. As I said above this started after adding the PostDuplicate override and I can’t figure out why. I’m calling Super::PostDuplicate so I shouldn’t be overriding anything of the regular behaviour unless I’m overlooking something? PostDuplicate will be needed for the next step below otherwise I would just remove it. Unless there’s a more appropriate function I’m unaware of that can be used instead of PostDuplicate to ensure the MeshComponent is attached to the custom SceneComponent (in editor after Construction Script)?

Now what I wanted to do is to skip the (unnecessary) step of manually adding and attaching the StaticMeshComponent in Blueprint everytime and have this done in the custom C++ SceneComponent. Since the Mesh property in my custom SceneComponent is Instanced (see code above) it is possible to select the first Add Component node for this and in the Details panel directly assign a new StaticMeshComponent instance with the Floor StaticMesh assigned to it. Again the custom SceneComponents are manually attached to the DefaultSceneRoot. Here’s a screenshot of the new Blueprint:
2514fd9030d224ff86768f9c7962a5831924b2e5.png

While this may look ok at first having an instance of this Blueprint placed in a level, the components list/hierarchy changes after selecting something else placed in the same level and then reselecting the Blueprint instance again. The same happens after starting and stopping Play In Editor. See this before-after image:
BEFORE
e34d399caeb99899f116748771dbdd6cc20f06a7.png

AFTER
a1e288cdaaf31cf4530899f6fd5a95a610c2104c.png

As before the meshes are visible in the level editor viewport as well as when playing as Standalone Game. However in Play In Editor the meshes are not visible again. Why would it do that?

When I enable Editor Preferences -> Content Editors / Blueprint Editor -> Workflow / Hid Construction Script Components In Details View one of the custom SceneComponents created from the loop disappears from the components list, all others stay there. The one SceneComponent that disappears from the list (it still exists in the world) is the one that no longer has a StaticMeshComponent child. This also spams the log with the following warning if the Blueprint instance is selected: “LogSCSEditor:Warning: Calling UpdateTree() from Tick() more frequently than expected due to invalid tree node state. This might be a performance issue”. You may also notice the StaticMeshComponents all have the same name.
60fa83d2388cee52a5ec6ba043175a7bc9a480d7.png

Thank you very much for your time and suggestions.

I don’t have 4.9 installed and had some other issues when trying to repro this - notably the instanced dropdown just resetting to None as soon as I selected anything. Not sure what that was about.

Anyway, one thing I can say is that I think your issue with the hierarchy jumbling is actually not an issue, but just a bug with the widget that displays this hierarchy not dealing properly with components with the same name. The mesh components having the same name is to be expected, since they get duplicated from the template object in the AddComponent node, and since each one has its own outer, there is no name collision. I tested this by forcing a rename to a globally unique name in OnComponentCreated, and the jumbling goes away.

That doesn’t explain stuff disappearing in PIE though. A couple of guesses at what could be an issue:

  • The component created via the instanced dropdown will be marked as EComponentCreationMethod::Native, not UserConstructionScript. I wonder if this might conflict somehow, since the instances are actually being created by duplication within the construction script. I kind of doubt this is the cause though to be honest.
  • Maybe calling RegisterComponent within PostDuplicate is too early and something doesn’t get setup properly, but then when it’s called later it gets skipped because it was marked as already registered? What happens (or doesn’t happen) without the PostDuplicate override? If the component has already been registered on the source object (be that the CDO, or the instance in your level that is being duplicated for PIE) I’d have thought that duplication would automatically sort out attachment status for you.

Edit: Just realized I got mixed up between the two components, obviously your instanced mesh needs to be registered/attached in the first place. You could perhaps try doing it within an override of PostEditChangeProperty on your scene component, checking to see if the changed property is the mesh and that it’s not null. That way it’s possible you wouldn’t need to override PostDuplicate.

Let me know if you make any progress. I’m interested in finding out what is possible with this kind of component instancing, and what is just going too far and doomed to break the UE4 universe.

Hey kamrann, thank you very much for your response.

I’ll break your answer down to smaller chunks and respond to each one individually.

Yeah, I noticed that too. What usually helped though was to create a new Add Component node on its own (i.e. not connected to anything yet). Then it would pretty much always work instead of resetting to None. After connecting the node and then trying to change the MeshComponent instance to something else would then sometimes reset to None again.

That’s somewhat of a relief. I had a hunch this could be an issue but wasn’t able to confirm this. I assumed it would assign a unique name (just the plain Object name without regards to its outer) to the duplicated object (as well as any children). I am now renaming the custom Scene Component as well as its Mesh Component (if it exists) like you describe in OnComponentCreated and that seems to have helped with the hierarchy at least.
This has not help with the invisibility though (didn’t expect that to be related in any way).

I agree that it should not be caused by the CreationMethod EComponentCreationMethod::Native since the meshes are also invisible with EComponentCreationMethod::UserConstructionScript in case of manually attaching them to the custom Scene Components. If I change the CreationMethod of the MeshComponent in OnComponentCreated to EComponentCreationMethod::UserConstructionScript as well, the meshes are still invisible.
I’ve tried PostEditChange but that didn’t really work either (can’t remember what exactly it was, maybe I just didn’t try hard enough).

But actually I’ve fixed it. Don’t ask me why this makes a difference, I have no idea :slight_smile:
I’ve moved the attachment and MeshComponent registration code (yes, the exact same lines) to OnComponentCreated and it automagically works, it’s a miracle… (although it feels somewhat like a hack)

What made me stumble into this was that I initially messed up the MeshComponent->Rename call and passed GetOuter instead of MeshComponent->GetOuter which caused some more issues (including completely messing up the attachment hierarchy) making one of the StaticMeshComponents the new root (at least from the Details panel point of view). The other StaticMeshComponents would then be visible in PIE as well.
Out of curiosity I’ve then moved the entire code from OnComponentCreated to PostDuplicate (so the same as before but now force renaming first). Guess what, it didn’t work still.

So the .cpp now looks like this (it might even work if I remove BeginPlay):


#include "MyProject.h"
#include "MySceneComponent.h"


UMySceneComponent::UMySceneComponent()
	: Super()
{
	bWantsBeginPlay = true;

	MeshComponent = nullptr;
}

void UMySceneComponent::BeginPlay()
{
	Super::BeginPlay();

	//if we have a mesh
	if (MeshComponent)
	{
		//ensure it is both registered and attached if it isn't already attached
		MeshComponent->RegisterComponent();
		if (!(MeshComponent->IsAttachedTo(this)))
		{
			MeshComponent->AttachTo(this);
		}
	}
}

void UMySceneComponent::OnComponentCreated()
{
	Super::OnComponentCreated();

	Rename(NULL, GetOuter(), REN_ForceGlobalUnique);
	if (MeshComponent)
	{
		MeshComponent->Rename(NULL, MeshComponent->GetOuter(), REN_ForceGlobalUnique);

		//registration would fail without a valid owner (and world)
		if (MeshComponent->GetOwner())
		{
			MeshComponent->RegisterComponent();
		}
		MeshComponent->AttachTo(this);
	}
}


Thank you very much kamrann, you’ve helped me out once again. Hopefully this helps you with your component instancing adventures :slight_smile:

I have to add that setting MeshComponent->CreationMethod = CreationMethod; in OnComponentCreated gets rid of the “LogSCSEditor:Warning: Calling UpdateTree() from Tick() more frequently than expected due to invalid tree node state. This might be a performance issue” warnings if Editor Preferences -> Content Editors / Blueprint Editor -> Workflow / Hide Construction Script Components In Details View is checked.

No worries, yeah I already learnt new things from reading your original post - didn’t know about the those workflow preferences for one thing.

I don’t think it’s a hack actually. After posting I thought about this some more and figured your scene component’s OnRegister would be a better place to register and attach its child mesh than PostDuplicate. That way you’re guaranteed that it will be done at a time that other components are being registered, and not too early. Just tested it and it also seems to work, without anything else in BeginPlay. OnComponentCreated I haven’t really used as yet, but is likely called at a similar stage to OnRegister. I have a feeling it’s not called on loading, but that likely won’t matter since your construction script presumably is.

About the dropdown issue. I originally thought it might be due to pushing the boundaries of what you can do within a construction script a bit too far, but I think it’s actually a bug within the AddComponent node implementation. From what I can see, when it generates a name for the instanced property object, it can introduce spaces in the name, which then cause it to fail to find the property by path when setting its value.

The problem is that I wasn’t aware of OnRegister/OnComponentCreated before and probably many more (similar for AActor and common UObject). And the second problem is that it’s not always clear to me when these are called and what they can/should or can/should NOT be used for. You kind of have to search all code for it and then see where it comes from and goes to.

I’ve noticed the spaces before but would not have thought this could cause such problems. Especially since I am now renaming both the StaticMeshComponents as well as the SceneComponents, or did I misunderstand something? It definitively also happens without spaces in the names (<ClassName>_<Number>).

Thanks again

I was referring there to the name that is auto-generated internally for the ‘template’ component (a UMySceneComponent in this case) that the engine creates when you use an AddComponent node. This is the object you configure in the details panel, and will be duplicated onto your actor when the construction script is run. This line here looks like a bug to me. By using the display name, it can introduce spaces into the component’s path, which I’m pretty sure isn’t intended. In the case that you have an instanced property (as you do), that instanced object will also end up with spaces in its path, and the code that updates a property’s value when you edit it in the details panel fails to find the object as a result. I stepped through, and it basically parses the path string incorrectly due to the space, causing the property to be reset to null.

I guess I’ll file a bug report sometime, though lately I’m rather over struggling to get bugs recognized by non-programmer staff on answer hub…

I’ve just now come across this code again (upgraded to 4.11.0 in the meantime) and noticed that I cannot load maps where I’m using MySceneComponent. Creating and saving them works ok, switching to another map and then back into the first works as well (as long as the editor wasn’t closed since the map was created). I am guessing this is because the map objects are still around in memory somewhere (or at least not fully unloaded). However whenever the editor is closed after saving the map and then loading the editor and the map again, the loading process would cause a failed assertion (check( HasCompleted() ):wink: in the “root” call of LoadPackageInternal (for the Map) to Linker->Summary.TextureAllocations.CancelRemainingAllocations( true );
I’ve debugged this process and noticed that Visual Studio shows the following values for the FTextureAllocations instance which explain the failed check:


TextureTypes = Invalid
PendingAllocationCount = -572662307
PendingAllocationSize = -572662307
NumTextureTypesConsidered = -572662307

Both the Linker/Summary also look similar (also have Invalid members etc.). After further investigation I’ve tracked it down to EndLoadAndCopyLocalizationGatherFlag(); which is called just before Linker->Summary.TextureAllocations.CancelRemainingAllocations( true );. The Linker->Summary.TextureAllocations values are Empty, 0, 0, 0 respectively before the call to EndLoadAndCopyLocalizationGatherFlag().
I haven’t fully confirmed this is the cause of the problem but it seems like EndLoad (indirectly) calls OnComponentCreated which renames itself and the MeshComponent which causes other objects depending on them (and their names) to no longer find those correctly. Why exactly this has an effect on the Linker for the Map I’m not sure about (didn’t feel like debugging thousands of objects being loaded).

So for now I have removed the renaming calls again. I guess one could check whether OnComponentCreated is currently being called as part of the level loading process and skip the renaming in that case (or find a more appropriate function).